Skip to content

Commit 7436a6a

Browse files
authored
Rollup merge of rust-lang#121193 - compiler-errors:coherence-fulfillment, r=lcnr
Use fulfillment in next trait solver coherence Use fulfillment in the new trait solver's `impl_intersection_has_impossible_obligation` routine. This means that inference that falls out of processing other obligations can influence whether we can determine if an obligation is impossible to satisfy. See the committed test. This should be completely sound, since evaluation and fulfillment both respect intercrate mode equally. We run the risk of breaking coherence later if we were to change the rules of fulfillment and/or inference during coherence, but this is a problem which affects evaluation, as nested obligations from a trait goal are processed together and can influence each other in the same way. r? lcnr cc rust-lang#114862 Also changed obligationctxt -> fulfillmentctxt because it feels kind of redundant to use an ocx here. I don't really care enough and can change it back if it really matters much.
2 parents 7f3cf08 + 228441d commit 7436a6a

File tree

5 files changed

+97
-37
lines changed

5 files changed

+97
-37
lines changed

compiler/rustc_trait_selection/src/solve/inspect/analyse.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,10 @@ impl<'tcx> InferCtxt<'tcx> {
223223
goal: Goal<'tcx, ty::Predicate<'tcx>>,
224224
visitor: &mut V,
225225
) -> ControlFlow<V::BreakTy> {
226-
let (_, proof_tree) = self.evaluate_root_goal(goal, GenerateProofTree::Yes);
227-
let proof_tree = proof_tree.unwrap();
228-
visitor.visit_goal(&InspectGoal::new(self, 0, &proof_tree))
226+
self.probe(|_| {
227+
let (_, proof_tree) = self.evaluate_root_goal(goal, GenerateProofTree::Yes);
228+
let proof_tree = proof_tree.unwrap();
229+
visitor.visit_goal(&InspectGoal::new(self, 0, &proof_tree))
230+
})
229231
}
230232
}

compiler/rustc_trait_selection/src/traits/coherence.rs

+36-34
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,21 @@ use crate::infer::outlives::env::OutlivesEnvironment;
88
use crate::infer::InferOk;
99
use crate::regions::InferCtxtRegionExt;
1010
use crate::solve::inspect::{InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor};
11-
use crate::solve::{deeply_normalize_for_diagnostics, inspect};
12-
use crate::traits::engine::TraitEngineExt;
13-
use crate::traits::query::evaluate_obligation::InferCtxtExt;
11+
use crate::solve::{deeply_normalize_for_diagnostics, inspect, FulfillmentCtxt};
12+
use crate::traits::engine::TraitEngineExt as _;
1413
use crate::traits::select::IntercrateAmbiguityCause;
1514
use crate::traits::structural_normalize::StructurallyNormalizeExt;
1615
use crate::traits::NormalizeExt;
1716
use crate::traits::SkipLeakCheck;
1817
use crate::traits::{
19-
Obligation, ObligationCause, ObligationCtxt, PredicateObligation, PredicateObligations,
20-
SelectionContext,
18+
Obligation, ObligationCause, PredicateObligation, PredicateObligations, SelectionContext,
2119
};
2220
use rustc_data_structures::fx::FxIndexSet;
2321
use rustc_errors::Diagnostic;
2422
use rustc_hir::def::DefKind;
2523
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
2624
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt};
27-
use rustc_infer::traits::{util, TraitEngine};
25+
use rustc_infer::traits::{util, TraitEngine, TraitEngineExt};
2826
use rustc_middle::traits::query::NoSolution;
2927
use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal};
3028
use rustc_middle::traits::specialization_graph::OverlapMode;
@@ -310,29 +308,35 @@ fn equate_impl_headers<'tcx>(
310308
fn impl_intersection_has_impossible_obligation<'a, 'cx, 'tcx>(
311309
selcx: &mut SelectionContext<'cx, 'tcx>,
312310
obligations: &'a [PredicateObligation<'tcx>],
313-
) -> Option<&'a PredicateObligation<'tcx>> {
311+
) -> Option<PredicateObligation<'tcx>> {
314312
let infcx = selcx.infcx;
315313

316-
obligations.iter().find(|obligation| {
317-
let evaluation_result = if infcx.next_trait_solver() {
318-
infcx.evaluate_obligation(obligation)
319-
} else {
314+
if infcx.next_trait_solver() {
315+
let mut fulfill_cx = FulfillmentCtxt::new(infcx);
316+
fulfill_cx.register_predicate_obligations(infcx, obligations.iter().cloned());
317+
318+
// We only care about the obligations that are *definitely* true errors.
319+
// Ambiguities do not prove the disjointness of two impls.
320+
let mut errors = fulfill_cx.select_where_possible(infcx);
321+
errors.pop().map(|err| err.obligation)
322+
} else {
323+
obligations.iter().cloned().find(|obligation| {
320324
// We use `evaluate_root_obligation` to correctly track intercrate
321325
// ambiguity clauses. We cannot use this in the new solver.
322-
selcx.evaluate_root_obligation(obligation)
323-
};
324-
325-
match evaluation_result {
326-
Ok(result) => !result.may_apply(),
327-
// If overflow occurs, we need to conservatively treat the goal as possibly holding,
328-
// since there can be instantiations of this goal that don't overflow and result in
329-
// success. This isn't much of a problem in the old solver, since we treat overflow
330-
// fatally (this still can be encountered: <https://github.com/rust-lang/rust/issues/105231>),
331-
// but in the new solver, this is very important for correctness, since overflow
332-
// *must* be treated as ambiguity for completeness.
333-
Err(_overflow) => false,
334-
}
335-
})
326+
let evaluation_result = selcx.evaluate_root_obligation(obligation);
327+
328+
match evaluation_result {
329+
Ok(result) => !result.may_apply(),
330+
// If overflow occurs, we need to conservatively treat the goal as possibly holding,
331+
// since there can be instantiations of this goal that don't overflow and result in
332+
// success. This isn't much of a problem in the old solver, since we treat overflow
333+
// fatally (this still can be encountered: <https://github.com/rust-lang/rust/issues/105231>),
334+
// but in the new solver, this is very important for correctness, since overflow
335+
// *must* be treated as ambiguity for completeness.
336+
Err(_overflow) => false,
337+
}
338+
})
339+
}
336340
}
337341

338342
/// Check if both impls can be satisfied by a common type by considering whether
@@ -522,15 +526,13 @@ fn try_prove_negated_where_clause<'tcx>(
522526
// Without this, we over-eagerly register coherence ambiguity candidates when
523527
// impl candidates do exist.
524528
let ref infcx = root_infcx.fork_with_intercrate(false);
525-
let ocx = ObligationCtxt::new(infcx);
526-
527-
ocx.register_obligation(Obligation::new(
528-
infcx.tcx,
529-
ObligationCause::dummy(),
530-
param_env,
531-
negative_predicate,
532-
));
533-
if !ocx.select_all_or_error().is_empty() {
529+
let mut fulfill_cx = FulfillmentCtxt::new(infcx);
530+
531+
fulfill_cx.register_predicate_obligation(
532+
infcx,
533+
Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, negative_predicate),
534+
);
535+
if !fulfill_cx.select_all_or_error(infcx).is_empty() {
534536
return false;
535537
}
536538

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//@ compile-flags: -Znext-solver=coherence
2+
//@ check-pass
3+
4+
trait Mirror {
5+
type Assoc;
6+
}
7+
impl<T> Mirror for T {
8+
type Assoc = T;
9+
}
10+
11+
trait Foo {}
12+
trait Bar {}
13+
14+
// self type starts out as `?0` but is constrained to `()`
15+
// due to the where clause below. Because `(): Bar` does not
16+
// hold in intercrate mode, we can prove the impls disjoint.
17+
impl<T> Foo for T where (): Mirror<Assoc = T> {}
18+
impl<T> Foo for T where T: Bar {}
19+
20+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//@ compile-flags: -Znext-solver=coherence
2+
3+
trait Mirror {
4+
type Assoc;
5+
}
6+
impl<T> Mirror for T {
7+
type Assoc = T;
8+
}
9+
10+
trait Foo {}
11+
12+
// Even though using fulfillment in coherence allows us to figure out that
13+
// `?T = ()`, we still treat it as incoherent because `(): Iterator` may be
14+
// added upstream.
15+
impl<T> Foo for T where (): Mirror<Assoc = T> {}
16+
//~^ NOTE first implementation here
17+
impl<T> Foo for T where T: Iterator {}
18+
//~^ ERROR conflicting implementations of trait `Foo` for type `()`
19+
//~| NOTE conflicting implementation for `()`
20+
//~| NOTE upstream crates may add a new impl of trait `std::iter::Iterator` for type `()` in future versions
21+
22+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error[E0119]: conflicting implementations of trait `Foo` for type `()`
2+
--> $DIR/incoherent-even-though-we-fulfill.rs:17:1
3+
|
4+
LL | impl<T> Foo for T where (): Mirror<Assoc = T> {}
5+
| --------------------------------------------- first implementation here
6+
LL |
7+
LL | impl<T> Foo for T where T: Iterator {}
8+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `()`
9+
|
10+
= note: upstream crates may add a new impl of trait `std::iter::Iterator` for type `()` in future versions
11+
12+
error: aborting due to 1 previous error
13+
14+
For more information about this error, try `rustc --explain E0119`.

0 commit comments

Comments
 (0)