From 24b5466892956b26b5e261ed418aee3c9c6e6d45 Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 22 May 2024 16:56:10 +0000 Subject: [PATCH] drop region constraints for ambiguous goals --- .../src/solve/eval_ctxt/canonical.rs | 64 +++++++++++-------- .../leak-check-in-selection-5-ambig.rs | 28 ++++++++ ...eck-in-selection-6-ambig-unify.next.stderr | 22 +++++++ ...heck-in-selection-6-ambig-unify.old.stderr | 22 +++++++ .../leak-check-in-selection-6-ambig-unify.rs | 32 ++++++++++ .../ambig-goal-infer-in-type-oulives.rs | 29 +++++++++ 6 files changed, 172 insertions(+), 25 deletions(-) create mode 100644 tests/ui/higher-ranked/leak-check/leak-check-in-selection-5-ambig.rs create mode 100644 tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.next.stderr create mode 100644 tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.old.stderr create mode 100644 tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.rs create mode 100644 tests/ui/traits/next-solver/normalize/ambig-goal-infer-in-type-oulives.rs diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs index 9590a82c0677f..f6ec654908450 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs @@ -99,6 +99,13 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { previous call to `try_evaluate_added_goals!`" ); + // We only check for leaks from universes which were entered inside + // of the query. + self.infcx.leak_check(self.max_input_universe, None).map_err(|e| { + trace!(?e, "failed the leak check"); + NoSolution + })?; + // When normalizing, we've replaced the expected term with an unconstrained // inference variable. This means that we dropped information which could // have been important. We handle this by instead returning the nested goals @@ -121,7 +128,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { }; let external_constraints = - self.compute_external_query_constraints(normalization_nested_goals)?; + self.compute_external_query_constraints(certainty, normalization_nested_goals); let (var_values, mut external_constraints) = (self.var_values, external_constraints).fold_with(&mut EagerResolver::new(self.infcx)); // Remove any trivial region constraints once we've resolved regions @@ -170,30 +177,37 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { #[instrument(level = "trace", skip(self), ret)] fn compute_external_query_constraints( &self, + certainty: Certainty, normalization_nested_goals: NestedNormalizationGoals<'tcx>, - ) -> Result, NoSolution> { - // We only check for leaks from universes which were entered inside - // of the query. - self.infcx.leak_check(self.max_input_universe, None).map_err(|e| { - trace!(?e, "failed the leak check"); - NoSolution - })?; - - // Cannot use `take_registered_region_obligations` as we may compute the response - // inside of a `probe` whenever we have multiple choices inside of the solver. - let region_obligations = self.infcx.inner.borrow().region_obligations().to_owned(); - let mut region_constraints = self.infcx.with_region_constraints(|region_constraints| { - make_query_region_constraints( - self.tcx(), - region_obligations - .iter() - .map(|r_o| (r_o.sup_type, r_o.sub_region, r_o.origin.to_constraint_category())), - region_constraints, - ) - }); - - let mut seen = FxHashSet::default(); - region_constraints.outlives.retain(|outlives| seen.insert(*outlives)); + ) -> ExternalConstraintsData<'tcx> { + // We only return region constraints once the certainty is `Yes`. This + // is necessary as we may drop nested goals on ambiguity, which may result + // in unconstrained inference variables in the region constraints. It also + // prevents us from emitting duplicate region constraints, avoiding some + // unnecessary work. This slightly weakens the leak check in case it uses + // region constraints from an ambiguous nested goal. This is tested in both + // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-5-ambig.rs` and + // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.rs`. + let region_constraints = if certainty == Certainty::Yes { + // Cannot use `take_registered_region_obligations` as we may compute the response + // inside of a `probe` whenever we have multiple choices inside of the solver. + let region_obligations = self.infcx.inner.borrow().region_obligations().to_owned(); + let mut region_constraints = self.infcx.with_region_constraints(|region_constraints| { + make_query_region_constraints( + self.tcx(), + region_obligations.iter().map(|r_o| { + (r_o.sup_type, r_o.sub_region, r_o.origin.to_constraint_category()) + }), + region_constraints, + ) + }); + + let mut seen = FxHashSet::default(); + region_constraints.outlives.retain(|outlives| seen.insert(*outlives)); + region_constraints + } else { + Default::default() + }; let mut opaque_types = self.infcx.clone_opaque_types_for_query_response(); // Only return opaque type keys for newly-defined opaques @@ -201,7 +215,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { self.predefined_opaques_in_body.opaque_types.iter().all(|(pa, _)| pa != a) }); - Ok(ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals }) + ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals } } /// After calling a canonical query, we apply the constraints returned diff --git a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-5-ambig.rs b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-5-ambig.rs new file mode 100644 index 0000000000000..beda719ac208a --- /dev/null +++ b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-5-ambig.rs @@ -0,0 +1,28 @@ +//@ revisions: old next +//@[next] compile-flags: -Znext-solver +//@ check-pass + +// The new trait solver does not return region constraints if the goal +// is still ambiguous. This causes the following test to fail with ambiguity, +// even though `(): LeakCheckFailure<'!a, V>` would return `'!a: 'static` +// which would have caused a leak check failure. + +trait Ambig {} +impl Ambig for u32 {} +impl Ambig for u16 {} + +trait Id {} +impl Id for u32 {} +impl Id for u16 {} + + +trait LeakCheckFailure<'a, V: ?Sized> {} +impl LeakCheckFailure<'static, V> for () {} + +trait Trait {} +impl Trait for () where for<'a> (): LeakCheckFailure<'a, V> {} +impl Trait for () {} +fn impls_trait, U: Id, V>() {} +fn main() { + impls_trait::<(), _, _>() +} diff --git a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.next.stderr b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.next.stderr new file mode 100644 index 0000000000000..a12e3312230af --- /dev/null +++ b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.next.stderr @@ -0,0 +1,22 @@ +error[E0283]: type annotations needed + --> $DIR/leak-check-in-selection-6-ambig-unify.rs:30:5 + | +LL | impls_trait::<(), _, _>() + | ^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `U` declared on the function `impls_trait` + | +note: multiple `impl`s satisfying `(): Trait<_, _>` found + --> $DIR/leak-check-in-selection-6-ambig-unify.rs:26:1 + | +LL | impl Trait for () where for<'b> (): LeakCheckFailure<'static, 'b, V> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl Trait for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `impls_trait` + --> $DIR/leak-check-in-selection-6-ambig-unify.rs:28:19 + | +LL | fn impls_trait, U: Id, V>() {} + | ^^^^^^^^^^^ required by this bound in `impls_trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.old.stderr b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.old.stderr new file mode 100644 index 0000000000000..a12e3312230af --- /dev/null +++ b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.old.stderr @@ -0,0 +1,22 @@ +error[E0283]: type annotations needed + --> $DIR/leak-check-in-selection-6-ambig-unify.rs:30:5 + | +LL | impls_trait::<(), _, _>() + | ^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `U` declared on the function `impls_trait` + | +note: multiple `impl`s satisfying `(): Trait<_, _>` found + --> $DIR/leak-check-in-selection-6-ambig-unify.rs:26:1 + | +LL | impl Trait for () where for<'b> (): LeakCheckFailure<'static, 'b, V> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl Trait for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `impls_trait` + --> $DIR/leak-check-in-selection-6-ambig-unify.rs:28:19 + | +LL | fn impls_trait, U: Id, V>() {} + | ^^^^^^^^^^^ required by this bound in `impls_trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.rs b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.rs new file mode 100644 index 0000000000000..592d581695e52 --- /dev/null +++ b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.rs @@ -0,0 +1,32 @@ +//@ revisions: old next +//@[next] compile-flags: -Znext-solver + +// The new trait solver does not return region constraints if the goal +// is still ambiguous. This should cause the following test to fail with +// ambiguity as even if `(): LeakCheckFailure<'static, '!b, V>` unifies +// `'!b` with `'static`, we erase all region constraints. +// +// However, we do still unify the var_value for `'b` with `'static`, +// causing us to return this requirement via the `var_values` even if +// we don't return any region constraints. This is a bit inconsistent +// but isn't something we should really worry about imo. +trait Ambig {} +impl Ambig for u32 {} +impl Ambig for u16 {} + +trait Id {} +impl Id for u32 {} +impl Id for u16 {} + + +trait LeakCheckFailure<'a, 'b, V: ?Sized> {} +impl<'a, 'b: 'a, V: ?Sized + Ambig> LeakCheckFailure<'a, 'b, V> for () {} + +trait Trait {} +impl Trait for () where for<'b> (): LeakCheckFailure<'static, 'b, V> {} +impl Trait for () {} +fn impls_trait, U: Id, V>() {} +fn main() { + impls_trait::<(), _, _>() + //~^ ERROR type annotations needed +} diff --git a/tests/ui/traits/next-solver/normalize/ambig-goal-infer-in-type-oulives.rs b/tests/ui/traits/next-solver/normalize/ambig-goal-infer-in-type-oulives.rs new file mode 100644 index 0000000000000..18dc34f7cc437 --- /dev/null +++ b/tests/ui/traits/next-solver/normalize/ambig-goal-infer-in-type-oulives.rs @@ -0,0 +1,29 @@ +//@ check-pass +//@ compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicitly enabled) + +// Regression test for an ICE when trying to bootstrap rustc +// with #125343. An ambiguous goal returned a `TypeOutlives` +// constraint referencing an inference variable. This inference +// variable was created inside of the goal, causing it to be +// unconstrained in the caller. This then caused an ICE in MIR +// borrowck. + +struct Foo(T); +trait Extend { + fn extend>(iter: I); +} + +impl Extend for Foo { + fn extend>(_: I) { + todo!() + } +} + +impl<'a, T: 'a + Copy> Extend<&'a T> for Foo { + fn extend>(iter: I) { + >::extend(iter.into_iter().copied()) + } +} + +fn main() {}