Skip to content

Rollup of 17 pull requests #145210

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 45 commits into from
Aug 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
30721b0
Add stubs for environment variables; document some of the important ones
jyn514 May 27, 2025
a383fb0
asm: Stabilize loongarch32
heiher Jan 9, 2025
90bb5ca
moved 34 tests to organized locations
Kivooeo Jul 24, 2025
d9b725a
Improve error output when a command fails in bootstrap
Kobzol Aug 8, 2025
43327b5
switch polonius compare-mode to polonius=next
lqd Feb 14, 2025
f4094ea
update test expectations for boring locals + dropckoutlives interactions
lqd Mar 24, 2025
a5adde8
simplify polonius=next
lqd Aug 8, 2025
1330898
new impl fixes crash test
lqd Jun 16, 2025
d4bbd68
turn expensive assert into debug assertion
lqd Jun 18, 2025
b172980
add some test cases for overlapping yielded items
lqd Jun 26, 2025
48ebae9
add NLL-like imprecision example
lqd Jun 26, 2025
9badbdf
add cursor-like example that works
lqd Jun 26, 2025
b99fe2b
mark polonius=next's NLL imprecisions as known-bugs
lqd Aug 8, 2025
dcc1605
[win][arm64ec] Partial fix for raw-dylib-link-ordinal on Arm64EC
dpaoliello Aug 8, 2025
87a09b2
[win][arm64ec] Add '/machine:arm64ec' when linking LLVM as Arm64EC
dpaoliello Aug 8, 2025
339be84
Use new public libtest `ERROR_EXIT_CODE` constant in rustdoc
GuillaumeGomez Jul 31, 2025
2736d66
rename `TraitRef::from_method` to `from_assoc`
fee1-dead Aug 9, 2025
b5e2ba6
Stabilize feature
Kivooeo Aug 8, 2025
a5ddf5d
Override custom Cargo `build-dir` in bootstrap
Kobzol Aug 9, 2025
1676563
comments
Kivooeo Jul 24, 2025
ca1e464
Change days-threshold to 28 in [behind-upstream]
xizheyin Aug 9, 2025
71f6e53
some `derive_more` refactors
fee1-dead Aug 9, 2025
f4d3dd0
Apply suggestions from code review
jyn514 Aug 9, 2025
a59842e
improve "Documentation problem" issue template.
lolbinarycat Aug 8, 2025
c0a3e48
`{BTree,Hash}Map`: add "`Entry` API" section heading
ada4a Aug 9, 2025
06e4365
`HashMap`: also add "Usage with custom key types" heading
ada4a Aug 9, 2025
feed41c
Fix an unstable feature comment that wasn't a doc comment
joshtriplett Aug 9, 2025
a70a312
`suggest_borrow_generic_arg`: use the correct generic args
dianne Aug 10, 2025
652429a
Rollup merge of #141624 - jyn514:env-var-stubs, r=BoxyUwU
Zalathar Aug 10, 2025
e5b98a9
Rollup merge of #143093 - lqd:polonius-pre-alpha, r=jackh726
Zalathar Aug 10, 2025
5955f00
Rollup merge of #144402 - heiher:stabilize-loong32-asm, r=Amanieu
Zalathar Aug 10, 2025
62b406d
Rollup merge of #144403 - Kivooeo:issue4, r=jieyouxu
Zalathar Aug 10, 2025
3d6976c
Rollup merge of #144739 - GuillaumeGomez:rustdoc-test-cleanup, r=fmease
Zalathar Aug 10, 2025
4e9bf08
Rollup merge of #145089 - Kobzol:bootstrap-cmd-error, r=jieyouxu
Zalathar Aug 10, 2025
baa3585
Rollup merge of #145112 - dpaoliello:raw-dylib-link-ordinal, r=jieyouxu
Zalathar Aug 10, 2025
2e08291
Rollup merge of #145129 - dpaoliello:arm64eclink, r=wesleywiser
Zalathar Aug 10, 2025
7b3afa6
Rollup merge of #145130 - lolbinarycat:issue-template-docs-update, r=…
Zalathar Aug 10, 2025
c15c2f0
Rollup merge of #145135 - Kivooeo:stabilize-duration_constructors_lit…
Zalathar Aug 10, 2025
b81be36
Rollup merge of #145145 - fee1-dead-contrib:push-qnmpmtmtpkkr, r=jiey…
Zalathar Aug 10, 2025
b30fe4b
Rollup merge of #145147 - fee1-dead-contrib:push-mxxpmlpmzmsz, r=comp…
Zalathar Aug 10, 2025
53eab6b
Rollup merge of #145156 - Kobzol:cargo-build-dir, r=lqd,jieyouxu
Zalathar Aug 10, 2025
a4acb8a
Rollup merge of #145160 - xizheyin:behind-upstream, r=Urgau
Zalathar Aug 10, 2025
461009d
Rollup merge of #145162 - ada4a:hash_and_btree_map-add-entry-section,…
Zalathar Aug 10, 2025
94b344d
Rollup merge of #145187 - joshtriplett:fix-unstable-feature-comment, …
Zalathar Aug 10, 2025
934cb10
Rollup merge of #145191 - dianne:fix-borrow-suggestion-args, r=compil…
Zalathar Aug 10, 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
12 changes: 6 additions & 6 deletions .github/ISSUE_TEMPLATE/documentation.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: Documentation problem
description: Create a report for a documentation problem.
description: Report an issue with documentation content.
labels: ["A-docs"]
body:
- type: markdown
Expand All @@ -19,20 +19,20 @@ body:
- [The Rustonomicon](https://github.com/rust-lang/nomicon/issues)
- [The Embedded Book](https://github.com/rust-embedded/book/issues)
All other documentation issues should be filed here.
Or, if you find an issue related to rustdoc (e.g. doctest, rustdoc UI), please use the rustdoc issue template instead.
Or, if you find an issue related to rustdoc (e.g. doctest, rustdoc UI), please use the bug report or blank issue template instead.
All other documentation issues should be filed here.
- type: textarea
id: location
attributes:
label: Location
label: Location (URL)
validations:
required: true
required: true

- type: textarea
id: summary
attributes:
label: Summary
validations:
required: true
required: true
1 change: 1 addition & 0 deletions compiler/rustc_ast_lowering/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
| asm::InlineAsmArch::Arm64EC
| asm::InlineAsmArch::RiscV32
| asm::InlineAsmArch::RiscV64
| asm::InlineAsmArch::LoongArch32
| asm::InlineAsmArch::LoongArch64
| asm::InlineAsmArch::S390x
);
Expand Down
29 changes: 22 additions & 7 deletions compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -410,18 +410,18 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}
let typeck = self.infcx.tcx.typeck(self.mir_def_id());
let parent = self.infcx.tcx.parent_hir_node(expr.hir_id);
let (def_id, call_id, args, offset) = if let hir::Node::Expr(parent_expr) = parent
let (def_id, args, offset) = if let hir::Node::Expr(parent_expr) = parent
&& let hir::ExprKind::MethodCall(_, _, args, _) = parent_expr.kind
{
let def_id = typeck.type_dependent_def_id(parent_expr.hir_id);
(def_id, Some(parent_expr.hir_id), args, 1)
(def_id, args, 1)
} else if let hir::Node::Expr(parent_expr) = parent
&& let hir::ExprKind::Call(call, args) = parent_expr.kind
&& let ty::FnDef(def_id, _) = typeck.node_type(call.hir_id).kind()
{
(Some(*def_id), Some(call.hir_id), args, 0)
(Some(*def_id), args, 0)
} else {
(None, None, &[][..], 0)
(None, &[][..], 0)
};
let ty = place.ty(self.body, self.infcx.tcx).ty;

Expand Down Expand Up @@ -459,11 +459,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
// If the moved place is used generically by the callee and a reference to it
// would still satisfy any bounds on its type, suggest borrowing.
if let Some(&param) = arg_param
&& let Some(generic_args) = call_id.and_then(|id| typeck.node_args_opt(id))
&& let hir::Node::Expr(call_expr) = parent
&& let Some(ref_mutability) = self.suggest_borrow_generic_arg(
err,
typeck,
call_expr,
def_id,
generic_args,
param,
moved_place,
pos + offset,
Expand Down Expand Up @@ -627,8 +628,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
fn suggest_borrow_generic_arg(
&self,
err: &mut Diag<'_>,
typeck: &ty::TypeckResults<'tcx>,
call_expr: &hir::Expr<'tcx>,
callee_did: DefId,
generic_args: ty::GenericArgsRef<'tcx>,
param: ty::ParamTy,
moved_place: PlaceRef<'tcx>,
moved_arg_pos: usize,
Expand All @@ -639,6 +641,19 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
let sig = tcx.fn_sig(callee_did).instantiate_identity().skip_binder();
let clauses = tcx.predicates_of(callee_did);

let generic_args = match call_expr.kind {
// For method calls, generic arguments are attached to the call node.
hir::ExprKind::MethodCall(..) => typeck.node_args_opt(call_expr.hir_id)?,
// For normal calls, generic arguments are in the callee's type.
// This diagnostic is only run for `FnDef` callees.
hir::ExprKind::Call(callee, _)
if let &ty::FnDef(_, args) = typeck.node_type(callee.hir_id).kind() =>
{
args
}
_ => return None,
};

// First, is there at least one method on one of `param`'s trait bounds?
// This keeps us from suggesting borrowing the argument to `mem::drop`, e.g.
if !clauses.instantiate_identity(tcx).predicates.iter().any(|clause| {
Expand Down
4 changes: 1 addition & 3 deletions compiler/rustc_borrowck/src/polonius/constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ use rustc_mir_dataflow::points::PointIndex;
///
/// This models two sources of constraints:
/// - constraints that traverse the subsets between regions at a given point, `a@p: b@p`. These
/// depend on typeck constraints generated via assignments, calls, etc. (In practice there are
/// subtleties where a statement's effect only starts being visible at the successor point, via
/// the "result" of that statement).
/// depend on typeck constraints generated via assignments, calls, etc.
/// - constraints that traverse the CFG via the same region, `a@p: a@q`, where `p` is a predecessor
/// of `q`. These depend on the liveness of the regions at these points, as well as their
/// variance.
Expand Down
12 changes: 2 additions & 10 deletions compiler/rustc_borrowck/src/polonius/liveness_constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,22 +105,14 @@ fn propagate_loans_between_points(
});
}

let Some(current_live_regions) = live_regions.row(current_point) else {
// There are no constraints to add: there are no live regions at the current point.
return;
};
let Some(next_live_regions) = live_regions.row(next_point) else {
// There are no constraints to add: there are no live regions at the next point.
return;
};

for region in next_live_regions.iter() {
if !current_live_regions.contains(region) {
continue;
}

// `region` is indeed live at both points, add a constraint between them, according to
// variance.
// `region` could be live at the current point, and is live at the next point: add a
// constraint between them, according to variance.
if let Some(&direction) = live_region_variances.get(&region) {
add_liveness_constraint(
region,
Expand Down
191 changes: 22 additions & 169 deletions compiler/rustc_borrowck/src/polonius/loan_liveness.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,25 @@
use std::collections::{BTreeMap, BTreeSet};

use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::{
Body, Local, Location, Place, Rvalue, Statement, StatementKind, Terminator, TerminatorKind,
};
use rustc_middle::ty::{RegionVid, TyCtxt};
use rustc_middle::ty::RegionVid;
use rustc_mir_dataflow::points::PointIndex;

use super::{LiveLoans, LocalizedOutlivesConstraintSet};
use crate::BorrowSet;
use crate::constraints::OutlivesConstraint;
use crate::dataflow::BorrowIndex;
use crate::region_infer::values::LivenessValues;
use crate::type_check::Locations;
use crate::{BorrowSet, PlaceConflictBias, places_conflict};

/// Compute loan reachability, stop at kills, and trace loan liveness throughout the CFG, by
/// Compute loan reachability to approximately trace loan liveness throughout the CFG, by
/// traversing the full graph of constraints that combines:
/// - the localized constraints (the physical edges),
/// - with the constraints that hold at all points (the logical edges).
pub(super) fn compute_loan_liveness<'tcx>(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
liveness: &LivenessValues,
outlives_constraints: impl Iterator<Item = OutlivesConstraint<'tcx>>,
borrow_set: &BorrowSet<'tcx>,
localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
) -> LiveLoans {
let mut live_loans = LiveLoans::new(borrow_set.len());

// FIXME: it may be preferable for kills to be encoded in the edges themselves, to simplify and
// likely make traversal (and constraint generation) more efficient. We also display kills on
// edges when visualizing the constraint graph anyways.
let kills = collect_kills(body, tcx, borrow_set);

// Create the full graph with the physical edges we've localized earlier, and the logical edges
// of constraints that hold at all points.
let logical_constraints =
Expand All @@ -59,15 +45,15 @@ pub(super) fn compute_loan_liveness<'tcx>(
continue;
}

// Record the loan as being live on entry to this point.
live_loans.insert(node.point, loan_idx);

// Here, we have a conundrum. There's currently a weakness in our theory, in that
// we're using a single notion of reachability to represent what used to be _two_
// different transitive closures. It didn't seem impactful when coming up with the
// single-graph and reachability through space (regions) + time (CFG) concepts, but in
// practice the combination of time-traveling with kills is more impactful than
// initially anticipated.
// Record the loan as being live on entry to this point if it reaches a live region
// there.
//
// This is an approximation of liveness (which is the thing we want), in that we're
// using a single notion of reachability to represent what used to be _two_ different
// transitive closures. It didn't seem impactful when coming up with the single-graph
// and reachability through space (regions) + time (CFG) concepts, but in practice the
// combination of time-traveling with kills is more impactful than initially
// anticipated.
//
// Kills should prevent a loan from reaching its successor points in the CFG, but not
// while time-traveling: we're not actually at that CFG point, but looking for
Expand All @@ -92,40 +78,20 @@ pub(super) fn compute_loan_liveness<'tcx>(
// two-step traversal described above: only kills encountered on exit via a backward
// edge are ignored.
//
// In our test suite, there are a couple of cases where kills are encountered while
// time-traveling, however as far as we can tell, always in cases where they would be
// unreachable. We have reason to believe that this is a property of the single-graph
// approach (but haven't proved it yet):
// - reachable kills while time-traveling would also be encountered via regular
// traversal
// - it makes _some_ sense to ignore unreachable kills, but subtleties around dead code
// in general need to be better thought through (like they were for NLLs).
// - ignoring kills is a conservative approximation: the loan is still live and could
// cause false positive errors at another place access. Soundness issues in this
// domain should look more like the absence of reachability instead.
//
// This is enough in practice to pass tests, and therefore is what we have implemented
// for now.
// This version of the analysis, however, is enough in practice to pass the tests that
// we care about and NLLs reject, without regressions on crater, and is an actionable
// subset of the full analysis. It also naturally points to areas of improvement that we
// wish to explore later, namely handling kills appropriately during traversal, instead
// of continuing traversal to all the reachable nodes.
//
// FIXME: all of the above. Analyze potential unsoundness, possibly in concert with a
// borrowck implementation in a-mir-formality, fuzzing, or manually crafting
// counter-examples.
// FIXME: analyze potential unsoundness, possibly in concert with a borrowck
// implementation in a-mir-formality, fuzzing, or manually crafting counter-examples.

// Continuing traversal will depend on whether the loan is killed at this point, and
// whether we're time-traveling.
let current_location = liveness.location_from_point(node.point);
let is_loan_killed =
kills.get(&current_location).is_some_and(|kills| kills.contains(&loan_idx));
if liveness.is_live_at(node.region, liveness.location_from_point(node.point)) {
live_loans.insert(node.point, loan_idx);
}

for succ in graph.outgoing_edges(node) {
// If the loan is killed at this point, it is killed _on exit_. But only during
// forward traversal.
if is_loan_killed {
let destination = liveness.location_from_point(succ.point);
if current_location.is_predecessor_of(destination, body) {
continue;
}
}
stack.push(succ);
}
}
Expand Down Expand Up @@ -192,116 +158,3 @@ impl LocalizedConstraintGraph {
physical_edges.chain(materialized_edges)
}
}

/// Traverses the MIR and collects kills.
fn collect_kills<'tcx>(
body: &Body<'tcx>,
tcx: TyCtxt<'tcx>,
borrow_set: &BorrowSet<'tcx>,
) -> BTreeMap<Location, BTreeSet<BorrowIndex>> {
let mut collector = KillsCollector { borrow_set, tcx, body, kills: BTreeMap::default() };
for (block, data) in body.basic_blocks.iter_enumerated() {
collector.visit_basic_block_data(block, data);
}
collector.kills
}

struct KillsCollector<'a, 'tcx> {
body: &'a Body<'tcx>,
tcx: TyCtxt<'tcx>,
borrow_set: &'a BorrowSet<'tcx>,

/// The set of loans killed at each location.
kills: BTreeMap<Location, BTreeSet<BorrowIndex>>,
}

// This visitor has a similar structure to the `Borrows` dataflow computation with respect to kills,
// and the datalog polonius fact generation for the `loan_killed_at` relation.
impl<'tcx> KillsCollector<'_, 'tcx> {
/// Records the borrows on the specified place as `killed`. For example, when assigning to a
/// local, or on a call's return destination.
fn record_killed_borrows_for_place(&mut self, place: Place<'tcx>, location: Location) {
// For the reasons described in graph traversal, we also filter out kills
// unreachable from the loan's introduction point, as they would stop traversal when
// e.g. checking for reachability in the subset graph through invariance constraints
// higher up.
let filter_unreachable_kills = |loan| {
let introduction = self.borrow_set[loan].reserve_location;
let reachable = introduction.is_predecessor_of(location, self.body);
reachable
};

let other_borrows_of_local = self
.borrow_set
.local_map
.get(&place.local)
.into_iter()
.flat_map(|bs| bs.iter())
.copied();

// If the borrowed place is a local with no projections, all other borrows of this
// local must conflict. This is purely an optimization so we don't have to call
// `places_conflict` for every borrow.
if place.projection.is_empty() {
if !self.body.local_decls[place.local].is_ref_to_static() {
self.kills
.entry(location)
.or_default()
.extend(other_borrows_of_local.filter(|&loan| filter_unreachable_kills(loan)));
}
return;
}

// By passing `PlaceConflictBias::NoOverlap`, we conservatively assume that any given
// pair of array indices are not equal, so that when `places_conflict` returns true, we
// will be assured that two places being compared definitely denotes the same sets of
// locations.
let definitely_conflicting_borrows = other_borrows_of_local
.filter(|&i| {
places_conflict(
self.tcx,
self.body,
self.borrow_set[i].borrowed_place,
place,
PlaceConflictBias::NoOverlap,
)
})
.filter(|&loan| filter_unreachable_kills(loan));

self.kills.entry(location).or_default().extend(definitely_conflicting_borrows);
}

/// Records the borrows on the specified local as `killed`.
fn record_killed_borrows_for_local(&mut self, local: Local, location: Location) {
if let Some(borrow_indices) = self.borrow_set.local_map.get(&local) {
self.kills.entry(location).or_default().extend(borrow_indices.iter());
}
}
}

impl<'tcx> Visitor<'tcx> for KillsCollector<'_, 'tcx> {
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
// Make sure there are no remaining borrows for locals that have gone out of scope.
if let StatementKind::StorageDead(local) = statement.kind {
self.record_killed_borrows_for_local(local, location);
}

self.super_statement(statement, location);
}

fn visit_assign(&mut self, place: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) {
// When we see `X = ...`, then kill borrows of `(*X).foo` and so forth.
self.record_killed_borrows_for_place(*place, location);
self.super_assign(place, rvalue, location);
}

fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
// A `Call` terminator's return value can be a local which has borrows, so we need to record
// those as killed as well.
if let TerminatorKind::Call { destination, .. } = terminator.kind {
self.record_killed_borrows_for_place(destination, location);
}

self.super_terminator(terminator, location);
}
}
Loading
Loading