Skip to content

Commit 4eedad3

Browse files
committed
Auto merge of #145805 - jhpratt:rollup-h1bm4z7, r=jhpratt
Rollup of 5 pull requests Successful merges: - #144531 (Add lint against integer to pointer transmutes) - #145307 (Fix `LazyLock` poison panic message) - #145554 (rustc-dev-guide subtree update) - #145798 (Use unnamed lifetime spans as primary spans for `MISMATCHED_LIFETIME_SYNTAXES`) - #145799 (std/src/lib.rs: mention "search button" instead of "search bar") r? `@ghost` `@rustbot` modify labels: rollup
2 parents f6d2341 + 7a3675c commit 4eedad3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+688
-246
lines changed

compiler/rustc_lint/messages.ftl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,14 @@ lint_invalid_reference_casting_note_book = for more information, visit <https://
462462
463463
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`
464464
465+
lint_int_to_ptr_transmutes = transmuting an integer to a pointer creates a pointer without provenance
466+
.note = this is dangerous because dereferencing the resulting pointer is undefined behavior
467+
.note_exposed_provenance = exposed provenance semantics can be used to create a pointer based on some previously exposed provenance
468+
.help_transmute = for more information about transmute, see <https://doc.rust-lang.org/std/mem/fn.transmute.html#transmutation-between-pointers-and-integers>
469+
.help_exposed_provenance = for more information about exposed provenance, see <https://doc.rust-lang.org/std/ptr/index.html#exposed-provenance>
470+
.suggestion_with_exposed_provenance = use `std::ptr::with_exposed_provenance{$suffix}` instead to use a previously exposed provenance
471+
.suggestion_without_provenance_mut = if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut`
472+
465473
lint_legacy_derive_helpers = derive helper attribute is used before it is introduced
466474
.label = the attribute is introduced here
467475

compiler/rustc_lint/src/lifetime_syntax.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,9 @@ impl<T> LifetimeSyntaxCategories<Vec<T>> {
214214
}
215215
}
216216

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

@@ -495,7 +495,7 @@ fn emit_mismatch_diagnostic<'tcx>(
495495

496496
cx.emit_span_lint(
497497
MISMATCHED_LIFETIME_SYNTAXES,
498-
inputs.flatten().copied().collect::<Vec<_>>(),
498+
inputs.iter_unnamed().chain(outputs.iter_unnamed()).copied().collect::<Vec<_>>(),
499499
lints::MismatchedLifetimeSyntaxes { inputs, outputs, suggestions },
500500
);
501501
}

compiler/rustc_lint/src/lints.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// ignore-tidy-filelength
2+
13
#![allow(rustc::untranslatable_diagnostic)]
24
use std::num::NonZero;
35

@@ -1542,6 +1544,48 @@ impl<'a> LintDiagnostic<'a, ()> for DropGlue<'_> {
15421544
}
15431545
}
15441546

1547+
// transmute.rs
1548+
#[derive(LintDiagnostic)]
1549+
#[diag(lint_int_to_ptr_transmutes)]
1550+
#[note]
1551+
#[note(lint_note_exposed_provenance)]
1552+
#[help(lint_suggestion_without_provenance_mut)]
1553+
#[help(lint_help_transmute)]
1554+
#[help(lint_help_exposed_provenance)]
1555+
pub(crate) struct IntegerToPtrTransmutes<'tcx> {
1556+
#[subdiagnostic]
1557+
pub suggestion: IntegerToPtrTransmutesSuggestion<'tcx>,
1558+
}
1559+
1560+
#[derive(Subdiagnostic)]
1561+
pub(crate) enum IntegerToPtrTransmutesSuggestion<'tcx> {
1562+
#[multipart_suggestion(
1563+
lint_suggestion_with_exposed_provenance,
1564+
applicability = "machine-applicable",
1565+
style = "verbose"
1566+
)]
1567+
ToPtr {
1568+
dst: Ty<'tcx>,
1569+
suffix: &'static str,
1570+
#[suggestion_part(code = "std::ptr::with_exposed_provenance{suffix}::<{dst}>(")]
1571+
start_call: Span,
1572+
},
1573+
#[multipart_suggestion(
1574+
lint_suggestion_with_exposed_provenance,
1575+
applicability = "machine-applicable",
1576+
style = "verbose"
1577+
)]
1578+
ToRef {
1579+
dst: Ty<'tcx>,
1580+
suffix: &'static str,
1581+
ref_mutbl: &'static str,
1582+
#[suggestion_part(
1583+
code = "&{ref_mutbl}*std::ptr::with_exposed_provenance{suffix}::<{dst}>("
1584+
)]
1585+
start_call: Span,
1586+
},
1587+
}
1588+
15451589
// types.rs
15461590
#[derive(LintDiagnostic)]
15471591
#[diag(lint_range_endpoint_out_of_range)]

compiler/rustc_lint/src/transmute.rs

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use rustc_ast::LitKind;
12
use rustc_errors::Applicability;
23
use rustc_hir::def::{DefKind, Res};
34
use rustc_hir::def_id::LocalDefId;
@@ -7,6 +8,7 @@ use rustc_middle::ty::{self, Ty};
78
use rustc_session::{declare_lint, impl_lint_pass};
89
use rustc_span::sym;
910

11+
use crate::lints::{IntegerToPtrTransmutes, IntegerToPtrTransmutesSuggestion};
1012
use crate::{LateContext, LateLintPass};
1113

1214
declare_lint! {
@@ -67,9 +69,44 @@ declare_lint! {
6769
"detects transmutes that can also be achieved by other operations"
6870
}
6971

72+
declare_lint! {
73+
/// The `integer_to_ptr_transmutes` lint detects integer to pointer
74+
/// transmutes where the resulting pointers are undefined behavior to dereference.
75+
///
76+
/// ### Example
77+
///
78+
/// ```rust
79+
/// fn foo(a: usize) -> *const u8 {
80+
/// unsafe {
81+
/// std::mem::transmute::<usize, *const u8>(a)
82+
/// }
83+
/// }
84+
/// ```
85+
///
86+
/// {{produces}}
87+
///
88+
/// ### Explanation
89+
///
90+
/// Any attempt to use the resulting pointers are undefined behavior as the resulting
91+
/// pointers won't have any provenance.
92+
///
93+
/// Alternatively, [`std::ptr::with_exposed_provenance`] should be used, as they do not
94+
/// carry the provenance requirement. If wanting to create pointers without provenance
95+
/// [`std::ptr::without_provenance`] should be used instead.
96+
///
97+
/// See [`std::mem::transmute`] in the reference for more details.
98+
///
99+
/// [`std::mem::transmute`]: https://doc.rust-lang.org/std/mem/fn.transmute.html
100+
/// [`std::ptr::with_exposed_provenance`]: https://doc.rust-lang.org/std/ptr/fn.with_exposed_provenance.html
101+
/// [`std::ptr::without_provenance`]: https://doc.rust-lang.org/std/ptr/fn.without_provenance.html
102+
pub INTEGER_TO_PTR_TRANSMUTES,
103+
Warn,
104+
"detects integer to pointer transmutes",
105+
}
106+
70107
pub(crate) struct CheckTransmutes;
71108

72-
impl_lint_pass!(CheckTransmutes => [PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS, UNNECESSARY_TRANSMUTES]);
109+
impl_lint_pass!(CheckTransmutes => [PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS, UNNECESSARY_TRANSMUTES, INTEGER_TO_PTR_TRANSMUTES]);
73110

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

95132
check_ptr_transmute_in_const(cx, expr, body_owner_def_id, const_context, src, dst);
96133
check_unnecessary_transmute(cx, expr, callee, arg, const_context, src, dst);
134+
check_int_to_ptr_transmute(cx, expr, arg, src, dst);
97135
}
98136
}
99137

138+
/// Check for transmutes from integer to pointers (*const/*mut and &/&mut).
139+
///
140+
/// Using the resulting pointers would be undefined behavior.
141+
fn check_int_to_ptr_transmute<'tcx>(
142+
cx: &LateContext<'tcx>,
143+
expr: &'tcx hir::Expr<'tcx>,
144+
arg: &'tcx hir::Expr<'tcx>,
145+
src: Ty<'tcx>,
146+
dst: Ty<'tcx>,
147+
) {
148+
if !matches!(src.kind(), ty::Uint(_) | ty::Int(_)) {
149+
return;
150+
}
151+
let (ty::Ref(_, inner_ty, mutbl) | ty::RawPtr(inner_ty, mutbl)) = dst.kind() else {
152+
return;
153+
};
154+
// bail-out if the argument is literal 0 as we have other lints for those cases
155+
if matches!(arg.kind, hir::ExprKind::Lit(hir::Lit { node: LitKind::Int(v, _), .. }) if v == 0) {
156+
return;
157+
}
158+
// bail-out if the inner type is a ZST
159+
let Ok(layout_inner_ty) = cx.tcx.layout_of(cx.typing_env().as_query_input(*inner_ty)) else {
160+
return;
161+
};
162+
if layout_inner_ty.is_1zst() {
163+
return;
164+
}
165+
166+
let suffix = if mutbl.is_mut() { "_mut" } else { "" };
167+
cx.tcx.emit_node_span_lint(
168+
INTEGER_TO_PTR_TRANSMUTES,
169+
expr.hir_id,
170+
expr.span,
171+
IntegerToPtrTransmutes {
172+
suggestion: if dst.is_ref() {
173+
IntegerToPtrTransmutesSuggestion::ToRef {
174+
dst: *inner_ty,
175+
suffix,
176+
ref_mutbl: mutbl.prefix_str(),
177+
start_call: expr.span.shrink_to_lo().until(arg.span),
178+
}
179+
} else {
180+
IntegerToPtrTransmutesSuggestion::ToPtr {
181+
dst: *inner_ty,
182+
suffix,
183+
start_call: expr.span.shrink_to_lo().until(arg.span),
184+
}
185+
},
186+
},
187+
);
188+
}
189+
100190
/// Check for transmutes that exhibit undefined behavior.
101191
/// For example, transmuting pointers to integers in a const context.
102192
///

library/core/src/ptr/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -914,6 +914,7 @@ pub const fn dangling<T>() -> *const T {
914914
#[must_use]
915915
#[stable(feature = "strict_provenance", since = "1.84.0")]
916916
#[rustc_const_stable(feature = "strict_provenance", since = "1.84.0")]
917+
#[allow(integer_to_ptr_transmutes)] // Expected semantics here.
917918
pub const fn without_provenance_mut<T>(addr: usize) -> *mut T {
918919
// An int-to-pointer transmute currently has exactly the intended semantics: it creates a
919920
// pointer without provenance. Note that this is *not* a stable guarantee about transmute

library/std/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
//!
1616
//! If you already know the name of what you are looking for, the fastest way to
1717
//! find it is to use the <a href="#" onclick="window.searchState.focus();">search
18-
//! bar</a> at the top of the page.
18+
//! button</a> at the top of the page.
1919
//!
2020
//! Otherwise, you may want to jump to one of these useful sections:
2121
//!

library/std/src/sync/lazy_lock.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,11 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> {
244244
#[inline]
245245
#[stable(feature = "lazy_cell", since = "1.80.0")]
246246
pub fn force(this: &LazyLock<T, F>) -> &T {
247-
this.once.call_once(|| {
247+
this.once.call_once_force(|state| {
248+
if state.is_poisoned() {
249+
panic_poisoned();
250+
}
251+
248252
// SAFETY: `call_once` only runs this closure once, ever.
249253
let data = unsafe { &mut *this.data.get() };
250254
let f = unsafe { ManuallyDrop::take(&mut data.f) };
@@ -257,8 +261,7 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> {
257261
// * the closure was called and initialized `value`.
258262
// * the closure was called and panicked, so this point is never reached.
259263
// * the closure was not called, but a previous call initialized `value`.
260-
// * the closure was not called because the Once is poisoned, so this point
261-
// is never reached.
264+
// * the closure was not called because the Once is poisoned, which we handled above.
262265
// So `value` has definitely been initialized and will not be modified again.
263266
unsafe { &*(*this.data.get()).value }
264267
}

library/std/tests/sync/lazy_lock.rs

Lines changed: 53 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,6 @@ fn lazy_default() {
3333
assert_eq!(CALLED.load(SeqCst), 1);
3434
}
3535

36-
#[test]
37-
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
38-
fn lazy_poisoning() {
39-
let x: LazyCell<String> = LazyCell::new(|| panic!("kaboom"));
40-
for _ in 0..2 {
41-
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| x.len()));
42-
assert!(res.is_err());
43-
}
44-
}
45-
4636
#[test]
4737
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
4838
fn sync_lazy_new() {
@@ -123,16 +113,6 @@ fn static_sync_lazy_via_fn() {
123113
assert_eq!(xs(), &vec![1, 2, 3]);
124114
}
125115

126-
#[test]
127-
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
128-
fn sync_lazy_poisoning() {
129-
let x: LazyLock<String> = LazyLock::new(|| panic!("kaboom"));
130-
for _ in 0..2 {
131-
let res = panic::catch_unwind(|| x.len());
132-
assert!(res.is_err());
133-
}
134-
}
135-
136116
// Check that we can infer `T` from closure's type.
137117
#[test]
138118
fn lazy_type_inference() {
@@ -145,17 +125,6 @@ fn is_sync_send() {
145125
assert_traits::<LazyLock<String>>();
146126
}
147127

148-
#[test]
149-
#[should_panic = "has previously been poisoned"]
150-
fn lazy_force_mut_panic() {
151-
let mut lazy = LazyLock::<String>::new(|| panic!());
152-
panic::catch_unwind(panic::AssertUnwindSafe(|| {
153-
let _ = LazyLock::force_mut(&mut lazy);
154-
}))
155-
.unwrap_err();
156-
let _ = &*lazy;
157-
}
158-
159128
#[test]
160129
fn lazy_force_mut() {
161130
let s = "abc".to_owned();
@@ -165,3 +134,56 @@ fn lazy_force_mut() {
165134
p.clear();
166135
LazyLock::force_mut(&mut lazy);
167136
}
137+
138+
#[test]
139+
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
140+
fn lazy_poisoning() {
141+
let x: LazyCell<String> = LazyCell::new(|| panic!("kaboom"));
142+
for _ in 0..2 {
143+
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| x.len()));
144+
assert!(res.is_err());
145+
}
146+
}
147+
148+
/// Verifies that when a `LazyLock` is poisoned, it panics with the correct error message ("LazyLock
149+
/// instance has previously been poisoned") instead of the underlying `Once` error message.
150+
#[test]
151+
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
152+
#[should_panic(expected = "LazyLock instance has previously been poisoned")]
153+
fn lazy_lock_deref_panic() {
154+
let lazy: LazyLock<String> = LazyLock::new(|| panic!("initialization failed"));
155+
156+
// First access will panic during initialization.
157+
let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| {
158+
let _ = &*lazy;
159+
}));
160+
161+
// Second access should panic with the poisoned message.
162+
let _ = &*lazy;
163+
}
164+
165+
#[test]
166+
#[should_panic(expected = "LazyLock instance has previously been poisoned")]
167+
fn lazy_lock_deref_mut_panic() {
168+
let mut lazy: LazyLock<String> = LazyLock::new(|| panic!("initialization failed"));
169+
170+
// First access will panic during initialization.
171+
let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| {
172+
let _ = LazyLock::force_mut(&mut lazy);
173+
}));
174+
175+
// Second access should panic with the poisoned message.
176+
let _ = &*lazy;
177+
}
178+
179+
/// Verifies that when the initialization closure panics with a custom message, that message is
180+
/// preserved and not overridden by `LazyLock`.
181+
#[test]
182+
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
183+
#[should_panic(expected = "custom panic message from closure")]
184+
fn lazy_lock_preserves_closure_panic_message() {
185+
let lazy: LazyLock<String> = LazyLock::new(|| panic!("custom panic message from closure"));
186+
187+
// This should panic with the original message from the closure.
188+
let _ = &*lazy;
189+
}

src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ name: rustc-pull
33
on:
44
workflow_dispatch:
55
schedule:
6-
# Run at 04:00 UTC every Monday and Thursday
7-
- cron: '0 4 * * 1,4'
6+
# Run at 04:00 UTC every Monday
7+
- cron: '0 4 * * 1'
88

99
jobs:
1010
pull:

src/doc/rustc-dev-guide/rust-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
6bcdcc73bd11568fd85f5a38b58e1eda054ad1cd
1+
425a9c0a0e365c0b8c6cfd00c2ded83a73bed9a0

0 commit comments

Comments
 (0)