Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
31b3915
Some hygiene doc improvements
camsteffen Sep 3, 2025
faf0e14
Update the arm-* and aarch64-* platform docs.
thejpster Sep 7, 2025
f1abb70
Pick up changes from robamu that I missed.
thejpster Sep 10, 2025
b152974
tidy: check that error messages don't start with a capitalized letter
scrabsha Sep 3, 2025
3d3769a
Add missing links in platform-support.md
thejpster Sep 10, 2025
5a74222
Add aarch64-unknown-none to SUMMARY.md
thejpster Sep 10, 2025
409a1fb
Update aarch64-unknown-none.md to include -softfloat too
thejpster Sep 10, 2025
1621ce6
Cleanups from review comments.
thejpster Sep 11, 2025
a0d1f5a
Insert missing word.
thejpster Sep 11, 2025
5d8e41b
Revert "Constify SystemTime methods"
RalfJung Sep 12, 2025
4383a4f
Remove bullet points from the target maintainer list for the arm bare…
thejpster Sep 12, 2025
ea661e9
Re-order and re-work the aarch64-unknown-none platform support page.
thejpster Sep 12, 2025
2baa39e
also apply revert to wasip2
RalfJung Sep 13, 2025
d3b3bb9
Fix small typo in check-cfg.md
mikysett Sep 13, 2025
cc92254
Document `become` keyword
WaffleLapkin May 10, 2023
f13c8c2
fixup `become` kw documentation wrt `#[track_caller]`
WaffleLapkin Sep 13, 2025
f21d3fb
fix Condvar::wait_timeout docs
RalfJung Sep 13, 2025
afd324a
Note that these targets are bare-metal.
thejpster Sep 13, 2025
61a3593
c-variadic: document `core::ffi::VaArgSafe`
folkertdev Sep 11, 2025
a84bb32
c-variadic: test `...` with naked functions
folkertdev Sep 13, 2025
d28c31a
c-variadic: check that c-variadic functions cannot be tail-called
folkertdev Sep 13, 2025
a107ea1
c-variadic: check that inline attributes are accepted on c-variadic f…
folkertdev Sep 13, 2025
e9c80c7
Rollup merge of #113095 - WaffleLapkin:document_becoming_unuwuable, r…
jhpratt Sep 13, 2025
4ee860f
Rollup merge of #146159 - camsteffen:hygiene-docs, r=petrochenkov
jhpratt Sep 13, 2025
141cb38
Rollup merge of #146171 - scrabsha:push-wovnxxwltsun, r=WaffleLapkin
jhpratt Sep 13, 2025
04f17d8
Rollup merge of #146419 - thejpster:update-arm-target-docs, r=working…
jhpratt Sep 13, 2025
5d56e52
Rollup merge of #146473 - RalfJung:system-time-deconst, r=workingjubilee
jhpratt Sep 13, 2025
392d2fa
Rollup merge of #146506 - mikysett:patch-1, r=Urgau
jhpratt Sep 13, 2025
08db938
Rollup merge of #146517 - RalfJung:wait-timeout, r=joboet
jhpratt Sep 13, 2025
da1c27d
Rollup merge of #146521 - folkertdev:document-va-arg-safe, r=workingj…
jhpratt Sep 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ codegen_ssa_cpu_required = target requires explicitly specifying a cpu with `-C
codegen_ssa_create_temp_dir = couldn't create a temp dir: {$error}
codegen_ssa_dlltool_fail_import_library =
Dlltool could not create import library with {$dlltool_path} {$dlltool_args}:
dlltool could not create import library with {$dlltool_path} {$dlltool_args}:
{$stdout}
{$stderr}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_passes/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ passes_const_stable_not_stable =
.label = attribute specified here
passes_custom_mir_incompatible_dialect_and_phase =
The {$dialect} dialect is not compatible with the {$phase} phase
the {$dialect} dialect is not compatible with the {$phase} phase
.dialect_span = this dialect...
.phase_span = ... is not compatible with this phase
Expand Down
30 changes: 20 additions & 10 deletions compiler/rustc_span/src/hygiene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ use crate::symbol::{Symbol, kw, sym};
use crate::{DUMMY_SP, HashStableContext, Span, SpanDecoder, SpanEncoder, with_session_globals};

/// A `SyntaxContext` represents a chain of pairs `(ExpnId, Transparency)` named "marks".
///
/// See <https://rustc-dev-guide.rust-lang.org/macro-expansion.html> for more explanation.
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct SyntaxContext(u32);

Expand All @@ -61,7 +63,10 @@ pub type SyntaxContextKey = (SyntaxContext, ExpnId, Transparency);

#[derive(Clone, Copy, Debug)]
struct SyntaxContextData {
/// The last macro expansion in the chain.
/// (Here we say the most deeply nested macro expansion is the "outermost" expansion.)
outer_expn: ExpnId,
/// Transparency of the last macro expansion
outer_transparency: Transparency,
parent: SyntaxContext,
/// This context, but with all transparent and semi-opaque expansions filtered away.
Expand Down Expand Up @@ -450,11 +455,13 @@ impl HygieneData {
self.syntax_context_data[ctxt.0 as usize].opaque_and_semiopaque
}

/// See [`SyntaxContextData::outer_expn`]
#[inline]
fn outer_expn(&self, ctxt: SyntaxContext) -> ExpnId {
self.syntax_context_data[ctxt.0 as usize].outer_expn
}

/// The last macro expansion and its Transparency
#[inline]
fn outer_mark(&self, ctxt: SyntaxContext) -> (ExpnId, Transparency) {
let data = &self.syntax_context_data[ctxt.0 as usize];
Expand Down Expand Up @@ -900,6 +907,7 @@ impl SyntaxContext {
HygieneData::with(|data| data.normalize_to_macro_rules(self))
}

/// See [`SyntaxContextData::outer_expn`]
#[inline]
pub fn outer_expn(self) -> ExpnId {
HygieneData::with(|data| data.outer_expn(self))
Expand All @@ -912,6 +920,7 @@ impl SyntaxContext {
HygieneData::with(|data| data.expn_data(data.outer_expn(self)).clone())
}

/// See [`HygieneData::outer_mark`]
#[inline]
fn outer_mark(self) -> (ExpnId, Transparency) {
HygieneData::with(|data| data.outer_mark(self))
Expand Down Expand Up @@ -982,19 +991,20 @@ impl Span {
#[derive(Clone, Debug, Encodable, Decodable, HashStable_Generic)]
pub struct ExpnData {
// --- The part unique to each expansion.
/// The kind of this expansion - macro or compiler desugaring.
pub kind: ExpnKind,
/// The expansion that produced this expansion.
/// The expansion that contains the definition of the macro for this expansion.
pub parent: ExpnId,
/// The location of the actual macro invocation or syntax sugar , e.g.
/// `let x = foo!();` or `if let Some(y) = x {}`
/// The span of the macro call which produced this expansion.
///
/// This span will typically have a different `ExpnData` and `call_site`.
/// This recursively traces back through any macro calls which expanded into further
/// macro calls, until the "source call-site" is reached at the root SyntaxContext.
/// For example, if `food!()` expands to `fruit!()` which then expands to `grape`,
/// then the call-site of `grape` is `fruit!()` and the call-site of `fruit!()`
/// is `food!()`.
///
/// This may recursively refer to other macro invocations, e.g., if
/// `foo!()` invoked `bar!()` internally, and there was an
/// expression inside `bar!`; the call_site of the expression in
/// the expansion would point to the `bar!` invocation; that
/// call_site span would have its own ExpnData, with the call_site
/// pointing to the `foo!` invocation.
/// For a desugaring expansion, this is the span of the expression or node that was
/// desugared.
pub call_site: Span,
/// Used to force two `ExpnData`s to have different `Fingerprint`s.
/// Due to macro expansion, it's possible to end up with two `ExpnId`s
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_span/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -710,8 +710,8 @@ impl Span {
if !ctxt.is_root() { ctxt.outer_expn_data().call_site.source_callsite() } else { self }
}

/// The `Span` for the tokens in the previous macro expansion from which `self` was generated,
/// if any.
/// Returns the call-site span of the last macro expansion which produced this `Span`.
/// (see [`ExpnData::call_site`]). Returns `None` if this is not an expansion.
pub fn parent_callsite(self) -> Option<Span> {
let ctxt = self.ctxt();
(!ctxt.is_root()).then(|| ctxt.outer_expn_data().call_site)
Expand Down
29 changes: 23 additions & 6 deletions library/core/src/ffi/va_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,18 +202,23 @@ mod sealed {
impl<T> Sealed for *const T {}
}

/// Trait which permits the allowed types to be used with [`VaListImpl::arg`].
/// Types that are valid to read using [`VaListImpl::arg`].
///
/// # Safety
///
/// This trait must only be implemented for types that C passes as varargs without implicit promotion.
/// The standard library implements this trait for primitive types that are
/// expected to have a variable argument application-binary interface (ABI) on all
/// platforms.
///
/// In C varargs, integers smaller than [`c_int`] and floats smaller than [`c_double`]
/// are implicitly promoted to [`c_int`] and [`c_double`] respectively. Implementing this trait for
/// types that are subject to this promotion rule is invalid.
/// When C passes variable arguments, integers smaller than [`c_int`] and floats smaller
/// than [`c_double`] are implicitly promoted to [`c_int`] and [`c_double`] respectively.
/// Implementing this trait for types that are subject to this promotion rule is invalid.
///
/// [`c_int`]: core::ffi::c_int
/// [`c_double`]: core::ffi::c_double
// We may unseal this trait in the future, but currently our `va_arg` implementations don't support
// types with an alignment larger than 8, or with a non-scalar layout. Inline assembly can be used
// to accept unsupported types in the meantime.
pub unsafe trait VaArgSafe: sealed::Sealed {}

// i8 and i16 are implicitly promoted to c_int in C, and cannot implement `VaArgSafe`.
Expand All @@ -233,7 +238,19 @@ unsafe impl<T> VaArgSafe for *mut T {}
unsafe impl<T> VaArgSafe for *const T {}

impl<'f> VaListImpl<'f> {
/// Advance to the next arg.
/// Advance to and read the next variable argument.
///
/// # Safety
///
/// This function is only sound to call when the next variable argument:
///
/// - has a type that is ABI-compatible with the type `T`
/// - has a value that is a properly initialized value of type `T`
///
/// Calling this function with an incompatible type, an invalid value, or when there
/// are no more variable arguments, is unsound.
///
/// [valid]: https://doc.rust-lang.org/nightly/nomicon/what-unsafe-does.html
#[inline]
pub unsafe fn arg<T: VaArgSafe>(&mut self) -> T {
// SAFETY: the caller must uphold the safety contract for `va_arg`.
Expand Down
102 changes: 102 additions & 0 deletions library/std/src/keyword_docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1257,6 +1257,108 @@ mod ref_keyword {}
/// [`async`]: ../std/keyword.async.html
mod return_keyword {}

#[doc(keyword = "become")]
//
/// Perform a tail-call of a function.
///
/// <div class="warning">
///
/// `feature(explicit_tail_calls)` is currently incomplete and may not work properly.
/// </div>
///
/// When tail calling a function, instead of its stack frame being added to the
/// stack, the stack frame of the caller is directly replaced with the callee's.
/// This means that as long as a loop in a call graph only uses tail calls, the
/// stack growth will be bounded.
///
/// This is useful for writing functional-style code (since it prevents recursion
/// from exhausting resources) or for code optimization (since a tail call
/// *might* be cheaper than a normal call, tail calls can be used in a similar
/// manner to computed goto).
///
/// Example of using `become` to implement functional-style `fold`:
/// ```
/// #![feature(explicit_tail_calls)]
/// #![expect(incomplete_features)]
///
/// fn fold<T: Copy, S>(slice: &[T], init: S, f: impl Fn(S, T) -> S) -> S {
/// match slice {
/// // without `become`, on big inputs this could easily overflow the
/// // stack. using a tail call guarantees that the stack will not grow unboundedly
/// [first, rest @ ..] => become fold(rest, f(init, *first), f),
/// [] => init,
/// }
/// }
/// ```
///
/// Compilers can already perform "tail call optimization" -- they can replace normal
/// calls with tail calls, although there are no guarantees that this will be done.
/// However, to perform TCO, the call needs to be the last thing that happens
/// in the functions and be returned from it. This requirement is often broken
/// by drop code for locals, which is run after computing the return expression:
///
/// ```
/// fn example() {
/// let string = "meow".to_owned();
/// println!("{string}");
/// return help(); // this is *not* the last thing that happens in `example`...
/// }
///
/// // ... because it is desugared to this:
/// fn example_desugared() {
/// let string = "meow".to_owned();
/// println!("{string}");
/// let tmp = help();
/// drop(string);
/// return tmp;
/// }
///
/// fn help() {}
/// ```
///
/// For this reason, `become` also changes the drop order, such that locals are
/// dropped *before* evaluating the call.
///
/// In order to guarantee that the compiler can perform a tail call, `become`
/// currently has these requirements:
/// 1. callee and caller must have the same ABI, arguments, and return type
/// 2. callee and caller must not have varargs
/// 3. caller must not be marked with `#[track_caller]`
/// - callee is allowed to be marked with `#[track_caller]` as otherwise
/// adding `#[track_caller]` would be a breaking change. if callee is
/// marked with `#[track_caller]` a tail call is not guaranteed.
/// 4. callee and caller cannot be a closure
/// (unless it's coerced to a function pointer)
///
/// It is possible to tail-call a function pointer:
/// ```
/// #![feature(explicit_tail_calls)]
/// #![expect(incomplete_features)]
///
/// #[derive(Copy, Clone)]
/// enum Inst { Inc, Dec }
///
/// fn dispatch(stream: &[Inst], state: u32) -> u32 {
/// const TABLE: &[fn(&[Inst], u32) -> u32] = &[increment, decrement];
/// match stream {
/// [inst, rest @ ..] => become TABLE[*inst as usize](rest, state),
/// [] => state,
/// }
/// }
///
/// fn increment(stream: &[Inst], state: u32) -> u32 {
/// become dispatch(stream, state + 1)
/// }
///
/// fn decrement(stream: &[Inst], state: u32) -> u32 {
/// become dispatch(stream, state - 1)
/// }
///
/// let program = &[Inst::Inc, Inst::Inc, Inst::Dec, Inst::Inc];
/// assert_eq!(dispatch(program, 0), 2);
/// ```
mod become_keyword {}

#[doc(keyword = "self")]
//
/// The receiver of a method, or the current module.
Expand Down
5 changes: 0 additions & 5 deletions library/std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,6 @@
#![feature(core_float_math)]
#![feature(decl_macro)]
#![feature(deprecated_suggestion)]
#![feature(derive_const)]
#![feature(doc_cfg)]
#![feature(doc_cfg_hide)]
#![feature(doc_masked)]
Expand Down Expand Up @@ -332,11 +331,7 @@
#![feature(cfg_select)]
#![feature(char_internals)]
#![feature(clone_to_uninit)]
#![feature(const_cmp)]
#![feature(const_convert)]
#![feature(const_ops)]
#![feature(const_option_ops)]
#![feature(const_try)]
#![feature(core_intrinsics)]
#![feature(core_io_borrowed_buf)]
#![feature(drop_guard)]
Expand Down
9 changes: 4 additions & 5 deletions library/std/src/sync/nonpoison/condvar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,11 +198,10 @@ impl Condvar {
/// the system time. This function is susceptible to spurious wakeups.
/// Condition variables normally have a boolean predicate associated with
/// them, and the predicate must always be checked each time this function
/// returns to protect against spurious wakeups. Additionally, it is
/// typically desirable for the timeout to not exceed some duration in
/// spite of spurious wakes, thus the sleep-duration is decremented by the
/// amount slept. Alternatively, use the `wait_timeout_while` method
/// to wait with a timeout while a predicate is true.
/// returns to protect against spurious wakeups. Furthermore, since the timeout
/// is given relative to the moment this function is called, it needs to be adjusted
/// when this function is called in a loop. The [`wait_timeout_while`] method
/// lets you wait with a timeout while a predicate is true, taking care of all these concerns.
///
/// The returned [`WaitTimeoutResult`] value indicates if the timeout is
/// known to have elapsed.
Expand Down
9 changes: 4 additions & 5 deletions library/std/src/sync/poison/condvar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,11 +269,10 @@ impl Condvar {
/// the system time. This function is susceptible to spurious wakeups.
/// Condition variables normally have a boolean predicate associated with
/// them, and the predicate must always be checked each time this function
/// returns to protect against spurious wakeups. Additionally, it is
/// typically desirable for the timeout to not exceed some duration in
/// spite of spurious wakes, thus the sleep-duration is decremented by the
/// amount slept. Alternatively, use the `wait_timeout_while` method
/// to wait with a timeout while a predicate is true.
/// returns to protect against spurious wakeups. Furthermore, since the timeout
/// is given relative to the moment this function is called, it needs to be adjusted
/// when this function is called in a loop. The [`wait_timeout_while`] method
/// lets you wait with a timeout while a predicate is true, taking care of all these concerns.
///
/// The returned [`WaitTimeoutResult`] value indicates if the timeout is
/// known to have elapsed.
Expand Down
32 changes: 10 additions & 22 deletions library/std/src/sys/pal/hermit/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,8 @@ impl Timespec {
Timespec { t: timespec { tv_sec, tv_nsec } }
}

#[rustc_const_unstable(feature = "const_system_time", issue = "144517")]
const fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> {
// FIXME: const PartialOrd
let mut cmp = self.t.tv_sec - other.t.tv_sec;
if cmp == 0 {
cmp = self.t.tv_nsec as i64 - other.t.tv_nsec as i64;
}

if cmp >= 0 {
fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> {
if self >= other {
Ok(if self.t.tv_nsec >= other.t.tv_nsec {
Duration::new(
(self.t.tv_sec - other.t.tv_sec) as u64,
Expand All @@ -53,22 +46,20 @@ impl Timespec {
}
}

#[rustc_const_unstable(feature = "const_system_time", issue = "144517")]
const fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> {
fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> {
let mut secs = self.t.tv_sec.checked_add_unsigned(other.as_secs())?;

// Nano calculations can't overflow because nanos are <1B which fit
// in a u32.
let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32;
if nsec >= NSEC_PER_SEC as u32 {
nsec -= NSEC_PER_SEC as u32;
let mut nsec = other.subsec_nanos() + u32::try_from(self.t.tv_nsec).unwrap();
if nsec >= NSEC_PER_SEC.try_into().unwrap() {
nsec -= u32::try_from(NSEC_PER_SEC).unwrap();
secs = secs.checked_add(1)?;
}
Some(Timespec { t: timespec { tv_sec: secs, tv_nsec: nsec as _ } })
}

#[rustc_const_unstable(feature = "const_system_time", issue = "144517")]
const fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> {
fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> {
let mut secs = self.t.tv_sec.checked_sub_unsigned(other.as_secs())?;

// Similar to above, nanos can't overflow.
Expand Down Expand Up @@ -222,18 +213,15 @@ impl SystemTime {
SystemTime(time)
}

#[rustc_const_unstable(feature = "const_system_time", issue = "144517")]
pub const fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
self.0.sub_timespec(&other.0)
}

#[rustc_const_unstable(feature = "const_system_time", issue = "144517")]
pub const fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
Some(SystemTime(self.0.checked_add_duration(other)?))
}

#[rustc_const_unstable(feature = "const_system_time", issue = "144517")]
pub const fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
Some(SystemTime(self.0.checked_sub_duration(other)?))
}
}
Loading
Loading