Auto merge of #147197 - matthiaskrgr:rollup-du5e4pv, r=matthiaskrgr

Rollup of 6 pull requests

Successful merges:

 - rust-lang/rust#142506 (Add `Path::has_trailing_sep` and related methods)
 - rust-lang/rust#146886 (Add repr(align(2)) to RcInner and ArcInner)
 - rust-lang/rust#147166 (several small `proc_macro` cleanups)
 - rust-lang/rust#147172 (bootstrap: build bootstrap docs with in-tree rustdoc)
 - rust-lang/rust#147181 (cg_llvm: Replace enum `MetadataType` with a list of `MetadataKindId` constants)
 - rust-lang/rust#147187 (remove unnecessary test directives)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors
2025-09-30 19:41:53 +00:00
19 changed files with 409 additions and 142 deletions
+3 -3
View File
@@ -34,7 +34,7 @@
use crate::back::write::to_llvm_code_model;
use crate::callee::get_fn;
use crate::debuginfo::metadata::apply_vcall_visibility_metadata;
use crate::llvm::Metadata;
use crate::llvm::{Metadata, MetadataKindId};
use crate::type_::Type;
use crate::value::Value;
use crate::{attributes, common, coverageinfo, debuginfo, llvm, llvm_util};
@@ -1006,11 +1006,11 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
pub(crate) fn set_metadata<'a>(
&self,
val: &'a Value,
kind_id: impl Into<llvm::MetadataKindId>,
kind_id: MetadataKindId,
md: &'ll Metadata,
) {
let node = self.get_metadata_value(md);
llvm::LLVMSetMetadata(val, kind_id.into(), node);
llvm::LLVMSetMetadata(val, kind_id, node);
}
}
@@ -1611,16 +1611,12 @@ enum VCallVisibility {
let v = [llvm::LLVMValueAsMetadata(cx.const_usize(0)), typeid];
llvm::LLVMRustGlobalAddMetadata(
vtable,
llvm::MD_type as c_uint,
llvm::MD_type,
llvm::LLVMMDNodeInContext2(cx.llcx, v.as_ptr(), v.len()),
);
let vcall_visibility = llvm::LLVMValueAsMetadata(cx.const_u64(vcall_visibility as u64));
let vcall_visibility_metadata = llvm::LLVMMDNodeInContext2(cx.llcx, &vcall_visibility, 1);
llvm::LLVMGlobalSetMetadata(
vtable,
llvm::MetadataType::MD_vcall_visibility as c_uint,
vcall_visibility_metadata,
);
llvm::LLVMGlobalSetMetadata(vtable, llvm::MD_vcall_visibility, vcall_visibility_metadata);
}
}
+7 -37
View File
@@ -29,6 +29,7 @@
DITemplateTypeParameter, DIType, DebugEmissionKind, DebugNameTableKind,
};
use crate::llvm;
use crate::llvm::MetadataKindId;
/// In the LLVM-C API, boolean values are passed as `typedef int LLVMBool`,
/// which has a different ABI from Rust or C++ `bool`.
@@ -513,31 +514,6 @@ pub(crate) enum FileType {
ObjectFile,
}
/// LLVMMetadataType
#[derive(Copy, Clone)]
#[repr(C)]
#[expect(dead_code, reason = "Some variants are unused, but are kept to match LLVM-C")]
pub(crate) enum MetadataType {
MD_dbg = 0,
MD_tbaa = 1,
MD_prof = 2,
MD_fpmath = 3,
MD_range = 4,
MD_tbaa_struct = 5,
MD_invariant_load = 6,
MD_alias_scope = 7,
MD_noalias = 8,
MD_nontemporal = 9,
MD_mem_parallel_loop_access = 10,
MD_nonnull = 11,
MD_unpredictable = 15,
MD_align = 17,
MD_type = 19,
MD_vcall_visibility = 28,
MD_noundef = 29,
MD_kcfi_type = 36,
}
/// Must match the layout of `LLVMInlineAsmDialect`.
#[derive(Copy, Clone, PartialEq)]
#[repr(C)]
@@ -1035,16 +1011,6 @@ pub struct GEPNoWrapFlags : c_uint {
unsafe extern "C" fn(*mut c_void, *const c_char) -> *mut c_void;
pub(crate) type GetSymbolsErrorCallback = unsafe extern "C" fn(*const c_char) -> *mut c_void;
#[derive(Copy, Clone)]
#[repr(transparent)]
pub(crate) struct MetadataKindId(c_uint);
impl From<MetadataType> for MetadataKindId {
fn from(value: MetadataType) -> Self {
Self(value as c_uint)
}
}
unsafe extern "C" {
// Create and destroy contexts.
pub(crate) fn LLVMContextDispose(C: &'static mut Context);
@@ -1139,7 +1105,11 @@ pub(crate) fn LLVMStructTypeInContext<'a>(
pub(crate) fn LLVMSetValueName2(Val: &Value, Name: *const c_char, NameLen: size_t);
pub(crate) fn LLVMReplaceAllUsesWith<'a>(OldVal: &'a Value, NewVal: &'a Value);
pub(crate) safe fn LLVMSetMetadata<'a>(Val: &'a Value, KindID: MetadataKindId, Node: &'a Value);
pub(crate) fn LLVMGlobalSetMetadata<'a>(Val: &'a Value, KindID: c_uint, Metadata: &'a Metadata);
pub(crate) fn LLVMGlobalSetMetadata<'a>(
Val: &'a Value,
KindID: MetadataKindId,
Metadata: &'a Metadata,
);
pub(crate) safe fn LLVMValueAsMetadata(Node: &Value) -> &Metadata;
// Operations on constants of any type
@@ -2059,7 +2029,7 @@ pub(crate) fn LLVMDIBuilderCreateParameterVariable<'ll>(
// Operations on all values
pub(crate) fn LLVMRustGlobalAddMetadata<'a>(
Val: &'a Value,
KindID: c_uint,
KindID: MetadataKindId,
Metadata: &'a Metadata,
);
pub(crate) fn LLVMRustIsNonGVFunctionPointerTy(Val: &Value) -> bool;
@@ -0,0 +1,71 @@
use libc::c_uint;
pub(crate) use self::fixed_kinds::*;
#[derive(Copy, Clone)]
#[repr(transparent)]
pub(crate) struct MetadataKindId(c_uint);
macro_rules! declare_fixed_metadata_kinds {
(
$(
FIXED_MD_KIND($variant:ident, $value:literal)
)*
) => {
// Use a submodule to group all declarations into one `#[expect(..)]`.
#[expect(dead_code)]
mod fixed_kinds {
use super::MetadataKindId;
$(
#[expect(non_upper_case_globals)]
pub(crate) const $variant: MetadataKindId = MetadataKindId($value);
)*
}
};
}
// Must be kept in sync with the corresponding static assertions in `RustWrapper.cpp`.
declare_fixed_metadata_kinds! {
FIXED_MD_KIND(MD_dbg, 0)
FIXED_MD_KIND(MD_tbaa, 1)
FIXED_MD_KIND(MD_prof, 2)
FIXED_MD_KIND(MD_fpmath, 3)
FIXED_MD_KIND(MD_range, 4)
FIXED_MD_KIND(MD_tbaa_struct, 5)
FIXED_MD_KIND(MD_invariant_load, 6)
FIXED_MD_KIND(MD_alias_scope, 7)
FIXED_MD_KIND(MD_noalias, 8)
FIXED_MD_KIND(MD_nontemporal, 9)
FIXED_MD_KIND(MD_mem_parallel_loop_access, 10)
FIXED_MD_KIND(MD_nonnull, 11)
FIXED_MD_KIND(MD_dereferenceable, 12)
FIXED_MD_KIND(MD_dereferenceable_or_null, 13)
FIXED_MD_KIND(MD_make_implicit, 14)
FIXED_MD_KIND(MD_unpredictable, 15)
FIXED_MD_KIND(MD_invariant_group, 16)
FIXED_MD_KIND(MD_align, 17)
FIXED_MD_KIND(MD_loop, 18)
FIXED_MD_KIND(MD_type, 19)
FIXED_MD_KIND(MD_section_prefix, 20)
FIXED_MD_KIND(MD_absolute_symbol, 21)
FIXED_MD_KIND(MD_associated, 22)
FIXED_MD_KIND(MD_callees, 23)
FIXED_MD_KIND(MD_irr_loop, 24)
FIXED_MD_KIND(MD_access_group, 25)
FIXED_MD_KIND(MD_callback, 26)
FIXED_MD_KIND(MD_preserve_access_index, 27)
FIXED_MD_KIND(MD_vcall_visibility, 28)
FIXED_MD_KIND(MD_noundef, 29)
FIXED_MD_KIND(MD_annotation, 30)
FIXED_MD_KIND(MD_nosanitize, 31)
FIXED_MD_KIND(MD_func_sanitize, 32)
FIXED_MD_KIND(MD_exclude, 33)
FIXED_MD_KIND(MD_memprof, 34)
FIXED_MD_KIND(MD_callsite, 35)
FIXED_MD_KIND(MD_kcfi_type, 36)
FIXED_MD_KIND(MD_pcsections, 37)
FIXED_MD_KIND(MD_DIAssignID, 38)
FIXED_MD_KIND(MD_coro_outside_frame, 39)
FIXED_MD_KIND(MD_mmra, 40)
FIXED_MD_KIND(MD_noalias_addrspace, 41)
}
+2 -1
View File
@@ -11,13 +11,14 @@
pub(crate) use self::CallConv::*;
pub(crate) use self::CodeGenOptSize::*;
pub(crate) use self::MetadataType::*;
pub(crate) use self::ffi::*;
pub(crate) use self::metadata_kind::*;
use crate::common::AsCCharPtr;
pub(crate) mod diagnostic;
pub(crate) mod enzyme_ffi;
mod ffi;
mod metadata_kind;
pub(crate) use self::enzyme_ffi::*;
+4 -4
View File
@@ -306,7 +306,7 @@ fn add_type_metadata(&self, function: &'ll Value, typeid: &[u8]) {
let v = [llvm::LLVMValueAsMetadata(self.const_usize(0)), typeid_metadata];
llvm::LLVMRustGlobalAddMetadata(
function,
llvm::MD_type as c_uint,
llvm::MD_type,
llvm::LLVMMDNodeInContext2(self.llcx, v.as_ptr(), v.len()),
)
}
@@ -318,7 +318,7 @@ fn set_type_metadata(&self, function: &'ll Value, typeid: &[u8]) {
let v = [llvm::LLVMValueAsMetadata(self.const_usize(0)), typeid_metadata];
llvm::LLVMGlobalSetMetadata(
function,
llvm::MD_type as c_uint,
llvm::MD_type,
llvm::LLVMMDNodeInContext2(self.llcx, v.as_ptr(), v.len()),
)
}
@@ -333,7 +333,7 @@ fn add_kcfi_type_metadata(&self, function: &'ll Value, kcfi_typeid: u32) {
unsafe {
llvm::LLVMRustGlobalAddMetadata(
function,
llvm::MD_kcfi_type as c_uint,
llvm::MD_kcfi_type,
llvm::LLVMMDNodeInContext2(
self.llcx,
&llvm::LLVMValueAsMetadata(kcfi_type_metadata),
@@ -348,7 +348,7 @@ fn set_kcfi_type_metadata(&self, function: &'ll Value, kcfi_typeid: u32) {
unsafe {
llvm::LLVMGlobalSetMetadata(
function,
llvm::MD_kcfi_type as c_uint,
llvm::MD_kcfi_type,
llvm::LLVMMDNodeInContext2(
self.llcx,
&llvm::LLVMValueAsMetadata(kcfi_type_metadata),
@@ -1824,3 +1824,55 @@ extern "C" size_t LLVMRustEnzymeGetMaxTypeDepth() {
return 6; // Default fallback depth
}
#endif
// Statically assert that the fixed metadata kind IDs declared in
// `metadata_kind.rs` match the ones actually used by LLVM.
#define FIXED_MD_KIND(VARIANT, VALUE) \
static_assert(::llvm::LLVMContext::VARIANT == VALUE);
// Must be kept in sync with the corresponding list in `metadata_kind.rs`.
FIXED_MD_KIND(MD_dbg, 0)
FIXED_MD_KIND(MD_tbaa, 1)
FIXED_MD_KIND(MD_prof, 2)
FIXED_MD_KIND(MD_fpmath, 3)
FIXED_MD_KIND(MD_range, 4)
FIXED_MD_KIND(MD_tbaa_struct, 5)
FIXED_MD_KIND(MD_invariant_load, 6)
FIXED_MD_KIND(MD_alias_scope, 7)
FIXED_MD_KIND(MD_noalias, 8)
FIXED_MD_KIND(MD_nontemporal, 9)
FIXED_MD_KIND(MD_mem_parallel_loop_access, 10)
FIXED_MD_KIND(MD_nonnull, 11)
FIXED_MD_KIND(MD_dereferenceable, 12)
FIXED_MD_KIND(MD_dereferenceable_or_null, 13)
FIXED_MD_KIND(MD_make_implicit, 14)
FIXED_MD_KIND(MD_unpredictable, 15)
FIXED_MD_KIND(MD_invariant_group, 16)
FIXED_MD_KIND(MD_align, 17)
FIXED_MD_KIND(MD_loop, 18)
FIXED_MD_KIND(MD_type, 19)
FIXED_MD_KIND(MD_section_prefix, 20)
FIXED_MD_KIND(MD_absolute_symbol, 21)
FIXED_MD_KIND(MD_associated, 22)
FIXED_MD_KIND(MD_callees, 23)
FIXED_MD_KIND(MD_irr_loop, 24)
FIXED_MD_KIND(MD_access_group, 25)
FIXED_MD_KIND(MD_callback, 26)
FIXED_MD_KIND(MD_preserve_access_index, 27)
FIXED_MD_KIND(MD_vcall_visibility, 28)
FIXED_MD_KIND(MD_noundef, 29)
FIXED_MD_KIND(MD_annotation, 30)
FIXED_MD_KIND(MD_nosanitize, 31)
FIXED_MD_KIND(MD_func_sanitize, 32)
FIXED_MD_KIND(MD_exclude, 33)
FIXED_MD_KIND(MD_memprof, 34)
FIXED_MD_KIND(MD_callsite, 35)
FIXED_MD_KIND(MD_kcfi_type, 36)
FIXED_MD_KIND(MD_pcsections, 37)
FIXED_MD_KIND(MD_DIAssignID, 38)
FIXED_MD_KIND(MD_coro_outside_frame, 39)
FIXED_MD_KIND(MD_mmra, 40)
FIXED_MD_KIND(MD_noalias_addrspace, 41)
// If some fixed metadata kinds are not present and consistent in all supported
// LLVM versions, it's fine to omit them from this list; in that case Rust-side
// code cannot declare them as fixed IDs and must look them up by name instead.
#undef FIXED_MD_KIND
+3 -1
View File
@@ -277,7 +277,9 @@
// This is repr(C) to future-proof against possible field-reordering, which
// would interfere with otherwise safe [into|from]_raw() of transmutable
// inner types.
#[repr(C)]
// repr(align(2)) (forcing alignment to at least 2) is required because usize
// has 1-byte alignment on AVR.
#[repr(C, align(2))]
struct RcInner<T: ?Sized> {
strong: Cell<usize>,
weak: Cell<usize>,
+9 -7
View File
@@ -341,7 +341,7 @@ pub struct Weak<
// but it is not necessarily a valid pointer.
// `Weak::new` sets this to `usize::MAX` so that it doesnt need
// to allocate space on the heap. That's not a value a real pointer
// will ever have because RcInner has alignment at least 2.
// will ever have because ArcInner has alignment at least 2.
ptr: NonNull<ArcInner<T>>,
alloc: A,
}
@@ -366,7 +366,9 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// This is repr(C) to future-proof against possible field-reordering, which
// would interfere with otherwise safe [into|from]_raw() of transmutable
// inner types.
#[repr(C)]
// Unlike RcInner, repr(align(2)) is not strictly required because atomic types
// have the alignment same as its size, but we use it for consistency and clarity.
#[repr(C, align(2))]
struct ArcInner<T: ?Sized> {
strong: Atomic<usize>,
@@ -1613,9 +1615,9 @@ pub fn into_raw_with_allocator(this: Self) -> (*const T, A) {
pub fn as_ptr(this: &Self) -> *const T {
let ptr: *mut ArcInner<T> = NonNull::as_ptr(this.ptr);
// SAFETY: This cannot go through Deref::deref or RcInnerPtr::inner because
// SAFETY: This cannot go through Deref::deref or ArcInnerPtr::inner because
// this is required to retain raw/mut provenance such that e.g. `get_mut` can
// write through the pointer after the Rc is recovered through `from_raw`.
// write through the pointer after the Arc is recovered through `from_raw`.
unsafe { &raw mut (*ptr).data }
}
@@ -2450,7 +2452,7 @@ pub fn get_mut(this: &mut Self) -> Option<&mut T> {
/// If any other `Arc` or [`Weak`] pointers to the same allocation exist, then
/// they must not be dereferenced or have active borrows for the duration
/// of the returned borrow, and their inner type must be exactly the same as the
/// inner type of this Rc (including lifetimes). This is trivially the case if no
/// inner type of this Arc (including lifetimes). This is trivially the case if no
/// such pointers exist, for example immediately after `Arc::new`.
///
/// # Examples
@@ -3022,7 +3024,7 @@ pub unsafe fn from_raw_in(ptr: *const T, alloc: A) -> Self {
// Otherwise, we're guaranteed the pointer came from a nondangling Weak.
// SAFETY: data_offset is safe to call, as ptr references a real (potentially dropped) T.
let offset = unsafe { data_offset(ptr) };
// Thus, we reverse the offset to get the whole RcInner.
// Thus, we reverse the offset to get the whole ArcInner.
// SAFETY: the pointer originated from a Weak, so this offset is safe.
unsafe { ptr.byte_sub(offset) as *mut ArcInner<T> }
};
@@ -4015,7 +4017,7 @@ impl<T: ?Sized, A: Allocator> Unpin for Arc<T, A> {}
/// valid instance of T, but the T is allowed to be dropped.
unsafe fn data_offset<T: ?Sized>(ptr: *const T) -> usize {
// Align the unsized value to the end of the ArcInner.
// Because RcInner is repr(C), it will always be the last field in memory.
// Because ArcInner is repr(C), it will always be the last field in memory.
// SAFETY: since the only unsized types possible are slices, trait objects,
// and extern types, the input safety requirement is currently enough to
// satisfy the requirements of align_of_val_raw; this is an implementation
+7 -12
View File
@@ -26,18 +26,16 @@ pub(super) struct HandleCounters {
$(
pub(crate) struct $oty {
handle: handle::Handle,
// Prevent Send and Sync impls. `!Send`/`!Sync` is the usual
// way of doing this, but that requires unstable features.
// rust-analyzer uses this code and avoids unstable features.
_marker: PhantomData<*mut ()>,
}
impl !Send for $oty {}
impl !Sync for $oty {}
// Forward `Drop::drop` to the inherent `drop` method.
impl Drop for $oty {
fn drop(&mut self) {
$oty {
handle: self.handle,
_marker: PhantomData,
}.drop();
}
}
@@ -64,7 +62,6 @@ impl<S> DecodeMut<'_, '_, S> for $oty {
fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
$oty {
handle: handle::Handle::decode(r, s),
_marker: PhantomData,
}
}
}
@@ -74,12 +71,11 @@ fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub(crate) struct $ity {
handle: handle::Handle,
// Prevent Send and Sync impls. `!Send`/`!Sync` is the usual
// way of doing this, but that requires unstable features.
// rust-analyzer uses this code and avoids unstable features.
_marker: PhantomData<*mut ()>,
}
impl !Send for $ity {}
impl !Sync for $ity {}
impl<S> Encode<S> for $ity {
fn encode(self, w: &mut Writer, s: &mut S) {
self.handle.encode(w, s);
@@ -90,7 +86,6 @@ impl<S> DecodeMut<'_, '_, S> for $ity {
fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
$ity {
handle: handle::Handle::decode(r, s),
_marker: PhantomData,
}
}
}
@@ -144,7 +139,7 @@ macro_rules! define_client_side {
buf.clear();
api_tags::Method::$name(api_tags::$name::$method).encode(&mut buf, &mut ());
reverse_encode!(buf; $($arg),*);
$($arg.encode(&mut buf, &mut ());)*
buf = bridge.dispatch.call(buf);
+1 -3
View File
@@ -6,9 +6,7 @@
pub(super) struct Closure<'a, A, R> {
call: unsafe extern "C" fn(*mut Env, A) -> R,
env: *mut Env,
// Prevent Send and Sync impls. `!Send`/`!Sync` is the usual way of doing
// this, but that requires unstable features. rust-analyzer uses this code
// and avoids unstable features.
// Prevent Send and Sync impls.
//
// The `'a` lifetime parameter represents the lifetime of `Env`.
_marker: PhantomData<*mut &'a mut ()>,
+3 -25
View File
@@ -119,26 +119,6 @@ macro_rules! with_api_handle_types {
};
}
// FIXME(eddyb) this calls `encode` for each argument, but in reverse,
// to match the ordering in `reverse_decode`.
macro_rules! reverse_encode {
($writer:ident;) => {};
($writer:ident; $first:ident $(, $rest:ident)*) => {
reverse_encode!($writer; $($rest),*);
$first.encode(&mut $writer, &mut ());
}
}
// FIXME(eddyb) this calls `decode` for each argument, but in reverse,
// to avoid borrow conflicts from borrows started by `&mut` arguments.
macro_rules! reverse_decode {
($reader:ident, $s:ident;) => {};
($reader:ident, $s:ident; $first:ident: $first_ty:ty $(, $rest:ident: $rest_ty:ty)*) => {
reverse_decode!($reader, $s; $($rest: $rest_ty),*);
let $first = <$first_ty>::decode(&mut $reader, $s);
}
}
#[allow(unsafe_code)]
mod arena;
#[allow(unsafe_code)]
@@ -180,13 +160,11 @@ pub struct BridgeConfig<'a> {
/// If 'true', always invoke the default panic hook
force_show_panics: bool,
// Prevent Send and Sync impls. `!Send`/`!Sync` is the usual way of doing
// this, but that requires unstable features. rust-analyzer uses this code
// and avoids unstable features.
_marker: marker::PhantomData<*mut ()>,
}
impl !Send for BridgeConfig<'_> {}
impl !Sync for BridgeConfig<'_> {}
#[forbid(unsafe_code)]
#[allow(non_camel_case_types)]
mod api_tags {
+3 -13
View File
@@ -178,7 +178,7 @@ fn dispatch(&mut self, mut buf: Buffer) -> Buffer {
$(api_tags::Method::$name(m) => match m {
$(api_tags::$name::$method => {
let mut call_method = || {
reverse_decode!(reader, handle_store; $($arg: $arg_ty),*);
$(let $arg = <$arg_ty>::decode(&mut reader, handle_store);)*
$name::$method(server, $($arg),*)
};
// HACK(eddyb) don't use `panic::catch_unwind` in a panic.
@@ -295,12 +295,7 @@ fn run_bridge_and_client(
let mut dispatch = |buf| dispatcher.dispatch(buf);
run_client(BridgeConfig {
input,
dispatch: (&mut dispatch).into(),
force_show_panics,
_marker: marker::PhantomData,
})
run_client(BridgeConfig { input, dispatch: (&mut dispatch).into(), force_show_panics })
}
}
@@ -331,12 +326,7 @@ fn run_bridge_and_client(
client.recv().expect("server died while client waiting for reply")
};
run_client(BridgeConfig {
input,
dispatch: (&mut dispatch).into(),
force_show_panics,
_marker: marker::PhantomData,
})
run_client(BridgeConfig { input, dispatch: (&mut dispatch).into(), force_show_panics })
});
while let Some(b) = server.recv() {
-1
View File
@@ -27,7 +27,6 @@
#![feature(panic_can_unwind)]
#![feature(restricted_std)]
#![feature(rustc_attrs)]
#![feature(stmt_expr_attributes)]
#![feature(extend_one)]
#![recursion_limit = "256"]
#![allow(internal_features)]
+184 -3
View File
@@ -1412,6 +1412,99 @@ pub fn pop(&mut self) -> bool {
}
}
/// Sets whether the path has a trailing [separator](MAIN_SEPARATOR).
///
/// The value returned by [`has_trailing_sep`](Path::has_trailing_sep) will be equivalent to
/// the provided value if possible.
///
/// # Examples
///
/// ```
/// #![feature(path_trailing_sep)]
/// use std::path::PathBuf;
///
/// let mut p = PathBuf::from("dir");
///
/// assert!(!p.has_trailing_sep());
/// p.set_trailing_sep(false);
/// assert!(!p.has_trailing_sep());
/// p.set_trailing_sep(true);
/// assert!(p.has_trailing_sep());
/// p.set_trailing_sep(false);
/// assert!(!p.has_trailing_sep());
///
/// p = PathBuf::from("/");
/// assert!(p.has_trailing_sep());
/// p.set_trailing_sep(false);
/// assert!(p.has_trailing_sep());
/// ```
#[unstable(feature = "path_trailing_sep", issue = "142503")]
pub fn set_trailing_sep(&mut self, trailing_sep: bool) {
if trailing_sep { self.push_trailing_sep() } else { self.pop_trailing_sep() }
}
/// Adds a trailing [separator](MAIN_SEPARATOR) to the path.
///
/// This acts similarly to [`Path::with_trailing_sep`], but mutates the underlying `PathBuf`.
///
/// # Examples
///
/// ```
/// #![feature(path_trailing_sep)]
/// use std::ffi::OsStr;
/// use std::path::PathBuf;
///
/// let mut p = PathBuf::from("dir");
///
/// assert!(!p.has_trailing_sep());
/// p.push_trailing_sep();
/// assert!(p.has_trailing_sep());
/// p.push_trailing_sep();
/// assert!(p.has_trailing_sep());
///
/// p = PathBuf::from("dir/");
/// p.push_trailing_sep();
/// assert_eq!(p.as_os_str(), OsStr::new("dir/"));
/// ```
#[unstable(feature = "path_trailing_sep", issue = "142503")]
pub fn push_trailing_sep(&mut self) {
if !self.has_trailing_sep() {
self.push("");
}
}
/// Removes a trailing [separator](MAIN_SEPARATOR) from the path, if possible.
///
/// This acts similarly to [`Path::trim_trailing_sep`], but mutates the underlying `PathBuf`.
///
/// # Examples
///
/// ```
/// #![feature(path_trailing_sep)]
/// use std::ffi::OsStr;
/// use std::path::PathBuf;
///
/// let mut p = PathBuf::from("dir//");
///
/// assert!(p.has_trailing_sep());
/// assert_eq!(p.as_os_str(), OsStr::new("dir//"));
/// p.pop_trailing_sep();
/// assert!(!p.has_trailing_sep());
/// assert_eq!(p.as_os_str(), OsStr::new("dir"));
/// p.pop_trailing_sep();
/// assert!(!p.has_trailing_sep());
/// assert_eq!(p.as_os_str(), OsStr::new("dir"));
///
/// p = PathBuf::from("/");
/// assert!(p.has_trailing_sep());
/// p.pop_trailing_sep();
/// assert!(p.has_trailing_sep());
/// ```
#[unstable(feature = "path_trailing_sep", issue = "142503")]
pub fn pop_trailing_sep(&mut self) {
self.inner.truncate(self.trim_trailing_sep().as_os_str().len());
}
/// Updates [`self.file_name`] to `file_name`.
///
/// If [`self.file_name`] was [`None`], this is equivalent to pushing
@@ -1610,7 +1703,7 @@ fn _add_extension(&mut self, extension: &OsStr) -> bool {
let new = extension.as_encoded_bytes();
if !new.is_empty() {
// truncate until right after the file name
// this is necessary for trimming the trailing slash
// this is necessary for trimming the trailing separator
let end_file_name = file_name[file_name.len()..].as_ptr().addr();
let start = self.inner.as_encoded_bytes().as_ptr().addr();
self.inner.truncate(end_file_name.wrapping_sub(start));
@@ -2755,6 +2848,94 @@ pub fn extension(&self) -> Option<&OsStr> {
self.file_name().map(rsplit_file_at_dot).and_then(|(before, after)| before.and(after))
}
/// Checks whether the path ends in a trailing [separator](MAIN_SEPARATOR).
///
/// This is generally done to ensure that a path is treated as a directory, not a file,
/// although it does not actually guarantee that such a path is a directory on the underlying
/// file system.
///
/// Despite this behavior, two paths are still considered the same in Rust whether they have a
/// trailing separator or not.
///
/// # Examples
///
/// ```
/// #![feature(path_trailing_sep)]
/// use std::path::Path;
///
/// assert!(Path::new("dir/").has_trailing_sep());
/// assert!(!Path::new("file.rs").has_trailing_sep());
/// ```
#[unstable(feature = "path_trailing_sep", issue = "142503")]
#[must_use]
#[inline]
pub fn has_trailing_sep(&self) -> bool {
self.as_os_str().as_encoded_bytes().last().copied().is_some_and(is_sep_byte)
}
/// Ensures that a path has a trailing [separator](MAIN_SEPARATOR),
/// allocating a [`PathBuf`] if necessary.
///
/// The resulting path will return true for [`has_trailing_sep`](Self::has_trailing_sep).
///
/// # Examples
///
/// ```
/// #![feature(path_trailing_sep)]
/// use std::ffi::OsStr;
/// use std::path::Path;
///
/// assert_eq!(Path::new("dir//").with_trailing_sep().as_os_str(), OsStr::new("dir//"));
/// assert_eq!(Path::new("dir/").with_trailing_sep().as_os_str(), OsStr::new("dir/"));
/// assert!(!Path::new("dir").has_trailing_sep());
/// assert!(Path::new("dir").with_trailing_sep().has_trailing_sep());
/// ```
#[unstable(feature = "path_trailing_sep", issue = "142503")]
#[must_use]
#[inline]
pub fn with_trailing_sep(&self) -> Cow<'_, Path> {
if self.has_trailing_sep() { Cow::Borrowed(self) } else { Cow::Owned(self.join("")) }
}
/// Trims a trailing [separator](MAIN_SEPARATOR) from a path, if possible.
///
/// The resulting path will return false for [`has_trailing_sep`](Self::has_trailing_sep) for
/// most paths.
///
/// Some paths, like `/`, cannot be trimmed in this way.
///
/// # Examples
///
/// ```
/// #![feature(path_trailing_sep)]
/// use std::ffi::OsStr;
/// use std::path::Path;
///
/// assert_eq!(Path::new("dir//").trim_trailing_sep().as_os_str(), OsStr::new("dir"));
/// assert_eq!(Path::new("dir/").trim_trailing_sep().as_os_str(), OsStr::new("dir"));
/// assert_eq!(Path::new("dir").trim_trailing_sep().as_os_str(), OsStr::new("dir"));
/// assert_eq!(Path::new("/").trim_trailing_sep().as_os_str(), OsStr::new("/"));
/// assert_eq!(Path::new("//").trim_trailing_sep().as_os_str(), OsStr::new("//"));
/// ```
#[unstable(feature = "path_trailing_sep", issue = "142503")]
#[must_use]
#[inline]
pub fn trim_trailing_sep(&self) -> &Path {
if self.has_trailing_sep() && (!self.has_root() || self.parent().is_some()) {
let mut bytes = self.inner.as_encoded_bytes();
while let Some((last, init)) = bytes.split_last()
&& is_sep_byte(*last)
{
bytes = init;
}
// SAFETY: Trimming trailing ASCII bytes will retain the validity of the string.
Path::new(unsafe { OsStr::from_encoded_bytes_unchecked(bytes) })
} else {
self
}
}
/// Creates an owned [`PathBuf`] with `path` adjoined to `self`.
///
/// If `path` is absolute, it replaces the current path.
@@ -2907,7 +3088,7 @@ pub fn with_added_extension<S: AsRef<OsStr>>(&self, extension: S) -> PathBuf {
/// `a/b` all have `a` and `b` as components, but `./a/b` starts with
/// an additional [`CurDir`] component.
///
/// * A trailing slash is normalized away, `/a/b` and `/a/b/` are equivalent.
/// * Trailing separators are normalized away, so `/a/b` and `/a/b/` are equivalent.
///
/// Note that no other normalization takes place; in particular, `a/c`
/// and `a/b/../c` are distinct, to account for the possibility that `b`
@@ -3718,7 +3899,7 @@ impl Error for NormalizeError {}
///
/// On POSIX platforms, the path is resolved using [POSIX semantics][posix-semantics],
/// except that it stops short of resolving symlinks. This means it will keep `..`
/// components and trailing slashes.
/// components and trailing separators.
///
/// On Windows, for verbatim paths, this will simply return the path as given. For other
/// paths, this is currently equivalent to calling
+37 -1
View File
@@ -1,4 +1,9 @@
#![feature(clone_to_uninit, maybe_uninit_slice, normalize_lexically)]
// tidy-alphabetical-start
#![feature(clone_to_uninit)]
#![feature(maybe_uninit_slice)]
#![feature(normalize_lexically)]
#![feature(path_trailing_sep)]
// tidy-alphabetical-end
use std::clone::CloneToUninit;
use std::ffi::OsStr;
@@ -2542,3 +2547,34 @@ fn compare_path_like_to_str_like() {
assert!(path_buf == s);
assert!(s == path_buf);
}
#[test]
fn test_trim_trailing_sep() {
assert_eq!(Path::new("/").trim_trailing_sep().as_os_str(), OsStr::new("/"));
assert_eq!(Path::new("//").trim_trailing_sep().as_os_str(), OsStr::new("//"));
assert_eq!(Path::new("").trim_trailing_sep().as_os_str(), OsStr::new(""));
assert_eq!(Path::new(".").trim_trailing_sep().as_os_str(), OsStr::new("."));
assert_eq!(Path::new("./").trim_trailing_sep().as_os_str(), OsStr::new("."));
assert_eq!(Path::new(".//").trim_trailing_sep().as_os_str(), OsStr::new("."));
assert_eq!(Path::new("..").trim_trailing_sep().as_os_str(), OsStr::new(".."));
assert_eq!(Path::new("../").trim_trailing_sep().as_os_str(), OsStr::new(".."));
assert_eq!(Path::new("..//").trim_trailing_sep().as_os_str(), OsStr::new(".."));
#[cfg(any(windows, target_os = "cygwin"))]
{
assert_eq!(Path::new("\\").trim_trailing_sep().as_os_str(), OsStr::new("\\"));
assert_eq!(Path::new("\\\\").trim_trailing_sep().as_os_str(), OsStr::new("\\\\"));
assert_eq!(Path::new("c:/").trim_trailing_sep().as_os_str(), OsStr::new("c:/"));
assert_eq!(Path::new("c://").trim_trailing_sep().as_os_str(), OsStr::new("c://"));
assert_eq!(Path::new("c:./").trim_trailing_sep().as_os_str(), OsStr::new("c:."));
assert_eq!(Path::new("c:.//").trim_trailing_sep().as_os_str(), OsStr::new("c:."));
assert_eq!(Path::new("c:../").trim_trailing_sep().as_os_str(), OsStr::new("c:.."));
assert_eq!(Path::new("c:..//").trim_trailing_sep().as_os_str(), OsStr::new("c:.."));
assert_eq!(Path::new("c:\\").trim_trailing_sep().as_os_str(), OsStr::new("c:\\"));
assert_eq!(Path::new("c:\\\\").trim_trailing_sep().as_os_str(), OsStr::new("c:\\\\"));
assert_eq!(Path::new("c:.\\").trim_trailing_sep().as_os_str(), OsStr::new("c:."));
assert_eq!(Path::new("c:.\\\\").trim_trailing_sep().as_os_str(), OsStr::new("c:."));
assert_eq!(Path::new("c:..\\").trim_trailing_sep().as_os_str(), OsStr::new("c:.."));
assert_eq!(Path::new("c:..\\\\").trim_trailing_sep().as_os_str(), OsStr::new("c:.."));
}
}
+11 -10
View File
@@ -1024,12 +1024,9 @@ fn make_run(run: RunConfig<'_>) {
run.builder.ensure(Rustc::from_build_compiler(run.builder, compilers.build_compiler(), target));
compilers.build_compiler()
}
Mode::ToolBootstrap => {
// bootstrap/host tools should be documented with the stage 0 compiler
prepare_doc_compiler(run.builder, run.builder.host_target, 1)
}
Mode::ToolTarget => {
// target tools should be documented with the in-tree compiler
// when shipping multiple docs together in one folder,
// they all need to use the same rustdoc version
prepare_doc_compiler(run.builder, run.builder.host_target, run.builder.top_stage)
}
_ => {
@@ -1132,7 +1129,11 @@ fn metadata(&self) -> Option<StepMetadata> {
tool_doc!(
BuildHelper,
"src/build_helper",
mode = Mode::ToolBootstrap,
// ideally, this would use ToolBootstrap,
// but we distribute these docs together in the same folder
// as a bunch of stage1 tools, and you can't mix rustdoc versions
// because that breaks cross-crate data (particularly search)
mode = Mode::ToolTarget,
is_library = true,
crates = ["build_helper"]
);
@@ -1175,25 +1176,25 @@ fn metadata(&self) -> Option<StepMetadata> {
// "specialization" feature in its build script when it detects a nightly toolchain.
allow_features: "specialization"
);
tool_doc!(Tidy, "src/tools/tidy", mode = Mode::ToolBootstrap, crates = ["tidy"]);
tool_doc!(Tidy, "src/tools/tidy", mode = Mode::ToolTarget, crates = ["tidy"]);
tool_doc!(
Bootstrap,
"src/bootstrap",
mode = Mode::ToolBootstrap,
mode = Mode::ToolTarget,
is_library = true,
crates = ["bootstrap"]
);
tool_doc!(
RunMakeSupport,
"src/tools/run-make-support",
mode = Mode::ToolBootstrap,
mode = Mode::ToolTarget,
is_library = true,
crates = ["run_make_support"]
);
tool_doc!(
Compiletest,
"src/tools/compiletest",
mode = Mode::ToolBootstrap,
mode = Mode::ToolTarget,
is_library = true,
crates = ["compiletest"]
);
+10 -8
View File
@@ -1158,13 +1158,12 @@ fn dist_compiler_docs() {
[doc] embedded-book (book) <host>
[doc] edition-guide (book) <host>
[doc] style-guide (book) <host>
[build] rustdoc 0 <host>
[doc] rustc 0 <host> -> Tidy 1 <host>
[doc] rustc 0 <host> -> Bootstrap 1 <host>
[doc] rustc 1 <host> -> Tidy 2 <host>
[doc] rustc 1 <host> -> Bootstrap 2 <host>
[doc] rustc 1 <host> -> releases 2 <host>
[doc] rustc 0 <host> -> RunMakeSupport 1 <host>
[doc] rustc 0 <host> -> BuildHelper 1 <host>
[doc] rustc 0 <host> -> Compiletest 1 <host>
[doc] rustc 1 <host> -> RunMakeSupport 2 <host>
[doc] rustc 1 <host> -> BuildHelper 2 <host>
[doc] rustc 1 <host> -> Compiletest 2 <host>
[build] rustc 0 <host> -> RustInstaller 1 <host>
"
);
@@ -2686,8 +2685,11 @@ fn doc_compiletest_stage_2() {
.path("src/tools/compiletest")
.stage(2)
.render_steps(), @r"
[build] rustdoc 0 <host>
[doc] rustc 0 <host> -> Compiletest 1 <host>
[build] llvm <host>
[build] rustc 0 <host> -> rustc 1 <host>
[build] rustc 1 <host> -> std 1 <host>
[build] rustdoc 1 <host>
[doc] rustc 1 <host> -> Compiletest 2 <host>
");
}
@@ -1,12 +1,5 @@
//@ revisions: current next
//@[next] compile-flags: -Znext-solver
//@[next] failure-status: 101
//@[next] known-bug: unknown
//@[next] normalize-stderr: "note: .*\n\n" -> ""
//@[next] normalize-stderr: "thread 'rustc' panicked.*\n.*\n" -> ""
//@[next] normalize-stderr: "(error: internal compiler error: [^:]+):\d+:\d+: " -> "$1:LL:CC: "
//@[next] normalize-stderr: "delayed at .*" -> ""
//@[next] rustc-env:RUST_BACKTRACE=0
//@ check-pass
trait Super {