Skip to content

Commit f5ad1f7

Browse files
committed
relax leak-check
1 parent e0ba2d0 commit f5ad1f7

15 files changed

+218
-159
lines changed

compiler/rustc_infer/src/infer/region_constraints/leak_check.rs

+19-82
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,9 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
2424
/// coherence and elsewhere -- see #56105 and #59490 for more details. The
2525
/// eventual fate of the leak checker is not yet settled.
2626
///
27-
/// The leak checker works by searching for the following error patterns:
27+
/// The leak checker works by searching for the following error pattern:
2828
///
29-
/// * P1: P2, where P1 != P2
30-
/// * P1: R, where R is in some universe that cannot name P1
29+
/// * P: R, where R is in some universe that cannot name P
3130
///
3231
/// The idea here is that each of these patterns represents something that
3332
/// the region solver would eventually report as an error, so we can detect
@@ -45,16 +44,16 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
4544
/// For each SCC S, we compute:
4645
///
4746
/// * what placeholder P it must be equal to, if any
48-
/// * if there are multiple placeholders that must be equal, report an error because `P1: P2`
47+
/// * if there are multiple placeholders that must be equal, we pick the one with the higher
48+
/// universe. It will eventually be an error in the next step if the placeholders are in
49+
/// different universes.
4950
/// * the minimum universe of its constituents
5051
///
5152
/// Then we walk the SCCs in dependency order and compute
5253
///
53-
/// * what placeholder they must outlive transitively
54-
/// * if they must also be equal to a placeholder, report an error because `P1: P2`
5554
/// * minimum universe U of all SCCs they must outlive
56-
/// * if they must also be equal to a placeholder P, and U cannot name P, report an error, as that
57-
/// indicates `P: R` and `R` is in an incompatible universe
55+
/// * if the SCC must also be equal to a placeholder P, and U cannot name P, report an error,
56+
/// as that indicates `P: R` and `R` is in a universe that cannot name P
5857
///
5958
/// To improve performance and for the old trait solver caching to be sound, this takes
6059
/// an optional snapshot in which case we only look at region constraints added in that
@@ -69,6 +68,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
6968
///
7069
/// * R: P1, even if R cannot name P1, because R = 'static is a valid sol'n
7170
/// * R: P1, R: P2, as above
71+
/// * P1: P2, when P2 lives in a universe that *can* name P1.
7272
#[instrument(level = "debug", skip(self, tcx, only_consider_snapshot), ret)]
7373
pub fn leak_check(
7474
&mut self,
@@ -83,26 +83,18 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
8383

8484
let mini_graph = &MiniGraph::new(tcx, &self, only_consider_snapshot);
8585

86-
let mut leak_check = LeakCheck::new(tcx, outer_universe, max_universe, mini_graph, self);
87-
leak_check.assign_placeholder_values()?;
88-
leak_check.propagate_scc_value()?;
89-
Ok(())
86+
let mut leak_check = LeakCheck::new(mini_graph, self);
87+
leak_check.assign_placeholder_values();
88+
leak_check.propagate_scc_value()
9089
}
9190
}
9291

9392
struct LeakCheck<'me, 'tcx> {
94-
tcx: TyCtxt<'tcx>,
95-
outer_universe: ty::UniverseIndex,
9693
mini_graph: &'me MiniGraph<'tcx>,
9794
rcc: &'me RegionConstraintCollector<'me, 'tcx>,
9895

9996
// Initially, for each SCC S, stores a placeholder `P` such that `S = P`
10097
// must hold.
101-
//
102-
// Later, during the [`LeakCheck::propagate_scc_value`] function, this array
103-
// is repurposed to store some placeholder `P` such that the weaker
104-
// condition `S: P` must hold. (This is true if `S: S1` transitively and `S1
105-
// = P`.)
10698
scc_placeholders: IndexVec<LeakCheckScc, Option<ty::PlaceholderRegion>>,
10799

108100
// For each SCC S, track the minimum universe that flows into it. Note that
@@ -119,16 +111,11 @@ struct LeakCheck<'me, 'tcx> {
119111

120112
impl<'me, 'tcx> LeakCheck<'me, 'tcx> {
121113
fn new(
122-
tcx: TyCtxt<'tcx>,
123-
outer_universe: ty::UniverseIndex,
124-
max_universe: ty::UniverseIndex,
125114
mini_graph: &'me MiniGraph<'tcx>,
126115
rcc: &'me RegionConstraintCollector<'me, 'tcx>,
127116
) -> Self {
128-
let dummy_scc_universe = SccUniverse { universe: max_universe, region: None };
117+
let dummy_scc_universe = SccUniverse { universe: ty::UniverseIndex::MAX, region: None };
129118
Self {
130-
tcx,
131-
outer_universe,
132119
mini_graph,
133120
rcc,
134121
scc_placeholders: IndexVec::from_elem_n(None, mini_graph.sccs.num_sccs()),
@@ -138,7 +125,7 @@ impl<'me, 'tcx> LeakCheck<'me, 'tcx> {
138125

139126
/// Compute what placeholders (if any) each SCC must be equal to.
140127
/// Also compute the minimum universe of all the regions in each SCC.
141-
fn assign_placeholder_values(&mut self) -> RelateResult<'tcx, ()> {
128+
fn assign_placeholder_values(&mut self) {
142129
// First walk: find each placeholder that is from a newly created universe.
143130
for (region, leak_check_node) in &self.mini_graph.nodes {
144131
let scc = self.mini_graph.sccs.scc(*leak_check_node);
@@ -152,34 +139,14 @@ impl<'me, 'tcx> LeakCheck<'me, 'tcx> {
152139
self.scc_universes[scc].take_min(universe, *region);
153140

154141
// Detect those SCCs that directly contain a placeholder
155-
if let ty::RePlaceholder(placeholder) = **region {
156-
if self.outer_universe.cannot_name(placeholder.universe) {
157-
self.assign_scc_value(scc, placeholder)?;
158-
}
142+
if let ty::RePlaceholder(p1) = **region {
143+
let max_placeholder = match self.scc_placeholders[scc] {
144+
Some(p2) => std::cmp::max_by_key(p1, p2, |p| p.universe),
145+
None => p1,
146+
};
147+
self.scc_placeholders[scc] = Some(max_placeholder);
159148
}
160149
}
161-
162-
Ok(())
163-
}
164-
165-
// assign_scc_value(S, P): Update `scc_values` to account for the fact that `P: S` must hold.
166-
// This may create an error.
167-
fn assign_scc_value(
168-
&mut self,
169-
scc: LeakCheckScc,
170-
placeholder: ty::PlaceholderRegion,
171-
) -> RelateResult<'tcx, ()> {
172-
match self.scc_placeholders[scc] {
173-
Some(p) => {
174-
assert_ne!(p, placeholder);
175-
return Err(self.placeholder_error(p, placeholder));
176-
}
177-
None => {
178-
self.scc_placeholders[scc] = Some(placeholder);
179-
}
180-
};
181-
182-
Ok(())
183150
}
184151

185152
/// For each SCC S, iterate over each successor S1 where `S: S1`:
@@ -201,8 +168,6 @@ impl<'me, 'tcx> LeakCheck<'me, 'tcx> {
201168
//
202169
// For each successor `scc2` where `scc1: scc2`:
203170
//
204-
// * `scc_placeholder[scc2]` stores some placeholder `P` where
205-
// `scc2: P` (if any)
206171
// * `scc_universes[scc2]` contains the minimum universe of the
207172
// constituents of `scc2` and any of its successors
208173
for scc1 in self.mini_graph.sccs.all_sccs() {
@@ -214,19 +179,12 @@ impl<'me, 'tcx> LeakCheck<'me, 'tcx> {
214179
// Walk over each `scc2` such that `scc1: scc2` and compute:
215180
//
216181
// * `scc1_universe`: the minimum universe of `scc2` and the constituents of `scc1`
217-
// * `succ_bound`: placeholder `P` that the successors must outlive, if any (if there are multiple,
218-
// we pick one arbitrarily)
219182
let mut scc1_universe = self.scc_universes[scc1];
220-
let mut succ_bound = None;
221183
for &scc2 in self.mini_graph.sccs.successors(scc1) {
222184
let SccUniverse { universe: scc2_universe, region: scc2_region } =
223185
self.scc_universes[scc2];
224186

225187
scc1_universe.take_min(scc2_universe, scc2_region.unwrap());
226-
227-
if let Some(b) = self.scc_placeholders[scc2] {
228-
succ_bound = Some(b);
229-
}
230188
}
231189

232190
// Update minimum universe of scc1.
@@ -245,32 +203,11 @@ impl<'me, 'tcx> LeakCheck<'me, 'tcx> {
245203
if scc1_universe.universe.cannot_name(scc1_placeholder.universe) {
246204
return Err(self.error(scc1_placeholder, scc1_universe.region.unwrap()));
247205
}
248-
249-
// Check if we have some placeholder where `S: P2`
250-
// (transitively). In that case, since `S = P1`, that implies
251-
// `P1: P2`, which is an error condition.
252-
if let Some(scc2_placeholder) = succ_bound {
253-
assert_ne!(scc1_placeholder, scc2_placeholder);
254-
return Err(self.placeholder_error(scc1_placeholder, scc2_placeholder));
255-
}
256-
} else {
257-
// Otherwise, we can reach a placeholder if some successor can.
258-
self.scc_placeholders[scc1] = succ_bound;
259206
}
260-
261-
// At this point, `scc_placeholder[scc1]` stores some placeholder that `scc1` must outlive (if any).
262207
}
263208
Ok(())
264209
}
265210

266-
fn placeholder_error(
267-
&self,
268-
placeholder1: ty::PlaceholderRegion,
269-
placeholder2: ty::PlaceholderRegion,
270-
) -> TypeError<'tcx> {
271-
self.error(placeholder1, ty::Region::new_placeholder(self.tcx, placeholder2))
272-
}
273-
274211
fn error(
275212
&self,
276213
placeholder: ty::PlaceholderRegion,
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,9 @@
1-
// Test that our leak-check is not smart enough to take implied bounds
2-
// into account (yet). Here we have two types that look like they
3-
// should not be equivalent, but because of the rules on implied
4-
// bounds we ought to know that, in fact, `'a = 'b` must always hold,
5-
// and hence they are.
6-
//
7-
// Rustc can't figure this out and hence it accepts the impls but
8-
// gives a future-compatibility warning (because we'd like to make
9-
// this an error someday).
10-
//
11-
// Note that while we would like to make this a hard error, we also
12-
// give the same warning for `coherence-wasm-bindgen.rs`, which ought
13-
// to be accepted.
14-
15-
#![deny(coherence_leak_check)]
16-
171
trait Trait {}
182

193
impl Trait for for<'a, 'b> fn(&'a &'b u32, &'b &'a u32) -> &'b u32 {}
204

215
impl Trait for for<'c> fn(&'c &'c u32, &'c &'c u32) -> &'c u32 {
226
//~^ ERROR conflicting implementations
23-
//~| WARNING this was previously accepted by the compiler
247
}
258

269
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,14 @@
1-
error: conflicting implementations of trait `Trait` for type `for<'a, 'b> fn(&'a &'b u32, &'b &'a u32) -> &'b u32`
2-
--> $DIR/coherence-fn-implied-bounds.rs:21:1
1+
error[E0119]: conflicting implementations of trait `Trait` for type `for<'a, 'b> fn(&'a &'b u32, &'b &'a u32) -> &'b u32`
2+
--> $DIR/coherence-fn-implied-bounds.rs:5:1
33
|
44
LL | impl Trait for for<'a, 'b> fn(&'a &'b u32, &'b &'a u32) -> &'b u32 {}
55
| ------------------------------------------------------------------ first implementation here
66
LL |
77
LL | impl Trait for for<'c> fn(&'c &'c u32, &'c &'c u32) -> &'c u32 {
88
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `for<'a, 'b> fn(&'a &'b u32, &'b &'a u32) -> &'b u32`
99
|
10-
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
11-
= note: for more information, see issue #56105 <https://github.com/rust-lang/rust/issues/56105>
1210
= note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details
13-
note: the lint level is defined here
14-
--> $DIR/coherence-fn-implied-bounds.rs:15:9
15-
|
16-
LL | #![deny(coherence_leak_check)]
17-
| ^^^^^^^^^^^^^^^^^^^^
1811

1912
error: aborting due to previous error
2013

14+
For more information about this error, try `rustc --explain E0119`.
+1-7
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
// Test that two distinct impls which match subtypes of one another
22
// yield coherence errors (or not) depending on the variance.
3-
//
4-
// Note: This scenario is currently accepted, but as part of the
5-
// universe transition (#56105) may eventually become an error.
6-
7-
// check-pass
83

94
trait TheTrait {
105
fn foo(&self) {}
@@ -13,8 +8,7 @@ trait TheTrait {
138
impl TheTrait for for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8 {}
149

1510
impl TheTrait for for<'a> fn(&'a u8, &'a u8) -> &'a u8 {
16-
//~^ WARNING conflicting implementation
17-
//~^^ WARNING this was previously accepted by the compiler but is being phased out
11+
//~^ ERROR conflicting implementation
1812
}
1913

2014
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
1-
warning: conflicting implementations of trait `TheTrait` for type `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8`
2-
--> $DIR/coherence-subtyping.rs:15:1
1+
error[E0119]: conflicting implementations of trait `TheTrait` for type `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8`
2+
--> $DIR/coherence-subtyping.rs:10:1
33
|
44
LL | impl TheTrait for for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8 {}
55
| ---------------------------------------------------------- first implementation here
66
LL |
77
LL | impl TheTrait for for<'a> fn(&'a u8, &'a u8) -> &'a u8 {
88
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8`
99
|
10-
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
11-
= note: for more information, see issue #56105 <https://github.com/rust-lang/rust/issues/56105>
1210
= note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details
13-
= note: `#[warn(coherence_leak_check)]` on by default
1411

15-
warning: 1 warning emitted
12+
error: aborting due to previous error
1613

14+
For more information about this error, try `rustc --explain E0119`.

tests/ui/coherence/leak-check.rs

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// compile-flags: -Ztrait-solver=next
2+
trait Relate {}
3+
4+
struct Outlives<'a, 'b>(&'a u8, &'b u8);
5+
impl<'a, 'b> Relate for Outlives<'a, 'b> where 'a: 'b, {}
6+
7+
struct Equal<'a, 'b>(&'a u8, &'b u8);
8+
impl<'a> Relate for Equal<'a, 'a> {}
9+
10+
macro_rules! rule {
11+
( $name:ident<$($lt:lifetime),*> :- $( ($($bound:tt)*) ),* ) => {
12+
struct $name<$($lt),*>($(&$lt u8),*);
13+
impl<$($lt),*> $crate::Relate for $name<$($lt),*>
14+
where $( $($bound)*: $crate::Relate, )*
15+
{}
16+
};
17+
}
18+
19+
// ----
20+
21+
trait CoherenceTrait {}
22+
impl<T> CoherenceTrait for T {}
23+
24+
macro_rules! assert_false_by_leak_check {
25+
( exist<$($lt:lifetime),*> $( ($($bound:tt)*) ),* ) => {
26+
struct Unique<$($lt),*>($(&$lt u8),*);
27+
impl<$($lt),*> $crate::CoherenceTrait for Unique<$($lt),*>
28+
//~^ ERROR for type `test1::Unique`
29+
//~| ERROR for type `test3::Unique`
30+
//~| ERROR for type `test6::Unique`
31+
where $( $($bound)*: $crate::Relate, )*
32+
{}
33+
};
34+
}
35+
36+
mod test1 {
37+
use super::*;
38+
assert_false_by_leak_check!(
39+
exist<> (for<'a, 'b> Outlives<'a, 'b>)
40+
);
41+
}
42+
43+
mod test2 {
44+
use super::*;
45+
assert_false_by_leak_check!(
46+
exist<'a> (for<'b> Outlives<'b, 'a>)
47+
);
48+
}
49+
50+
mod test3 {
51+
use super::*;
52+
rule!( OutlivesPlaceholder<'a> :- (for<'b> Outlives<'a, 'b>) );
53+
assert_false_by_leak_check!(
54+
exist<> (for<'a> OutlivesPlaceholder<'a>)
55+
);
56+
}
57+
58+
mod test4 {
59+
use super::*;
60+
rule!( OutlivedByPlaceholder<'a> :- (for<'b> Outlives<'b, 'a>) );
61+
assert_false_by_leak_check!(
62+
exist<> (for<'a> OutlivedByPlaceholder<'a>)
63+
);
64+
}
65+
66+
mod test5 {
67+
use super::*;
68+
rule!( EqualsPlaceholder<'a> :- (for<'b> Equal<'b, 'a>) );
69+
assert_false_by_leak_check!(
70+
exist<> (for<'a> EqualsPlaceholder<'a>)
71+
);
72+
}
73+
74+
mod test6 {
75+
use super::*;
76+
assert_false_by_leak_check!(
77+
exist<> (for<'a, 'b> Equal<'a, 'b>)
78+
);
79+
}
80+
81+
fn main() {}

0 commit comments

Comments
 (0)