Skip to content

Commit deef8a8

Browse files
authored
Unrolled build for rust-lang#116960
Rollup merge of rust-lang#116960 - lqd:applied-member-constraints-scope, r=matthewjasper Location-insensitive polonius: consider a loan escaping if an SCC has member constraints applied only The location-insensitive analysis considered loans to escape if there were member constraints, which makes *some* sense for scopes and matches the scopes that NLL computes on all the tests. However, polonius and NLLs differ on the fuzzed case rust-lang#116657, where an SCC has member constraints but no applied ones (and is kinda surprising). The existing UI tests with member constraints impacting scopes all have some constraint applied. This PR changes the location-insensitive analysis to consider a loan to escape if there are applied member constraints, and for extra paranoia/insurance via fuzzing and crater: actually checks the constraint's min choice is indeed a universal region as we expect. (This could be turned into a `debug_assert` and early return as a slight optimization after these periods of verification) The 4 UI tests where member constraints are meaningful for computing scopes still pass obviously, and this also fixes rust-lang#116657. r? `@matthewjasper`
2 parents 858a42b + d9c213c commit deef8a8

File tree

6 files changed

+128
-25
lines changed

6 files changed

+128
-25
lines changed

compiler/rustc_borrowck/src/dataflow.rs

+14-7
Original file line numberDiff line numberDiff line change
@@ -272,21 +272,28 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
272272
loan_issued_at: Location,
273273
) {
274274
let sccs = self.regioncx.constraint_sccs();
275+
let universal_regions = self.regioncx.universal_regions();
275276
let issuing_region_scc = sccs.scc(issuing_region);
276277

277278
// We first handle the cases where the loan doesn't go out of scope, depending on the issuing
278279
// region's successors.
279280
for scc in sccs.depth_first_search(issuing_region_scc) {
280-
// 1. Via member constraints
281+
// 1. Via applied member constraints
281282
//
282283
// The issuing region can flow into the choice regions, and they are either:
283284
// - placeholders or free regions themselves,
284285
// - or also transitively outlive a free region.
285286
//
286-
// That is to say, if there are member constraints here, the loan escapes the function
287-
// and cannot go out of scope. We can early return.
288-
if self.regioncx.scc_has_member_constraints(scc) {
289-
return;
287+
// That is to say, if there are applied member constraints here, the loan escapes the
288+
// function and cannot go out of scope. We could early return here.
289+
//
290+
// For additional insurance via fuzzing and crater, we verify that the constraint's min
291+
// choice indeed escapes the function. In the future, we could e.g. turn this check into
292+
// a debug assert and early return as an optimization.
293+
for constraint in self.regioncx.applied_member_constraints(scc) {
294+
if universal_regions.is_universal_region(constraint.min_choice) {
295+
return;
296+
}
290297
}
291298

292299
// 2. Via regions that are live at all points: placeholders and free regions.
@@ -413,12 +420,12 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
413420
let mut polonius_prec = PoloniusOutOfScopePrecomputer::new(body, regioncx);
414421
for (loan_idx, loan_data) in borrow_set.iter_enumerated() {
415422
let issuing_region = loan_data.region;
416-
let issued_location = loan_data.reserve_location;
423+
let loan_issued_at = loan_data.reserve_location;
417424

418425
polonius_prec.precompute_loans_out_of_scope(
419426
loan_idx,
420427
issuing_region,
421-
issued_location,
428+
loan_issued_at,
422429
);
423430
}
424431

compiler/rustc_borrowck/src/region_infer/mod.rs

+7-11
Original file line numberDiff line numberDiff line change
@@ -644,11 +644,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
644644
self.scc_universes[scc]
645645
}
646646

647-
/// Once region solving has completed, this function will return
648-
/// the member constraints that were applied to the value of a given
649-
/// region `r`. See `AppliedMemberConstraint`.
650-
pub(crate) fn applied_member_constraints(&self, r: RegionVid) -> &[AppliedMemberConstraint] {
651-
let scc = self.constraint_sccs.scc(r);
647+
/// Once region solving has completed, this function will return the member constraints that
648+
/// were applied to the value of a given SCC `scc`. See `AppliedMemberConstraint`.
649+
pub(crate) fn applied_member_constraints(
650+
&self,
651+
scc: ConstraintSccIndex,
652+
) -> &[AppliedMemberConstraint] {
652653
binary_search_util::binary_search_slice(
653654
&self.member_constraints_applied,
654655
|applied| applied.member_region_scc,
@@ -1945,7 +1946,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
19451946
// Member constraints can also give rise to `'r: 'x` edges that
19461947
// were not part of the graph initially, so watch out for those.
19471948
// (But they are extremely rare; this loop is very cold.)
1948-
for constraint in self.applied_member_constraints(r) {
1949+
for constraint in self.applied_member_constraints(self.constraint_sccs.scc(r)) {
19491950
let p_c = &self.member_constraints[constraint.member_constraint_index];
19501951
let constraint = OutlivesConstraint {
19511952
sup: r,
@@ -2292,11 +2293,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
22922293
self.constraint_sccs.as_ref()
22932294
}
22942295

2295-
/// Returns whether the given SCC has any member constraints.
2296-
pub(crate) fn scc_has_member_constraints(&self, scc: ConstraintSccIndex) -> bool {
2297-
self.member_constraints.indices(scc).next().is_some()
2298-
}
2299-
23002296
/// Returns whether the given SCC is live at all points: whether the representative is a
23012297
/// placeholder or a free region.
23022298
pub(crate) fn scc_is_live_at_all_points(&self, scc: ConstraintSccIndex) -> bool {

compiler/rustc_session/src/config.rs

+2-7
Original file line numberDiff line numberDiff line change
@@ -3463,9 +3463,10 @@ impl DumpMonoStatsFormat {
34633463

34643464
/// `-Zpolonius` values, enabling the borrow checker polonius analysis, and which version: legacy,
34653465
/// or future prototype.
3466-
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
3466+
#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)]
34673467
pub enum Polonius {
34683468
/// The default value: disabled.
3469+
#[default]
34693470
Off,
34703471

34713472
/// Legacy version, using datalog and the `polonius-engine` crate. Historical value for `-Zpolonius`.
@@ -3475,12 +3476,6 @@ pub enum Polonius {
34753476
Next,
34763477
}
34773478

3478-
impl Default for Polonius {
3479-
fn default() -> Self {
3480-
Polonius::Off
3481-
}
3482-
}
3483-
34843479
impl Polonius {
34853480
/// Returns whether the legacy version of polonius is enabled
34863481
pub fn is_legacy_enabled(&self) -> bool {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
error[E0046]: not all trait items implemented, missing: `call`
2+
--> $DIR/location-insensitive-scopes-issue-116657.rs:18:1
3+
|
4+
LL | fn call(x: Self) -> Self::Output;
5+
| --------------------------------- `call` from trait
6+
...
7+
LL | impl<T: PlusOne> Callable for T {
8+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `call` in implementation
9+
10+
error: unconstrained opaque type
11+
--> $DIR/location-insensitive-scopes-issue-116657.rs:22:19
12+
|
13+
LL | type Output = impl PlusOne;
14+
| ^^^^^^^^^^^^
15+
|
16+
= note: `Output` must be used in combination with a concrete type within the same impl
17+
18+
error[E0700]: hidden type for `impl PlusOne` captures lifetime that does not appear in bounds
19+
--> $DIR/location-insensitive-scopes-issue-116657.rs:28:5
20+
|
21+
LL | fn test<'a>(y: &'a mut i32) -> impl PlusOne {
22+
| -- ------------ opaque type defined here
23+
| |
24+
| hidden type `<&'a mut i32 as Callable>::Output` captures the lifetime `'a` as defined here
25+
LL | <&mut i32 as Callable>::call(y)
26+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
27+
|
28+
help: to declare that `impl PlusOne` captures `'a`, you can add an explicit `'a` lifetime bound
29+
|
30+
LL | fn test<'a>(y: &'a mut i32) -> impl PlusOne + 'a {
31+
| ++++
32+
33+
error: aborting due to 3 previous errors
34+
35+
Some errors have detailed explanations: E0046, E0700.
36+
For more information about an error, try `rustc --explain E0046`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
error[E0046]: not all trait items implemented, missing: `call`
2+
--> $DIR/location-insensitive-scopes-issue-116657.rs:18:1
3+
|
4+
LL | fn call(x: Self) -> Self::Output;
5+
| --------------------------------- `call` from trait
6+
...
7+
LL | impl<T: PlusOne> Callable for T {
8+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `call` in implementation
9+
10+
error: unconstrained opaque type
11+
--> $DIR/location-insensitive-scopes-issue-116657.rs:22:19
12+
|
13+
LL | type Output = impl PlusOne;
14+
| ^^^^^^^^^^^^
15+
|
16+
= note: `Output` must be used in combination with a concrete type within the same impl
17+
18+
error[E0700]: hidden type for `impl PlusOne` captures lifetime that does not appear in bounds
19+
--> $DIR/location-insensitive-scopes-issue-116657.rs:28:5
20+
|
21+
LL | fn test<'a>(y: &'a mut i32) -> impl PlusOne {
22+
| -- ------------ opaque type defined here
23+
| |
24+
| hidden type `<&'a mut i32 as Callable>::Output` captures the lifetime `'a` as defined here
25+
LL | <&mut i32 as Callable>::call(y)
26+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
27+
|
28+
help: to declare that `impl PlusOne` captures `'a`, you can add an explicit `'a` lifetime bound
29+
|
30+
LL | fn test<'a>(y: &'a mut i32) -> impl PlusOne + 'a {
31+
| ++++
32+
33+
error: aborting due to 3 previous errors
34+
35+
Some errors have detailed explanations: E0046, E0700.
36+
For more information about an error, try `rustc --explain E0046`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// This is a non-regression test for issue #116657, where NLL and `-Zpolonius=next` computed
2+
// different loan scopes when a member constraint was not ultimately applied.
3+
4+
// revisions: nll polonius
5+
// [polonius] compile-flags: -Zpolonius=next
6+
7+
#![feature(impl_trait_in_assoc_type)]
8+
9+
trait Callable {
10+
type Output;
11+
fn call(x: Self) -> Self::Output;
12+
}
13+
14+
trait PlusOne {}
15+
16+
impl<'a> PlusOne for &'a mut i32 {}
17+
18+
impl<T: PlusOne> Callable for T {
19+
//[nll]~^ ERROR not all trait items implemented
20+
//[polonius]~^^ ERROR not all trait items implemented
21+
22+
type Output = impl PlusOne;
23+
//[nll]~^ ERROR unconstrained opaque type
24+
//[polonius]~^^ ERROR unconstrained opaque type
25+
}
26+
27+
fn test<'a>(y: &'a mut i32) -> impl PlusOne {
28+
<&mut i32 as Callable>::call(y)
29+
//[nll]~^ ERROR hidden type for `impl PlusOne` captures lifetime
30+
//[polonius]~^^ ERROR hidden type for `impl PlusOne` captures lifetime
31+
}
32+
33+
fn main() {}

0 commit comments

Comments
 (0)