Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
2a8215d
Add `rustc_as_ptr` to `Vec::as_non_null`
theemathas Jun 3, 2026
1a2857c
Split `Vec::as_non_null()` into a separate `vec_as_non_null` feature
theemathas Jun 13, 2026
a18a972
Stabilize `box_vec_non_null`
theemathas Jun 1, 2026
aec1f2a
simplify `slice::Iter[Mut]::next_chunk` implementation
WaffleLapkin Jul 6, 2026
b319483
Added implementation on set_permissions_nofollow for all platforms su…
asder8215 Jun 20, 2026
1d08359
don't fire `explicit_outlives_requirements` on unsized type params
Albab-Hasan Jul 27, 2026
d1da7c5
rustdoc-json: Make `Stability` compatible with non-self-describing se…
aDotInTheVoid Jul 26, 2026
2aa74d7
Add regression test for enum unconstrained parameter issue
chenyukang Jul 28, 2026
170973a
Change splat tests to use assert_eq!() rather than println!()
teor2345 Jul 28, 2026
ed93b89
Remove resolved splat FIXMEs in tests
teor2345 Jul 28, 2026
d403761
Add late res on_unknown test
mejrs Jul 14, 2026
023d55e
Factor out async rust 2018+ suggestion
mejrs Jul 16, 2026
80b6fdb
Plug diagnostic::on_unknown into late resolution
mejrs Jul 16, 2026
4a1b9dd
Update late_res test
mejrs Jul 16, 2026
bf4f8c1
remove duplicate local
mejrs Jul 16, 2026
62c5cdd
fix(lld): route version mismatch warnings to linker_info on macOS
r3v5 Jul 23, 2026
39d081f
Fix rustdoc toolbar height when title is taller than one line
GuillaumeGomez Jul 28, 2026
e9ca1ab
Add regression test for rustdoc toolbar buttons height
GuillaumeGomez Jul 28, 2026
0f0b9f8
Simplify `MaybeRequiresStorage`
nnethercote Jul 28, 2026
bf49633
Add note about object lifetime defaulting to description of lint `exp…
fmease Jul 28, 2026
04e2f01
Rollup merge of #158168 - asder8215:windows_set_permissions_nofollow,…
jhpratt Jul 29, 2026
108fa3e
Rollup merge of #160055 - nnethercote:simplify-MaybeRequiresStorage, …
jhpratt Jul 29, 2026
c5ea97e
Rollup merge of #157226 - theemathas:stab-box-vec-non-null, r=nia-e
jhpratt Jul 29, 2026
8c6ef9f
Rollup merge of #158879 - WaffleLapkin:slice_iter_next_chunk_simp, r=…
jhpratt Jul 29, 2026
37c1398
Rollup merge of #159413 - mejrs:on_unknown_late, r=nnethercote
jhpratt Jul 29, 2026
dccf2ca
Rollup merge of #160091 - GuillaumeGomez:rustdoc-toolbar-buttons, r=n…
jhpratt Jul 29, 2026
cc89039
Rollup merge of #158615 - Albab-Hasan:fix-explicit-outlives-unsized, …
jhpratt Jul 29, 2026
05cfae5
Rollup merge of #159666 - r3v5:fix-llvm-ld64-lld-linker-stderr-warnin…
jhpratt Jul 29, 2026
e983685
Rollup merge of #160032 - aDotInTheVoid:push-svrwoqqpoxsu, r=Guillaum…
jhpratt Jul 29, 2026
75dd89e
Rollup merge of #160039 - chenyukang:yukang-fix-159811-enum-unconstra…
jhpratt Jul 29, 2026
4a7ab6f
Rollup merge of #160049 - teor2345:splat-test-assert-eq, r=folkertdev
jhpratt Jul 29, 2026
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
10 changes: 6 additions & 4 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -884,9 +884,9 @@ fn is_msvc_link_exe(sess: &Session) -> bool {
&& linker_path.to_str() == Some("link.exe")
}

fn is_macos_ld(sess: &Session) -> bool {
fn is_macos_linker(sess: &Session) -> bool {
let (_, flavor) = linker_and_flavor(sess);
sess.target.is_like_darwin && matches!(flavor, LinkerFlavor::Darwin(_, Lld::No))
sess.target.is_like_darwin && matches!(flavor, LinkerFlavor::Darwin(..))
}

fn is_windows_gnu_ld(sess: &Session) -> bool {
Expand Down Expand Up @@ -953,8 +953,8 @@ fn report_linker_output(
*output += "\r\n"
}
});
} else if is_macos_ld(sess) {
info!("inferred macOS LD");
} else if is_macos_linker(sess) {
info!("inferred macOS linker");

// FIXME: Tracked by https://github.com/rust-lang/rust/issues/136113
let deployment_mismatch = |line: &str| {
Expand All @@ -967,6 +967,8 @@ fn report_linker_output(
&& line.contains("building for")
&& line.contains("but linking with")
&& line.contains("which was built for newer version"))
// lld (ld64.lld / rust-lld):
|| line.contains("which is newer than target minimum of")
};
// FIXME: This is a real warning we would like to show, but it hits too many crates
// to want to turn it on immediately.
Expand Down
18 changes: 18 additions & 0 deletions compiler/rustc_lint/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2070,6 +2070,24 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
continue;
};
let index = ty_generics.param_def_id_to_index[&def_id];
// Removing a `T: 'r` outlives bound can silently change
// the object lifetime default for `Struct<'r, dyn Trait>`
// (RFC 599): the explicit bound sets the default to `'r`,
// so removing it may change it to `'static` (or cause an
// ambiguity error if there is no unique default). Only
// suppress the lint for non-higher-ranked predicates when
// T is not `Sized` (i.e. can hold trait object types).
// Higher-ranked predicates (`for<'x> T: 'r`) are excluded
// from RFC 599 object lifetime defaulting and are always
// safe to remove.
if predicate.bound_generic_params.is_empty() {
let ty_param = &ty_generics.own_params[index as usize];
let param_ty =
Ty::new_param(cx.tcx, ty_param.index, ty_param.name);
if !param_ty.is_sized(cx.tcx, cx.typing_env()) {
continue;
}
}
(
Self::lifetimes_outliving_type(
// don't warn if the inferred span actually came from the predicate we're looking at
Expand Down
24 changes: 17 additions & 7 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2300,10 +2300,12 @@ declare_lint! {
///
/// See [RFC 2093] for more details.
///
/// > [!WARNING]
/// > Implicit lifetime bounds are not semantically equivalent to explicit ones since the latter
/// > may affect the implicit lifetime bound of trait object types that are passed as arguments
/// > to the overarching struct, enum or union.
/// > [!NOTE]
/// > This lint intentionally doesn't get emitted for explicit outlives-bounds on type
/// > parameters that aren't bounded by `Sized` (unless they're higher-ranked) since unlike
/// > implicit outlives-bounds these may affect the implicit lifetime bound of trait object
/// > types that are passed as arguments to the overarching struct, enum or union.
/// >
/// > Rephrased, they participate in [trait object lifetime defaulting][TOLD].
/// >
/// > Consider the following piece of code where removing bound `T: 'a` would lead to a lifetime
Expand All @@ -2321,9 +2323,17 @@ declare_lint! {
/// > fn render(_: Ref<dyn std::fmt::Display>) {}
/// > ```
/// >
/// > Consequently, removing explicit outlives-bounds on type parameters of publicly reachable types
/// > constitutes a **breaking change** if the lifetime refers to a lifetime parameter and
/// > the type parameter is not bounded by `Sized` (thereby admitting trait object types).
/// > Due to the explicit outlives-bound the function `render` above is equivalent to:
/// >
/// > ```rust,ignore (incomplete)
/// > fn render<'r>(_: Ref<'r, dyn std::fmt::Display + 'r>) {}
/// > ```
/// >
/// > If it wasn't for that explicit bound then the function would mean the following instead:
/// >
/// > ```rust,ignore (incomplete)
/// > fn render<'r>(_: Ref<'r, dyn std::fmt::Display + 'static>) {}
/// > ```
///
/// [RFC 2093]: https://github.com/rust-lang/rfcs/blob/master/text/2093-infer-outlives.md
/// [TOLD]: https://doc.rust-lang.org/reference/lifetime-elision.html#default-trait-object-lifetimes
Expand Down
77 changes: 46 additions & 31 deletions compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use std::borrow::Cow;
use std::cell::RefCell;

use rustc_data_structures::fx::FxHashMap;
use rustc_index::bit_set::DenseBitSet;
use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::*;
use smallvec::SmallVec;

use super::MaybeBorrowedLocals;
use crate::{Analysis, GenKill, ResultsCursor};
use crate::{Analysis, GenKill, Results, ResultsCursor};

/// The set of locals in a MIR body that do not have `StorageLive`/`StorageDead` annotations.
///
Expand Down Expand Up @@ -111,21 +112,51 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeStorageDead<'a> {
}
}

type BorrowedLocalsResults<'mir, 'tcx> = ResultsCursor<'mir, 'tcx, MaybeBorrowedLocals>;
/// For each location, records which locals can be killed by `MaybeRequiresStorage`.
type KillableLocals = FxHashMap<Location, SmallVec<[Local; 4]>>;

/// Dataflow analysis that determines whether each local requires storage at a
/// given location; i.e. whether its storage can go away without being observed.
pub struct MaybeRequiresStorage<'mir, 'tcx> {
borrowed_locals: RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
pub struct MaybeRequiresStorage {
/// Used to kill locals that are fully moved and have not been borrowed.
killable_locals: KillableLocals,
}

impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> {
pub fn new(borrowed_locals: BorrowedLocalsResults<'mir, 'tcx>) -> Self {
MaybeRequiresStorage { borrowed_locals: RefCell::new(borrowed_locals) }
impl MaybeRequiresStorage {
pub fn new<'tcx>(
body: &Body<'tcx>,
borrowed_locals: &Results<'tcx, MaybeBorrowedLocals>,
) -> Self {
struct KillableLocalsVisitor<'mir, 'tcx> {
borrowed_locals_cursor: ResultsCursor<'mir, 'tcx, MaybeBorrowedLocals>,
killable_locals: KillableLocals,
}

impl<'tcx> Visitor<'tcx> for KillableLocalsVisitor<'_, 'tcx> {
fn visit_local(&mut self, local: Local, context: PlaceContext, loc: Location) {
if PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) == context {
self.borrowed_locals_cursor.seek_before_primary_effect(loc);
if !self.borrowed_locals_cursor.get().contains(local) {
self.killable_locals.entry(loc).or_default().push(local);
}
}
}
}

let mut visitor = KillableLocalsVisitor {
borrowed_locals_cursor: ResultsCursor::new_borrowing(body, borrowed_locals),
killable_locals: Default::default(),
};

for (bb, data) in body.basic_blocks.iter_enumerated() {
visitor.visit_basic_block_data(bb, data);
}

MaybeRequiresStorage { killable_locals: visitor.killable_locals }
}
}

impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage {
type Domain = DenseBitSet<Local>;

const NAME: &'static str = "requires_storage";
Expand Down Expand Up @@ -182,8 +213,7 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
stmt: &Statement<'tcx>,
loc: Location,
) {
// If we move from a place then it only stops needing storage *after*
// that statement.
// If we move from a place then it only stops needing storage *after* that statement.
self.check_for_move(state, loc);

match &stmt.kind {
Expand Down Expand Up @@ -316,27 +346,12 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
}
}

impl<'tcx> MaybeRequiresStorage<'_, 'tcx> {
impl MaybeRequiresStorage {
/// Kill locals that are fully moved and have not been borrowed.
fn check_for_move(&self, state: &mut <Self as Analysis<'tcx>>::Domain, loc: Location) {
let mut borrowed_locals = self.borrowed_locals.borrow_mut();
let body = borrowed_locals.body();
let mut visitor = MoveVisitor { state, borrowed_locals: &mut borrowed_locals };
visitor.visit_location(body, loc);
}
}

struct MoveVisitor<'a, 'mir, 'tcx> {
borrowed_locals: &'a mut BorrowedLocalsResults<'mir, 'tcx>,
state: &'a mut DenseBitSet<Local>,
}

impl<'tcx> Visitor<'tcx> for MoveVisitor<'_, '_, 'tcx> {
fn visit_local(&mut self, local: Local, context: PlaceContext, loc: Location) {
if PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) == context {
self.borrowed_locals.seek_before_primary_effect(loc);
if !self.borrowed_locals.get().contains(local) {
self.state.kill(local);
fn check_for_move(&self, state: &mut <Self as Analysis<'_>>::Domain, loc: Location) {
if let Some(locals) = self.killable_locals.get(&loc) {
for &l in locals {
state.kill(l);
}
}
}
Expand Down
19 changes: 8 additions & 11 deletions compiler/rustc_mir_transform/src/coroutine/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,10 @@ pub(super) fn locals_live_across_suspend_points<'tcx>(

// Calculate the MIR locals that have been previously borrowed (even if they are still active).
let borrowed_locals = MaybeBorrowedLocals.iterate_to_fixpoint(tcx, body, Some("coroutine"));
let borrowed_locals_cursor1 = ResultsCursor::new_borrowing(body, &borrowed_locals);
let mut borrowed_locals_cursor2 = ResultsCursor::new_borrowing(body, &borrowed_locals);

// Calculate the MIR locals that we need to keep storage around for.
let requires_storage =
MaybeRequiresStorage::new(borrowed_locals_cursor1).iterate_to_fixpoint(tcx, body, None);
MaybeRequiresStorage::new(body, &borrowed_locals).iterate_to_fixpoint(tcx, body, None);
let mut requires_storage_cursor = ResultsCursor::new_borrowing(body, &requires_storage);

// Calculate the liveness of MIR locals ignoring borrows.
Expand All @@ -109,6 +107,7 @@ pub(super) fn locals_live_across_suspend_points<'tcx>(
let mut live_locals_at_suspension_points = Vec::new();
let mut source_info_at_suspension_points = Vec::new();
let mut live_locals_at_any_suspension_point = DenseBitSet::new_empty(body.local_decls.len());
let mut borrowed_locals_cursor = ResultsCursor::new_owning(body, borrowed_locals);

for (block, data) in body.basic_blocks.iter_enumerated() {
let TerminatorKind::Yield { .. } = data.terminator().kind else { continue };
Expand All @@ -129,8 +128,8 @@ pub(super) fn locals_live_across_suspend_points<'tcx>(
// If a borrow is converted to a raw reference, we must also assume that it lives
// forever. Note that the final liveness is still bounded by the storage liveness
// of the local, which happens using the `intersect` operation below.
borrowed_locals_cursor2.seek_before_primary_effect(loc);
live_locals.union(borrowed_locals_cursor2.get());
borrowed_locals_cursor.seek_before_primary_effect(loc);
live_locals.union(borrowed_locals_cursor.get());
}

// Store the storage liveness for later use so we can restore the state
Expand Down Expand Up @@ -236,7 +235,7 @@ fn compute_storage_conflicts<'mir, 'tcx>(
body: &'mir Body<'tcx>,
saved_locals: &'mir CoroutineSavedLocals,
always_live_locals: DenseBitSet<Local>,
results: &Results<'tcx, MaybeRequiresStorage<'mir, 'tcx>>,
results: &Results<'tcx, MaybeRequiresStorage>,
) -> BitMatrix<CoroutineSavedLocal, CoroutineSavedLocal> {
assert_eq!(body.local_decls.len(), saved_locals.domain_size());

Expand Down Expand Up @@ -294,12 +293,10 @@ struct StorageConflictVisitor<'a, 'tcx> {
eligible_storage_live: DenseBitSet<Local>,
}

impl<'a, 'tcx> ResultsVisitor<'tcx, MaybeRequiresStorage<'a, 'tcx>>
for StorageConflictVisitor<'a, 'tcx>
{
impl<'a, 'tcx> ResultsVisitor<'tcx, MaybeRequiresStorage> for StorageConflictVisitor<'a, 'tcx> {
fn visit_after_early_statement_effect(
&mut self,
_analysis: &MaybeRequiresStorage<'a, 'tcx>,
_analysis: &MaybeRequiresStorage,
state: &DenseBitSet<Local>,
_statement: &Statement<'tcx>,
loc: Location,
Expand All @@ -309,7 +306,7 @@ impl<'a, 'tcx> ResultsVisitor<'tcx, MaybeRequiresStorage<'a, 'tcx>>

fn visit_after_early_terminator_effect(
&mut self,
_analysis: &MaybeRequiresStorage<'a, 'tcx>,
_analysis: &MaybeRequiresStorage,
state: &DenseBitSet<Local>,
_terminator: &Terminator<'tcx>,
loc: Location,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_resolve/src/diagnostics/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3667,7 +3667,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
}

/// Gets the `#[diagnostic::on_unknown]` attribute data associated with this `DefId`.
fn on_unknown_data(&self, def_id: DefId) -> Option<&Directive> {
pub(crate) fn on_unknown_data(&self, def_id: DefId) -> Option<&Directive> {
match def_id.as_local() {
Some(local) => Some(self.on_unknown_data.get(&local)?.directive.as_ref()),
None => find_attr!(self.tcx, def_id, OnUnknown{ directive } => directive)?.as_deref(),
Expand Down
Loading
Loading