Skip to content

Commit 301d91b

Browse files
authored
Rollup merge of rust-lang#125413 - lcnr:ambig-drop-region-constraints, r=compiler-errors
drop region constraints for ambiguous goals See the comment in `compute_external_query_constraints`. While the underlying issue is preexisting, this fixes a bug introduced by rust-lang#125343. It slightly weakens the leak chec, even if we didn't have any test which was affected. I want to write such a test before merging this PR. r? `@compiler-errors`
2 parents a0b7e92 + 24b5466 commit 301d91b

File tree

6 files changed

+172
-25
lines changed

6 files changed

+172
-25
lines changed

compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs

+39-25
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,13 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
9999
previous call to `try_evaluate_added_goals!`"
100100
);
101101

102+
// We only check for leaks from universes which were entered inside
103+
// of the query.
104+
self.infcx.leak_check(self.max_input_universe, None).map_err(|e| {
105+
trace!(?e, "failed the leak check");
106+
NoSolution
107+
})?;
108+
102109
// When normalizing, we've replaced the expected term with an unconstrained
103110
// inference variable. This means that we dropped information which could
104111
// have been important. We handle this by instead returning the nested goals
@@ -121,7 +128,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
121128
};
122129

123130
let external_constraints =
124-
self.compute_external_query_constraints(normalization_nested_goals)?;
131+
self.compute_external_query_constraints(certainty, normalization_nested_goals);
125132
let (var_values, mut external_constraints) =
126133
(self.var_values, external_constraints).fold_with(&mut EagerResolver::new(self.infcx));
127134
// Remove any trivial region constraints once we've resolved regions
@@ -170,38 +177,45 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
170177
#[instrument(level = "trace", skip(self), ret)]
171178
fn compute_external_query_constraints(
172179
&self,
180+
certainty: Certainty,
173181
normalization_nested_goals: NestedNormalizationGoals<'tcx>,
174-
) -> Result<ExternalConstraintsData<'tcx>, NoSolution> {
175-
// We only check for leaks from universes which were entered inside
176-
// of the query.
177-
self.infcx.leak_check(self.max_input_universe, None).map_err(|e| {
178-
trace!(?e, "failed the leak check");
179-
NoSolution
180-
})?;
181-
182-
// Cannot use `take_registered_region_obligations` as we may compute the response
183-
// inside of a `probe` whenever we have multiple choices inside of the solver.
184-
let region_obligations = self.infcx.inner.borrow().region_obligations().to_owned();
185-
let mut region_constraints = self.infcx.with_region_constraints(|region_constraints| {
186-
make_query_region_constraints(
187-
self.tcx(),
188-
region_obligations
189-
.iter()
190-
.map(|r_o| (r_o.sup_type, r_o.sub_region, r_o.origin.to_constraint_category())),
191-
region_constraints,
192-
)
193-
});
194-
195-
let mut seen = FxHashSet::default();
196-
region_constraints.outlives.retain(|outlives| seen.insert(*outlives));
182+
) -> ExternalConstraintsData<'tcx> {
183+
// We only return region constraints once the certainty is `Yes`. This
184+
// is necessary as we may drop nested goals on ambiguity, which may result
185+
// in unconstrained inference variables in the region constraints. It also
186+
// prevents us from emitting duplicate region constraints, avoiding some
187+
// unnecessary work. This slightly weakens the leak check in case it uses
188+
// region constraints from an ambiguous nested goal. This is tested in both
189+
// `tests/ui/higher-ranked/leak-check/leak-check-in-selection-5-ambig.rs` and
190+
// `tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.rs`.
191+
let region_constraints = if certainty == Certainty::Yes {
192+
// Cannot use `take_registered_region_obligations` as we may compute the response
193+
// inside of a `probe` whenever we have multiple choices inside of the solver.
194+
let region_obligations = self.infcx.inner.borrow().region_obligations().to_owned();
195+
let mut region_constraints = self.infcx.with_region_constraints(|region_constraints| {
196+
make_query_region_constraints(
197+
self.tcx(),
198+
region_obligations.iter().map(|r_o| {
199+
(r_o.sup_type, r_o.sub_region, r_o.origin.to_constraint_category())
200+
}),
201+
region_constraints,
202+
)
203+
});
204+
205+
let mut seen = FxHashSet::default();
206+
region_constraints.outlives.retain(|outlives| seen.insert(*outlives));
207+
region_constraints
208+
} else {
209+
Default::default()
210+
};
197211

198212
let mut opaque_types = self.infcx.clone_opaque_types_for_query_response();
199213
// Only return opaque type keys for newly-defined opaques
200214
opaque_types.retain(|(a, _)| {
201215
self.predefined_opaques_in_body.opaque_types.iter().all(|(pa, _)| pa != a)
202216
});
203217

204-
Ok(ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals })
218+
ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals }
205219
}
206220

207221
/// After calling a canonical query, we apply the constraints returned
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//@ revisions: old next
2+
//@[next] compile-flags: -Znext-solver
3+
//@ check-pass
4+
5+
// The new trait solver does not return region constraints if the goal
6+
// is still ambiguous. This causes the following test to fail with ambiguity,
7+
// even though `(): LeakCheckFailure<'!a, V>` would return `'!a: 'static`
8+
// which would have caused a leak check failure.
9+
10+
trait Ambig {}
11+
impl Ambig for u32 {}
12+
impl Ambig for u16 {}
13+
14+
trait Id<T> {}
15+
impl Id<u32> for u32 {}
16+
impl Id<u16> for u16 {}
17+
18+
19+
trait LeakCheckFailure<'a, V: ?Sized> {}
20+
impl<V: ?Sized + Ambig> LeakCheckFailure<'static, V> for () {}
21+
22+
trait Trait<U, V> {}
23+
impl<V> Trait<u32, V> for () where for<'a> (): LeakCheckFailure<'a, V> {}
24+
impl<V> Trait<u16, V> for () {}
25+
fn impls_trait<T: Trait<U, V>, U: Id<V>, V>() {}
26+
fn main() {
27+
impls_trait::<(), _, _>()
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error[E0283]: type annotations needed
2+
--> $DIR/leak-check-in-selection-6-ambig-unify.rs:30:5
3+
|
4+
LL | impls_trait::<(), _, _>()
5+
| ^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `U` declared on the function `impls_trait`
6+
|
7+
note: multiple `impl`s satisfying `(): Trait<_, _>` found
8+
--> $DIR/leak-check-in-selection-6-ambig-unify.rs:26:1
9+
|
10+
LL | impl<V> Trait<u32, V> for () where for<'b> (): LeakCheckFailure<'static, 'b, V> {}
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
LL | impl<V> Trait<u16, V> for () {}
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
14+
note: required by a bound in `impls_trait`
15+
--> $DIR/leak-check-in-selection-6-ambig-unify.rs:28:19
16+
|
17+
LL | fn impls_trait<T: Trait<U, V>, U: Id<V>, V>() {}
18+
| ^^^^^^^^^^^ required by this bound in `impls_trait`
19+
20+
error: aborting due to 1 previous error
21+
22+
For more information about this error, try `rustc --explain E0283`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error[E0283]: type annotations needed
2+
--> $DIR/leak-check-in-selection-6-ambig-unify.rs:30:5
3+
|
4+
LL | impls_trait::<(), _, _>()
5+
| ^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `U` declared on the function `impls_trait`
6+
|
7+
note: multiple `impl`s satisfying `(): Trait<_, _>` found
8+
--> $DIR/leak-check-in-selection-6-ambig-unify.rs:26:1
9+
|
10+
LL | impl<V> Trait<u32, V> for () where for<'b> (): LeakCheckFailure<'static, 'b, V> {}
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
LL | impl<V> Trait<u16, V> for () {}
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
14+
note: required by a bound in `impls_trait`
15+
--> $DIR/leak-check-in-selection-6-ambig-unify.rs:28:19
16+
|
17+
LL | fn impls_trait<T: Trait<U, V>, U: Id<V>, V>() {}
18+
| ^^^^^^^^^^^ required by this bound in `impls_trait`
19+
20+
error: aborting due to 1 previous error
21+
22+
For more information about this error, try `rustc --explain E0283`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//@ revisions: old next
2+
//@[next] compile-flags: -Znext-solver
3+
4+
// The new trait solver does not return region constraints if the goal
5+
// is still ambiguous. This should cause the following test to fail with
6+
// ambiguity as even if `(): LeakCheckFailure<'static, '!b, V>` unifies
7+
// `'!b` with `'static`, we erase all region constraints.
8+
//
9+
// However, we do still unify the var_value for `'b` with `'static`,
10+
// causing us to return this requirement via the `var_values` even if
11+
// we don't return any region constraints. This is a bit inconsistent
12+
// but isn't something we should really worry about imo.
13+
trait Ambig {}
14+
impl Ambig for u32 {}
15+
impl Ambig for u16 {}
16+
17+
trait Id<T> {}
18+
impl Id<u32> for u32 {}
19+
impl Id<u16> for u16 {}
20+
21+
22+
trait LeakCheckFailure<'a, 'b, V: ?Sized> {}
23+
impl<'a, 'b: 'a, V: ?Sized + Ambig> LeakCheckFailure<'a, 'b, V> for () {}
24+
25+
trait Trait<U, V> {}
26+
impl<V> Trait<u32, V> for () where for<'b> (): LeakCheckFailure<'static, 'b, V> {}
27+
impl<V> Trait<u16, V> for () {}
28+
fn impls_trait<T: Trait<U, V>, U: Id<V>, V>() {}
29+
fn main() {
30+
impls_trait::<(), _, _>()
31+
//~^ ERROR type annotations needed
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//@ check-pass
2+
//@ compile-flags: -Znext-solver
3+
//@ ignore-compare-mode-next-solver (explicitly enabled)
4+
5+
// Regression test for an ICE when trying to bootstrap rustc
6+
// with #125343. An ambiguous goal returned a `TypeOutlives`
7+
// constraint referencing an inference variable. This inference
8+
// variable was created inside of the goal, causing it to be
9+
// unconstrained in the caller. This then caused an ICE in MIR
10+
// borrowck.
11+
12+
struct Foo<T>(T);
13+
trait Extend<T> {
14+
fn extend<I: IntoIterator<Item = T>>(iter: I);
15+
}
16+
17+
impl<T> Extend<T> for Foo<T> {
18+
fn extend<I: IntoIterator<Item = T>>(_: I) {
19+
todo!()
20+
}
21+
}
22+
23+
impl<'a, T: 'a + Copy> Extend<&'a T> for Foo<T> {
24+
fn extend<I: IntoIterator<Item = &'a T>>(iter: I) {
25+
<Self as Extend<T>>::extend(iter.into_iter().copied())
26+
}
27+
}
28+
29+
fn main() {}

0 commit comments

Comments
 (0)