Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
186ea6e
Only run the pull workflow once per week
Kobzol Aug 7, 2025
96083af
Merge pull request #2540 from Kobzol/ci-cron-interval
tshepang Aug 7, 2025
9ade653
Remove mentions of Discord.
kpreid Aug 8, 2025
08461ec
Merge pull request #2541 from kpreid/discord
Noratrieb Aug 8, 2025
c12f83e
add a tip for english writing
chenyukang Aug 11, 2025
5abfd82
Merge pull request #2543 from chenyukang/yukang-add-tip-for-english-w…
tshepang Aug 11, 2025
97564bd
place link on the more suitable text
tshepang Aug 11, 2025
3b52678
Merge pull request #2544 from rust-lang/tshepang-patch-1
tshepang Aug 11, 2025
538299a
fix grammar
tshepang Aug 11, 2025
f2294bb
Merge pull request #2545 from rust-lang/tshepang-patch-1
Noratrieb Aug 11, 2025
a3510c7
Prepare for merging from rust-lang/rust
invalid-email-address Aug 18, 2025
5818cbd
Merge ref '425a9c0a0e36' from rust-lang/rust
invalid-email-address Aug 18, 2025
2787e9e
Merge pull request #2547 from rust-lang/rustc-pull
tshepang Aug 18, 2025
06eb782
modify `LazyLock` poison panic message
connortsui20 Aug 22, 2025
399f5e2
std/src/lib.rs: mention "search button" instead of "search bar"
ada4a Aug 23, 2025
d4cbd9a
Add lint against integer to pointer transmutes
Urgau Jul 27, 2025
f25bf37
Allow `integer_to_ptr_transmutes` in core
Urgau Jul 27, 2025
3c66478
Allow `integer_to_ptr_transmutes` in tests
Urgau Jul 27, 2025
02a67cc
Adjust clippy lints for rustc `integer_to_ptr_transmutes` lint
Urgau Jul 27, 2025
1da4959
Prefer verbose suggestions for `integer_to_ptr_transmutes` lint
Urgau Aug 16, 2025
e4557f0
Use unnamed lifetime spans as primary spans for MISMATCHED_LIFETIME_S…
compiler-errors Aug 23, 2025
2655036
Rollup merge of #144531 - Urgau:int_to_ptr_transmutes, r=jackh726
jhpratt Aug 24, 2025
d5340c2
Rollup merge of #145307 - connortsui20:lazylock-poison-msg, r=Amanieu
jhpratt Aug 24, 2025
7026c84
Rollup merge of #145554 - tshepang:rdg-sync, r=BoxyUwU
jhpratt Aug 24, 2025
ccfe968
Rollup merge of #145798 - compiler-errors:unnamed-lt-primary, r=lqd
jhpratt Aug 24, 2025
7a3675c
Rollup merge of #145799 - ada4a:patch-3, r=GuillaumeGomez
jhpratt Aug 24, 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
8 changes: 8 additions & 0 deletions compiler/rustc_lint/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,14 @@ lint_invalid_reference_casting_note_book = for more information, visit <https://

lint_invalid_reference_casting_note_ty_has_interior_mutability = even for types with interior mutability, the only legal way to obtain a mutable pointer from a shared reference is through `UnsafeCell::get`

lint_int_to_ptr_transmutes = transmuting an integer to a pointer creates a pointer without provenance
.note = this is dangerous because dereferencing the resulting pointer is undefined behavior
.note_exposed_provenance = exposed provenance semantics can be used to create a pointer based on some previously exposed provenance
.help_transmute = for more information about transmute, see <https://doc.rust-lang.org/std/mem/fn.transmute.html#transmutation-between-pointers-and-integers>
.help_exposed_provenance = for more information about exposed provenance, see <https://doc.rust-lang.org/std/ptr/index.html#exposed-provenance>
.suggestion_with_exposed_provenance = use `std::ptr::with_exposed_provenance{$suffix}` instead to use a previously exposed provenance
.suggestion_without_provenance_mut = if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut`

lint_legacy_derive_helpers = derive helper attribute is used before it is introduced
.label = the attribute is introduced here

Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_lint/src/lifetime_syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,9 @@ impl<T> LifetimeSyntaxCategories<Vec<T>> {
}
}

pub fn flatten(&self) -> impl Iterator<Item = &T> {
let Self { hidden, elided, named } = self;
[hidden.iter(), elided.iter(), named.iter()].into_iter().flatten()
pub fn iter_unnamed(&self) -> impl Iterator<Item = &T> {
let Self { hidden, elided, named: _ } = self;
[hidden.iter(), elided.iter()].into_iter().flatten()
}
}

Expand Down Expand Up @@ -495,7 +495,7 @@ fn emit_mismatch_diagnostic<'tcx>(

cx.emit_span_lint(
MISMATCHED_LIFETIME_SYNTAXES,
inputs.flatten().copied().collect::<Vec<_>>(),
inputs.iter_unnamed().chain(outputs.iter_unnamed()).copied().collect::<Vec<_>>(),
lints::MismatchedLifetimeSyntaxes { inputs, outputs, suggestions },
);
}
Expand Down
44 changes: 44 additions & 0 deletions compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// ignore-tidy-filelength

#![allow(rustc::untranslatable_diagnostic)]
use std::num::NonZero;

Expand Down Expand Up @@ -1542,6 +1544,48 @@ impl<'a> LintDiagnostic<'a, ()> for DropGlue<'_> {
}
}

// transmute.rs
#[derive(LintDiagnostic)]
#[diag(lint_int_to_ptr_transmutes)]
#[note]
#[note(lint_note_exposed_provenance)]
#[help(lint_suggestion_without_provenance_mut)]
#[help(lint_help_transmute)]
#[help(lint_help_exposed_provenance)]
pub(crate) struct IntegerToPtrTransmutes<'tcx> {
#[subdiagnostic]
pub suggestion: IntegerToPtrTransmutesSuggestion<'tcx>,
}

#[derive(Subdiagnostic)]
pub(crate) enum IntegerToPtrTransmutesSuggestion<'tcx> {
#[multipart_suggestion(
lint_suggestion_with_exposed_provenance,
applicability = "machine-applicable",
style = "verbose"
)]
ToPtr {
dst: Ty<'tcx>,
suffix: &'static str,
#[suggestion_part(code = "std::ptr::with_exposed_provenance{suffix}::<{dst}>(")]
start_call: Span,
},
#[multipart_suggestion(
lint_suggestion_with_exposed_provenance,
applicability = "machine-applicable",
style = "verbose"
)]
ToRef {
dst: Ty<'tcx>,
suffix: &'static str,
ref_mutbl: &'static str,
#[suggestion_part(
code = "&{ref_mutbl}*std::ptr::with_exposed_provenance{suffix}::<{dst}>("
)]
start_call: Span,
},
}

// types.rs
#[derive(LintDiagnostic)]
#[diag(lint_range_endpoint_out_of_range)]
Expand Down
92 changes: 91 additions & 1 deletion compiler/rustc_lint/src/transmute.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use rustc_ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::LocalDefId;
Expand All @@ -7,6 +8,7 @@ use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_lint, impl_lint_pass};
use rustc_span::sym;

use crate::lints::{IntegerToPtrTransmutes, IntegerToPtrTransmutesSuggestion};
use crate::{LateContext, LateLintPass};

declare_lint! {
Expand Down Expand Up @@ -67,9 +69,44 @@ declare_lint! {
"detects transmutes that can also be achieved by other operations"
}

declare_lint! {
/// The `integer_to_ptr_transmutes` lint detects integer to pointer
/// transmutes where the resulting pointers are undefined behavior to dereference.
///
/// ### Example
///
/// ```rust
/// fn foo(a: usize) -> *const u8 {
/// unsafe {
/// std::mem::transmute::<usize, *const u8>(a)
/// }
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Any attempt to use the resulting pointers are undefined behavior as the resulting
/// pointers won't have any provenance.
///
/// Alternatively, [`std::ptr::with_exposed_provenance`] should be used, as they do not
/// carry the provenance requirement. If wanting to create pointers without provenance
/// [`std::ptr::without_provenance`] should be used instead.
///
/// See [`std::mem::transmute`] in the reference for more details.
///
/// [`std::mem::transmute`]: https://doc.rust-lang.org/std/mem/fn.transmute.html
/// [`std::ptr::with_exposed_provenance`]: https://doc.rust-lang.org/std/ptr/fn.with_exposed_provenance.html
/// [`std::ptr::without_provenance`]: https://doc.rust-lang.org/std/ptr/fn.without_provenance.html
pub INTEGER_TO_PTR_TRANSMUTES,
Warn,
"detects integer to pointer transmutes",
}

pub(crate) struct CheckTransmutes;

impl_lint_pass!(CheckTransmutes => [PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS, UNNECESSARY_TRANSMUTES]);
impl_lint_pass!(CheckTransmutes => [PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS, UNNECESSARY_TRANSMUTES, INTEGER_TO_PTR_TRANSMUTES]);

impl<'tcx> LateLintPass<'tcx> for CheckTransmutes {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
Expand All @@ -94,9 +131,62 @@ impl<'tcx> LateLintPass<'tcx> for CheckTransmutes {

check_ptr_transmute_in_const(cx, expr, body_owner_def_id, const_context, src, dst);
check_unnecessary_transmute(cx, expr, callee, arg, const_context, src, dst);
check_int_to_ptr_transmute(cx, expr, arg, src, dst);
}
}

/// Check for transmutes from integer to pointers (*const/*mut and &/&mut).
///
/// Using the resulting pointers would be undefined behavior.
fn check_int_to_ptr_transmute<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'tcx>,
arg: &'tcx hir::Expr<'tcx>,
src: Ty<'tcx>,
dst: Ty<'tcx>,
) {
if !matches!(src.kind(), ty::Uint(_) | ty::Int(_)) {
return;
}
let (ty::Ref(_, inner_ty, mutbl) | ty::RawPtr(inner_ty, mutbl)) = dst.kind() else {
return;
};
// bail-out if the argument is literal 0 as we have other lints for those cases
if matches!(arg.kind, hir::ExprKind::Lit(hir::Lit { node: LitKind::Int(v, _), .. }) if v == 0) {
return;
}
// bail-out if the inner type is a ZST
let Ok(layout_inner_ty) = cx.tcx.layout_of(cx.typing_env().as_query_input(*inner_ty)) else {
return;
};
if layout_inner_ty.is_1zst() {
return;
}

let suffix = if mutbl.is_mut() { "_mut" } else { "" };
cx.tcx.emit_node_span_lint(
INTEGER_TO_PTR_TRANSMUTES,
expr.hir_id,
expr.span,
IntegerToPtrTransmutes {
suggestion: if dst.is_ref() {
IntegerToPtrTransmutesSuggestion::ToRef {
dst: *inner_ty,
suffix,
ref_mutbl: mutbl.prefix_str(),
start_call: expr.span.shrink_to_lo().until(arg.span),
}
} else {
IntegerToPtrTransmutesSuggestion::ToPtr {
dst: *inner_ty,
suffix,
start_call: expr.span.shrink_to_lo().until(arg.span),
}
},
},
);
}

/// Check for transmutes that exhibit undefined behavior.
/// For example, transmuting pointers to integers in a const context.
///
Expand Down
1 change: 1 addition & 0 deletions library/core/src/ptr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,7 @@ pub const fn dangling<T>() -> *const T {
#[must_use]
#[stable(feature = "strict_provenance", since = "1.84.0")]
#[rustc_const_stable(feature = "strict_provenance", since = "1.84.0")]
#[allow(integer_to_ptr_transmutes)] // Expected semantics here.
pub const fn without_provenance_mut<T>(addr: usize) -> *mut T {
// An int-to-pointer transmute currently has exactly the intended semantics: it creates a
// pointer without provenance. Note that this is *not* a stable guarantee about transmute
Expand Down
2 changes: 1 addition & 1 deletion library/std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
//!
//! If you already know the name of what you are looking for, the fastest way to
//! find it is to use the <a href="#" onclick="window.searchState.focus();">search
//! bar</a> at the top of the page.
//! button</a> at the top of the page.
//!
//! Otherwise, you may want to jump to one of these useful sections:
//!
Expand Down
9 changes: 6 additions & 3 deletions library/std/src/sync/lazy_lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,11 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> {
#[inline]
#[stable(feature = "lazy_cell", since = "1.80.0")]
pub fn force(this: &LazyLock<T, F>) -> &T {
this.once.call_once(|| {
this.once.call_once_force(|state| {
if state.is_poisoned() {
panic_poisoned();
}

// SAFETY: `call_once` only runs this closure once, ever.
let data = unsafe { &mut *this.data.get() };
let f = unsafe { ManuallyDrop::take(&mut data.f) };
Expand All @@ -257,8 +261,7 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> {
// * the closure was called and initialized `value`.
// * the closure was called and panicked, so this point is never reached.
// * the closure was not called, but a previous call initialized `value`.
// * the closure was not called because the Once is poisoned, so this point
// is never reached.
// * the closure was not called because the Once is poisoned, which we handled above.
// So `value` has definitely been initialized and will not be modified again.
unsafe { &*(*this.data.get()).value }
}
Expand Down
84 changes: 53 additions & 31 deletions library/std/tests/sync/lazy_lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,6 @@ fn lazy_default() {
assert_eq!(CALLED.load(SeqCst), 1);
}

#[test]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn lazy_poisoning() {
let x: LazyCell<String> = LazyCell::new(|| panic!("kaboom"));
for _ in 0..2 {
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| x.len()));
assert!(res.is_err());
}
}

#[test]
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
fn sync_lazy_new() {
Expand Down Expand Up @@ -123,16 +113,6 @@ fn static_sync_lazy_via_fn() {
assert_eq!(xs(), &vec![1, 2, 3]);
}

#[test]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn sync_lazy_poisoning() {
let x: LazyLock<String> = LazyLock::new(|| panic!("kaboom"));
for _ in 0..2 {
let res = panic::catch_unwind(|| x.len());
assert!(res.is_err());
}
}

// Check that we can infer `T` from closure's type.
#[test]
fn lazy_type_inference() {
Expand All @@ -145,17 +125,6 @@ fn is_sync_send() {
assert_traits::<LazyLock<String>>();
}

#[test]
#[should_panic = "has previously been poisoned"]
fn lazy_force_mut_panic() {
let mut lazy = LazyLock::<String>::new(|| panic!());
panic::catch_unwind(panic::AssertUnwindSafe(|| {
let _ = LazyLock::force_mut(&mut lazy);
}))
.unwrap_err();
let _ = &*lazy;
}

#[test]
fn lazy_force_mut() {
let s = "abc".to_owned();
Expand All @@ -165,3 +134,56 @@ fn lazy_force_mut() {
p.clear();
LazyLock::force_mut(&mut lazy);
}

#[test]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn lazy_poisoning() {
let x: LazyCell<String> = LazyCell::new(|| panic!("kaboom"));
for _ in 0..2 {
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| x.len()));
assert!(res.is_err());
}
}

/// Verifies that when a `LazyLock` is poisoned, it panics with the correct error message ("LazyLock
/// instance has previously been poisoned") instead of the underlying `Once` error message.
#[test]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
#[should_panic(expected = "LazyLock instance has previously been poisoned")]
fn lazy_lock_deref_panic() {
let lazy: LazyLock<String> = LazyLock::new(|| panic!("initialization failed"));

// First access will panic during initialization.
let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| {
let _ = &*lazy;
}));

// Second access should panic with the poisoned message.
let _ = &*lazy;
}

#[test]
#[should_panic(expected = "LazyLock instance has previously been poisoned")]
fn lazy_lock_deref_mut_panic() {
let mut lazy: LazyLock<String> = LazyLock::new(|| panic!("initialization failed"));

// First access will panic during initialization.
let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| {
let _ = LazyLock::force_mut(&mut lazy);
}));

// Second access should panic with the poisoned message.
let _ = &*lazy;
}

/// Verifies that when the initialization closure panics with a custom message, that message is
/// preserved and not overridden by `LazyLock`.
#[test]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
#[should_panic(expected = "custom panic message from closure")]
fn lazy_lock_preserves_closure_panic_message() {
let lazy: LazyLock<String> = LazyLock::new(|| panic!("custom panic message from closure"));

// This should panic with the original message from the closure.
let _ = &*lazy;
}
4 changes: 2 additions & 2 deletions src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ name: rustc-pull
on:
workflow_dispatch:
schedule:
# Run at 04:00 UTC every Monday and Thursday
- cron: '0 4 * * 1,4'
# Run at 04:00 UTC every Monday
- cron: '0 4 * * 1'

jobs:
pull:
Expand Down
2 changes: 1 addition & 1 deletion src/doc/rustc-dev-guide/rust-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
6bcdcc73bd11568fd85f5a38b58e1eda054ad1cd
425a9c0a0e365c0b8c6cfd00c2ded83a73bed9a0
1 change: 0 additions & 1 deletion src/doc/rustc-dev-guide/src/about-this-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ You might also find the following sites useful:
of the team procedures, active working groups, and the team calendar.
- [std-dev-guide] -- a similar guide for developing the standard library.
- [The t-compiler zulip][z]
- `#contribute` and `#wg-rustup` on [Discord](https://discord.gg/rust-lang).
- The [Rust Internals forum][rif], a place to ask questions and
discuss Rust's internals
- The [Rust reference][rr], even though it doesn't specifically talk about
Expand Down
2 changes: 1 addition & 1 deletion src/doc/rustc-dev-guide/src/diagnostics/error-codes.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ explanations should help users understand why their code cannot be accepted by
the compiler. Rust prides itself on helpful error messages and long-form
explanations are no exception. However, before error explanations are
overhauled[^new-explanations] it is a bit open as to how exactly they should be
written, as always: ask your reviewer or ask around on the Rust Discord or Zulip.
written, as always: ask your reviewer or ask around on the Rust Zulip.

[^new-explanations]: See the draft RFC [here][new-explanations-rfc].

Expand Down
Loading
Loading