Skip to content

Commit b6f3e72

Browse files
committed
Auto merge of #145204 - Zalathar:rollup-eyrrf5y, r=Zalathar
Rollup of 18 pull requests Successful merges: - #141624 (unstable-book: Add stubs for environment variables; document some of the important ones) - #143093 (Simplify polonius location-sensitive analysis) - #144402 (Stabilize loongarch32 inline asm) - #144403 (`tests/ui/issues/`: The Issues Strike Back [4/N]) - #144739 (Use new public libtest `ERROR_EXIT_CODE` constant in rustdoc) - #145089 (Improve error output when a command fails in bootstrap) - #145112 ([win][arm64ec] Partial fix for raw-dylib-link-ordinal on Arm64EC) - #145129 ([win][arm64ec] Add `/machine:arm64ec` when linking LLVM as Arm64EC) - #145130 (improve "Documentation problem" issue template.) - #145135 (Stabilize `duration_constructors_lite` feature) - #145145 (some `derive_more` refactors) - #145147 (rename `TraitRef::from_method` to `from_assoc`) - #145156 (Override custom Cargo `build-dir` in bootstrap) - #145160 (Change days-threshold to 28 in [behind-upstream]) - #145162 (`{BTree,Hash}Map`: add "`Entry` API" section heading) - #145175 (Enable limit_rdylib_exports on Solaris) - #145187 (Fix an unstable feature comment that wasn't a doc comment) - #145191 (`suggest_borrow_generic_arg`: use the correct generic args) r? `@ghost` `@rustbot` modify labels: rollup
2 parents 8712e45 + 7e3c18d commit b6f3e72

File tree

144 files changed

+1412
-562
lines changed

Some content is hidden

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

144 files changed

+1412
-562
lines changed

.github/ISSUE_TEMPLATE/documentation.yaml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: Documentation problem
2-
description: Create a report for a documentation problem.
2+
description: Report an issue with documentation content.
33
labels: ["A-docs"]
44
body:
55
- type: markdown
@@ -19,20 +19,20 @@ body:
1919
- [The Rustonomicon](https://github.com/rust-lang/nomicon/issues)
2020
- [The Embedded Book](https://github.com/rust-embedded/book/issues)
2121
22-
All other documentation issues should be filed here.
22+
Or, if you find an issue related to rustdoc (e.g. doctest, rustdoc UI), please use the rustdoc issue template instead.
2323
24-
Or, if you find an issue related to rustdoc (e.g. doctest, rustdoc UI), please use the bug report or blank issue template instead.
24+
All other documentation issues should be filed here.
2525
2626
- type: textarea
2727
id: location
2828
attributes:
29-
label: Location
29+
label: Location (URL)
3030
validations:
31-
required: true
31+
required: true
3232

3333
- type: textarea
3434
id: summary
3535
attributes:
3636
label: Summary
3737
validations:
38-
required: true
38+
required: true

compiler/rustc_ast_lowering/src/asm.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
4848
| asm::InlineAsmArch::Arm64EC
4949
| asm::InlineAsmArch::RiscV32
5050
| asm::InlineAsmArch::RiscV64
51+
| asm::InlineAsmArch::LoongArch32
5152
| asm::InlineAsmArch::LoongArch64
5253
| asm::InlineAsmArch::S390x
5354
);

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -410,18 +410,18 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
410410
}
411411
let typeck = self.infcx.tcx.typeck(self.mir_def_id());
412412
let parent = self.infcx.tcx.parent_hir_node(expr.hir_id);
413-
let (def_id, call_id, args, offset) = if let hir::Node::Expr(parent_expr) = parent
413+
let (def_id, args, offset) = if let hir::Node::Expr(parent_expr) = parent
414414
&& let hir::ExprKind::MethodCall(_, _, args, _) = parent_expr.kind
415415
{
416416
let def_id = typeck.type_dependent_def_id(parent_expr.hir_id);
417-
(def_id, Some(parent_expr.hir_id), args, 1)
417+
(def_id, args, 1)
418418
} else if let hir::Node::Expr(parent_expr) = parent
419419
&& let hir::ExprKind::Call(call, args) = parent_expr.kind
420420
&& let ty::FnDef(def_id, _) = typeck.node_type(call.hir_id).kind()
421421
{
422-
(Some(*def_id), Some(call.hir_id), args, 0)
422+
(Some(*def_id), args, 0)
423423
} else {
424-
(None, None, &[][..], 0)
424+
(None, &[][..], 0)
425425
};
426426
let ty = place.ty(self.body, self.infcx.tcx).ty;
427427

@@ -459,11 +459,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
459459
// If the moved place is used generically by the callee and a reference to it
460460
// would still satisfy any bounds on its type, suggest borrowing.
461461
if let Some(&param) = arg_param
462-
&& let Some(generic_args) = call_id.and_then(|id| typeck.node_args_opt(id))
462+
&& let hir::Node::Expr(call_expr) = parent
463463
&& let Some(ref_mutability) = self.suggest_borrow_generic_arg(
464464
err,
465+
typeck,
466+
call_expr,
465467
def_id,
466-
generic_args,
467468
param,
468469
moved_place,
469470
pos + offset,
@@ -627,8 +628,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
627628
fn suggest_borrow_generic_arg(
628629
&self,
629630
err: &mut Diag<'_>,
631+
typeck: &ty::TypeckResults<'tcx>,
632+
call_expr: &hir::Expr<'tcx>,
630633
callee_did: DefId,
631-
generic_args: ty::GenericArgsRef<'tcx>,
632634
param: ty::ParamTy,
633635
moved_place: PlaceRef<'tcx>,
634636
moved_arg_pos: usize,
@@ -639,6 +641,19 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
639641
let sig = tcx.fn_sig(callee_did).instantiate_identity().skip_binder();
640642
let clauses = tcx.predicates_of(callee_did);
641643

644+
let generic_args = match call_expr.kind {
645+
// For method calls, generic arguments are attached to the call node.
646+
hir::ExprKind::MethodCall(..) => typeck.node_args_opt(call_expr.hir_id)?,
647+
// For normal calls, generic arguments are in the callee's type.
648+
// This diagnostic is only run for `FnDef` callees.
649+
hir::ExprKind::Call(callee, _)
650+
if let &ty::FnDef(_, args) = typeck.node_type(callee.hir_id).kind() =>
651+
{
652+
args
653+
}
654+
_ => return None,
655+
};
656+
642657
// First, is there at least one method on one of `param`'s trait bounds?
643658
// This keeps us from suggesting borrowing the argument to `mem::drop`, e.g.
644659
if !clauses.instantiate_identity(tcx).predicates.iter().any(|clause| {

compiler/rustc_borrowck/src/polonius/constraints.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ use rustc_mir_dataflow::points::PointIndex;
77
///
88
/// This models two sources of constraints:
99
/// - constraints that traverse the subsets between regions at a given point, `a@p: b@p`. These
10-
/// depend on typeck constraints generated via assignments, calls, etc. (In practice there are
11-
/// subtleties where a statement's effect only starts being visible at the successor point, via
12-
/// the "result" of that statement).
10+
/// depend on typeck constraints generated via assignments, calls, etc.
1311
/// - constraints that traverse the CFG via the same region, `a@p: a@q`, where `p` is a predecessor
1412
/// of `q`. These depend on the liveness of the regions at these points, as well as their
1513
/// variance.

compiler/rustc_borrowck/src/polonius/liveness_constraints.rs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -105,22 +105,14 @@ fn propagate_loans_between_points(
105105
});
106106
}
107107

108-
let Some(current_live_regions) = live_regions.row(current_point) else {
109-
// There are no constraints to add: there are no live regions at the current point.
110-
return;
111-
};
112108
let Some(next_live_regions) = live_regions.row(next_point) else {
113109
// There are no constraints to add: there are no live regions at the next point.
114110
return;
115111
};
116112

117113
for region in next_live_regions.iter() {
118-
if !current_live_regions.contains(region) {
119-
continue;
120-
}
121-
122-
// `region` is indeed live at both points, add a constraint between them, according to
123-
// variance.
114+
// `region` could be live at the current point, and is live at the next point: add a
115+
// constraint between them, according to variance.
124116
if let Some(&direction) = live_region_variances.get(&region) {
125117
add_liveness_constraint(
126118
region,
Lines changed: 22 additions & 169 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,25 @@
1-
use std::collections::{BTreeMap, BTreeSet};
2-
31
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
4-
use rustc_middle::mir::visit::Visitor;
5-
use rustc_middle::mir::{
6-
Body, Local, Location, Place, Rvalue, Statement, StatementKind, Terminator, TerminatorKind,
7-
};
8-
use rustc_middle::ty::{RegionVid, TyCtxt};
2+
use rustc_middle::ty::RegionVid;
93
use rustc_mir_dataflow::points::PointIndex;
104

115
use super::{LiveLoans, LocalizedOutlivesConstraintSet};
6+
use crate::BorrowSet;
127
use crate::constraints::OutlivesConstraint;
13-
use crate::dataflow::BorrowIndex;
148
use crate::region_infer::values::LivenessValues;
159
use crate::type_check::Locations;
16-
use crate::{BorrowSet, PlaceConflictBias, places_conflict};
1710

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

32-
// FIXME: it may be preferable for kills to be encoded in the edges themselves, to simplify and
33-
// likely make traversal (and constraint generation) more efficient. We also display kills on
34-
// edges when visualizing the constraint graph anyways.
35-
let kills = collect_kills(body, tcx, borrow_set);
36-
3723
// Create the full graph with the physical edges we've localized earlier, and the logical edges
3824
// of constraints that hold at all points.
3925
let logical_constraints =
@@ -59,15 +45,15 @@ pub(super) fn compute_loan_liveness<'tcx>(
5945
continue;
6046
}
6147

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

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

12094
for succ in graph.outgoing_edges(node) {
121-
// If the loan is killed at this point, it is killed _on exit_. But only during
122-
// forward traversal.
123-
if is_loan_killed {
124-
let destination = liveness.location_from_point(succ.point);
125-
if current_location.is_predecessor_of(destination, body) {
126-
continue;
127-
}
128-
}
12995
stack.push(succ);
13096
}
13197
}
@@ -192,116 +158,3 @@ impl LocalizedConstraintGraph {
192158
physical_edges.chain(materialized_edges)
193159
}
194160
}
195-
196-
/// Traverses the MIR and collects kills.
197-
fn collect_kills<'tcx>(
198-
body: &Body<'tcx>,
199-
tcx: TyCtxt<'tcx>,
200-
borrow_set: &BorrowSet<'tcx>,
201-
) -> BTreeMap<Location, BTreeSet<BorrowIndex>> {
202-
let mut collector = KillsCollector { borrow_set, tcx, body, kills: BTreeMap::default() };
203-
for (block, data) in body.basic_blocks.iter_enumerated() {
204-
collector.visit_basic_block_data(block, data);
205-
}
206-
collector.kills
207-
}
208-
209-
struct KillsCollector<'a, 'tcx> {
210-
body: &'a Body<'tcx>,
211-
tcx: TyCtxt<'tcx>,
212-
borrow_set: &'a BorrowSet<'tcx>,
213-
214-
/// The set of loans killed at each location.
215-
kills: BTreeMap<Location, BTreeSet<BorrowIndex>>,
216-
}
217-
218-
// This visitor has a similar structure to the `Borrows` dataflow computation with respect to kills,
219-
// and the datalog polonius fact generation for the `loan_killed_at` relation.
220-
impl<'tcx> KillsCollector<'_, 'tcx> {
221-
/// Records the borrows on the specified place as `killed`. For example, when assigning to a
222-
/// local, or on a call's return destination.
223-
fn record_killed_borrows_for_place(&mut self, place: Place<'tcx>, location: Location) {
224-
// For the reasons described in graph traversal, we also filter out kills
225-
// unreachable from the loan's introduction point, as they would stop traversal when
226-
// e.g. checking for reachability in the subset graph through invariance constraints
227-
// higher up.
228-
let filter_unreachable_kills = |loan| {
229-
let introduction = self.borrow_set[loan].reserve_location;
230-
let reachable = introduction.is_predecessor_of(location, self.body);
231-
reachable
232-
};
233-
234-
let other_borrows_of_local = self
235-
.borrow_set
236-
.local_map
237-
.get(&place.local)
238-
.into_iter()
239-
.flat_map(|bs| bs.iter())
240-
.copied();
241-
242-
// If the borrowed place is a local with no projections, all other borrows of this
243-
// local must conflict. This is purely an optimization so we don't have to call
244-
// `places_conflict` for every borrow.
245-
if place.projection.is_empty() {
246-
if !self.body.local_decls[place.local].is_ref_to_static() {
247-
self.kills
248-
.entry(location)
249-
.or_default()
250-
.extend(other_borrows_of_local.filter(|&loan| filter_unreachable_kills(loan)));
251-
}
252-
return;
253-
}
254-
255-
// By passing `PlaceConflictBias::NoOverlap`, we conservatively assume that any given
256-
// pair of array indices are not equal, so that when `places_conflict` returns true, we
257-
// will be assured that two places being compared definitely denotes the same sets of
258-
// locations.
259-
let definitely_conflicting_borrows = other_borrows_of_local
260-
.filter(|&i| {
261-
places_conflict(
262-
self.tcx,
263-
self.body,
264-
self.borrow_set[i].borrowed_place,
265-
place,
266-
PlaceConflictBias::NoOverlap,
267-
)
268-
})
269-
.filter(|&loan| filter_unreachable_kills(loan));
270-
271-
self.kills.entry(location).or_default().extend(definitely_conflicting_borrows);
272-
}
273-
274-
/// Records the borrows on the specified local as `killed`.
275-
fn record_killed_borrows_for_local(&mut self, local: Local, location: Location) {
276-
if let Some(borrow_indices) = self.borrow_set.local_map.get(&local) {
277-
self.kills.entry(location).or_default().extend(borrow_indices.iter());
278-
}
279-
}
280-
}
281-
282-
impl<'tcx> Visitor<'tcx> for KillsCollector<'_, 'tcx> {
283-
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
284-
// Make sure there are no remaining borrows for locals that have gone out of scope.
285-
if let StatementKind::StorageDead(local) = statement.kind {
286-
self.record_killed_borrows_for_local(local, location);
287-
}
288-
289-
self.super_statement(statement, location);
290-
}
291-
292-
fn visit_assign(&mut self, place: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) {
293-
// When we see `X = ...`, then kill borrows of `(*X).foo` and so forth.
294-
self.record_killed_borrows_for_place(*place, location);
295-
self.super_assign(place, rvalue, location);
296-
}
297-
298-
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
299-
// A `Call` terminator's return value can be a local which has borrows, so we need to record
300-
// those as killed as well.
301-
if let TerminatorKind::Call { destination, .. } = terminator.kind {
302-
self.record_killed_borrows_for_place(destination, location);
303-
}
304-
305-
self.super_terminator(terminator, location);
306-
}
307-
}

0 commit comments

Comments
 (0)