Rollup merge of #109447 - lcnr:coherence, r=compiler-errors
new solver cleanup + implement coherence the cleanup: - change `Certainty::unify_and` to consider ambig + overflow to be ambig - rename `trait_candidate_should_be_dropped_in_favor_of` to `candidate_should_be_dropped_in_favor_of` - remove outdated fixme For coherence I mostly just add an ambiguous candidate if the current trait ref is unknowable. I am doing the same for reservation impl where I also just add an ambiguous candidate.
This commit is contained in:
commit
28b9354bf6
20 changed files with 226 additions and 62 deletions
|
@ -585,8 +585,8 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn intercrate(mut self) -> Self {
|
pub fn intercrate(mut self, intercrate: bool) -> Self {
|
||||||
self.intercrate = true;
|
self.intercrate = intercrate;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,15 +63,14 @@ impl Certainty {
|
||||||
(Certainty::Yes, Certainty::Yes) => Certainty::Yes,
|
(Certainty::Yes, Certainty::Yes) => Certainty::Yes,
|
||||||
(Certainty::Yes, Certainty::Maybe(_)) => other,
|
(Certainty::Yes, Certainty::Maybe(_)) => other,
|
||||||
(Certainty::Maybe(_), Certainty::Yes) => self,
|
(Certainty::Maybe(_), Certainty::Yes) => self,
|
||||||
(Certainty::Maybe(MaybeCause::Overflow), Certainty::Maybe(MaybeCause::Overflow)) => {
|
(Certainty::Maybe(MaybeCause::Ambiguity), Certainty::Maybe(MaybeCause::Ambiguity)) => {
|
||||||
Certainty::Maybe(MaybeCause::Overflow)
|
|
||||||
}
|
|
||||||
// If at least one of the goals is ambiguous, hide the overflow as the ambiguous goal
|
|
||||||
// may still result in failure.
|
|
||||||
(Certainty::Maybe(MaybeCause::Ambiguity), Certainty::Maybe(_))
|
|
||||||
| (Certainty::Maybe(_), Certainty::Maybe(MaybeCause::Ambiguity)) => {
|
|
||||||
Certainty::Maybe(MaybeCause::Ambiguity)
|
Certainty::Maybe(MaybeCause::Ambiguity)
|
||||||
}
|
}
|
||||||
|
(Certainty::Maybe(MaybeCause::Ambiguity), Certainty::Maybe(MaybeCause::Overflow))
|
||||||
|
| (Certainty::Maybe(MaybeCause::Overflow), Certainty::Maybe(MaybeCause::Ambiguity))
|
||||||
|
| (Certainty::Maybe(MaybeCause::Overflow), Certainty::Maybe(MaybeCause::Overflow)) => {
|
||||||
|
Certainty::Maybe(MaybeCause::Overflow)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
#[cfg(doc)]
|
#[cfg(doc)]
|
||||||
use super::trait_goals::structural_traits::*;
|
use super::trait_goals::structural_traits::*;
|
||||||
use super::EvalCtxt;
|
use super::{EvalCtxt, SolverMode};
|
||||||
|
use crate::traits::coherence;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_infer::traits::query::NoSolution;
|
use rustc_infer::traits::query::NoSolution;
|
||||||
|
@ -87,6 +88,8 @@ pub(super) enum CandidateSource {
|
||||||
pub(super) trait GoalKind<'tcx>: TypeFoldable<TyCtxt<'tcx>> + Copy + Eq {
|
pub(super) trait GoalKind<'tcx>: TypeFoldable<TyCtxt<'tcx>> + Copy + Eq {
|
||||||
fn self_ty(self) -> Ty<'tcx>;
|
fn self_ty(self) -> Ty<'tcx>;
|
||||||
|
|
||||||
|
fn trait_ref(self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx>;
|
||||||
|
|
||||||
fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self;
|
fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self;
|
||||||
|
|
||||||
fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId;
|
fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId;
|
||||||
|
@ -244,15 +247,16 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
|
|
||||||
self.assemble_object_bound_candidates(goal, &mut candidates);
|
self.assemble_object_bound_candidates(goal, &mut candidates);
|
||||||
|
|
||||||
|
self.assemble_coherence_unknowable_candidates(goal, &mut candidates);
|
||||||
|
|
||||||
candidates
|
candidates
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If the self type of a goal is a projection, computing the relevant candidates is difficult.
|
/// If the self type of a goal is a projection, computing the relevant candidates is difficult.
|
||||||
///
|
///
|
||||||
/// To deal with this, we first try to normalize the self type and add the candidates for the normalized
|
/// To deal with this, we first try to normalize the self type and add the candidates for the normalized
|
||||||
/// self type to the list of candidates in case that succeeds. Note that we can't just eagerly return in
|
/// self type to the list of candidates in case that succeeds. We also have to consider candidates with the
|
||||||
/// this case as projections as self types add
|
/// projection as a self type as well
|
||||||
// FIXME complete the unfinished sentence above
|
|
||||||
fn assemble_candidates_after_normalizing_self_ty<G: GoalKind<'tcx>>(
|
fn assemble_candidates_after_normalizing_self_ty<G: GoalKind<'tcx>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
goal: Goal<'tcx, G>,
|
goal: Goal<'tcx, G>,
|
||||||
|
@ -468,14 +472,40 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn assemble_coherence_unknowable_candidates<G: GoalKind<'tcx>>(
|
||||||
|
&mut self,
|
||||||
|
goal: Goal<'tcx, G>,
|
||||||
|
candidates: &mut Vec<Candidate<'tcx>>,
|
||||||
|
) {
|
||||||
|
match self.solver_mode() {
|
||||||
|
SolverMode::Normal => return,
|
||||||
|
SolverMode::Coherence => {
|
||||||
|
let trait_ref = goal.predicate.trait_ref(self.tcx());
|
||||||
|
match coherence::trait_ref_is_knowable(self.tcx(), trait_ref) {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(_) => match self
|
||||||
|
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
|
||||||
|
{
|
||||||
|
Ok(result) => candidates
|
||||||
|
.push(Candidate { source: CandidateSource::BuiltinImpl, result }),
|
||||||
|
// FIXME: This will be reachable at some point if we're in
|
||||||
|
// `assemble_candidates_after_normalizing_self_ty` and we get a
|
||||||
|
// universe error. We'll deal with it at this point.
|
||||||
|
Err(NoSolution) => bug!("coherence candidate resulted in NoSolution"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[instrument(level = "debug", skip(self), ret)]
|
#[instrument(level = "debug", skip(self), ret)]
|
||||||
pub(super) fn merge_candidates_and_discard_reservation_impls(
|
pub(super) fn merge_candidates(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut candidates: Vec<Candidate<'tcx>>,
|
mut candidates: Vec<Candidate<'tcx>>,
|
||||||
) -> QueryResult<'tcx> {
|
) -> QueryResult<'tcx> {
|
||||||
match candidates.len() {
|
match candidates.len() {
|
||||||
0 => return Err(NoSolution),
|
0 => return Err(NoSolution),
|
||||||
1 => return Ok(self.discard_reservation_impl(candidates.pop().unwrap()).result),
|
1 => return Ok(candidates.pop().unwrap().result),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -483,10 +513,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
'outer: while i < candidates.len() {
|
'outer: while i < candidates.len() {
|
||||||
for j in (0..candidates.len()).filter(|&j| i != j) {
|
for j in (0..candidates.len()).filter(|&j| i != j) {
|
||||||
if self.trait_candidate_should_be_dropped_in_favor_of(
|
if self.candidate_should_be_dropped_in_favor_of(&candidates[i], &candidates[j])
|
||||||
&candidates[i],
|
{
|
||||||
&candidates[j],
|
|
||||||
) {
|
|
||||||
debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len());
|
debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len());
|
||||||
candidates.swap_remove(i);
|
candidates.swap_remove(i);
|
||||||
continue 'outer;
|
continue 'outer;
|
||||||
|
@ -511,11 +539,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: What if there are >1 candidates left with the same response, and one is a reservation impl?
|
Ok(candidates.pop().unwrap().result)
|
||||||
Ok(self.discard_reservation_impl(candidates.pop().unwrap()).result)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trait_candidate_should_be_dropped_in_favor_of(
|
fn candidate_should_be_dropped_in_favor_of(
|
||||||
&self,
|
&self,
|
||||||
candidate: &Candidate<'tcx>,
|
candidate: &Candidate<'tcx>,
|
||||||
other: &Candidate<'tcx>,
|
other: &Candidate<'tcx>,
|
||||||
|
@ -528,20 +555,4 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
| (CandidateSource::BuiltinImpl, _) => false,
|
| (CandidateSource::BuiltinImpl, _) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn discard_reservation_impl(&mut self, mut candidate: Candidate<'tcx>) -> Candidate<'tcx> {
|
|
||||||
if let CandidateSource::Impl(def_id) = candidate.source {
|
|
||||||
if let ty::ImplPolarity::Reservation = self.tcx().impl_polarity(def_id) {
|
|
||||||
debug!("Selected reservation impl");
|
|
||||||
// We assemble all candidates inside of a probe so by
|
|
||||||
// making a new canonical response here our result will
|
|
||||||
// have no constraints.
|
|
||||||
candidate.result = self
|
|
||||||
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
candidate
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ use rustc_span::DUMMY_SP;
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
use super::search_graph::{self, OverflowHandler};
|
use super::search_graph::{self, OverflowHandler};
|
||||||
|
use super::SolverMode;
|
||||||
use super::{search_graph::SearchGraph, Goal};
|
use super::{search_graph::SearchGraph, Goal};
|
||||||
|
|
||||||
pub struct EvalCtxt<'a, 'tcx> {
|
pub struct EvalCtxt<'a, 'tcx> {
|
||||||
|
@ -78,7 +79,9 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
|
||||||
&self,
|
&self,
|
||||||
goal: Goal<'tcx, ty::Predicate<'tcx>>,
|
goal: Goal<'tcx, ty::Predicate<'tcx>>,
|
||||||
) -> Result<(bool, Certainty), NoSolution> {
|
) -> Result<(bool, Certainty), NoSolution> {
|
||||||
let mut search_graph = search_graph::SearchGraph::new(self.tcx);
|
let mode = if self.intercrate { SolverMode::Coherence } else { SolverMode::Normal };
|
||||||
|
|
||||||
|
let mut search_graph = search_graph::SearchGraph::new(self.tcx, mode);
|
||||||
|
|
||||||
let mut ecx = EvalCtxt {
|
let mut ecx = EvalCtxt {
|
||||||
search_graph: &mut search_graph,
|
search_graph: &mut search_graph,
|
||||||
|
@ -101,6 +104,10 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||||
|
pub(super) fn solver_mode(&self) -> SolverMode {
|
||||||
|
self.search_graph.solver_mode()
|
||||||
|
}
|
||||||
|
|
||||||
/// The entry point of the solver.
|
/// The entry point of the solver.
|
||||||
///
|
///
|
||||||
/// This function deals with (coinductive) cycles, overflow, and caching
|
/// This function deals with (coinductive) cycles, overflow, and caching
|
||||||
|
@ -120,8 +127,14 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||||
//
|
//
|
||||||
// The actual solver logic happens in `ecx.compute_goal`.
|
// The actual solver logic happens in `ecx.compute_goal`.
|
||||||
search_graph.with_new_goal(tcx, canonical_goal, |search_graph| {
|
search_graph.with_new_goal(tcx, canonical_goal, |search_graph| {
|
||||||
let (ref infcx, goal, var_values) =
|
let intercrate = match search_graph.solver_mode() {
|
||||||
tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical_goal);
|
SolverMode::Normal => false,
|
||||||
|
SolverMode::Coherence => true,
|
||||||
|
};
|
||||||
|
let (ref infcx, goal, var_values) = tcx
|
||||||
|
.infer_ctxt()
|
||||||
|
.intercrate(intercrate)
|
||||||
|
.build_with_canonical(DUMMY_SP, &canonical_goal);
|
||||||
let mut ecx = EvalCtxt {
|
let mut ecx = EvalCtxt {
|
||||||
infcx,
|
infcx,
|
||||||
var_values,
|
var_values,
|
||||||
|
|
|
@ -9,10 +9,6 @@
|
||||||
//! FIXME(@lcnr): Write that section. If you read this before then ask me
|
//! FIXME(@lcnr): Write that section. If you read this before then ask me
|
||||||
//! about it on zulip.
|
//! about it on zulip.
|
||||||
|
|
||||||
// FIXME: Instead of using `infcx.canonicalize_query` we have to add a new routine which
|
|
||||||
// preserves universes and creates a unique var (in the highest universe) for each
|
|
||||||
// appearance of a region.
|
|
||||||
|
|
||||||
// FIXME: uses of `infcx.at` need to enable deferred projection equality once that's implemented.
|
// FIXME: uses of `infcx.at` need to enable deferred projection equality once that's implemented.
|
||||||
|
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
|
@ -41,6 +37,19 @@ mod trait_goals;
|
||||||
pub use eval_ctxt::{EvalCtxt, InferCtxtEvalExt};
|
pub use eval_ctxt::{EvalCtxt, InferCtxtEvalExt};
|
||||||
pub use fulfill::FulfillmentCtxt;
|
pub use fulfill::FulfillmentCtxt;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
enum SolverMode {
|
||||||
|
/// Ordinary trait solving, using everywhere except for coherence.
|
||||||
|
Normal,
|
||||||
|
/// Trait solving during coherence. There are a few notable differences
|
||||||
|
/// between coherence and ordinary trait solving.
|
||||||
|
///
|
||||||
|
/// Most importantly, trait solving during coherence must not be incomplete,
|
||||||
|
/// i.e. return `Err(NoSolution)` for goals for which a solution exists.
|
||||||
|
/// This means that we must not make any guesses or arbitrary choices.
|
||||||
|
Coherence,
|
||||||
|
}
|
||||||
|
|
||||||
trait CanonicalResponseExt {
|
trait CanonicalResponseExt {
|
||||||
fn has_no_inference_or_external_constraints(&self) -> bool;
|
fn has_no_inference_or_external_constraints(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
@ -255,7 +264,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
return Err(NoSolution);
|
return Err(NoSolution);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(-Ztreat-solver=next): We should instead try to find a `Certainty::Yes` response with
|
// FIXME(-Ztrait-solver=next): We should instead try to find a `Certainty::Yes` response with
|
||||||
// a subset of the constraints that all the other responses have.
|
// a subset of the constraints that all the other responses have.
|
||||||
let one = candidates[0];
|
let one = candidates[0];
|
||||||
if candidates[1..].iter().all(|resp| resp == &one) {
|
if candidates[1..].iter().all(|resp| resp == &one) {
|
||||||
|
|
|
@ -34,7 +34,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
// projection cache in the solver.
|
// projection cache in the solver.
|
||||||
if self.term_is_fully_unconstrained(goal) {
|
if self.term_is_fully_unconstrained(goal) {
|
||||||
let candidates = self.assemble_and_evaluate_candidates(goal);
|
let candidates = self.assemble_and_evaluate_candidates(goal);
|
||||||
self.merge_candidates_and_discard_reservation_impls(candidates)
|
self.merge_candidates(candidates)
|
||||||
} else {
|
} else {
|
||||||
let predicate = goal.predicate;
|
let predicate = goal.predicate;
|
||||||
let unconstrained_rhs = self.next_term_infer_of_kind(predicate.term);
|
let unconstrained_rhs = self.next_term_infer_of_kind(predicate.term);
|
||||||
|
@ -56,6 +56,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||||
self.self_ty()
|
self.self_ty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn trait_ref(self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> {
|
||||||
|
self.projection_ty.trait_ref(tcx)
|
||||||
|
}
|
||||||
|
|
||||||
fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
|
fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
|
||||||
self.with_self_ty(tcx, self_ty)
|
self.with_self_ty(tcx, self_ty)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
mod cache;
|
mod cache;
|
||||||
mod overflow;
|
mod overflow;
|
||||||
|
|
||||||
|
pub(super) use overflow::OverflowHandler;
|
||||||
|
|
||||||
use self::cache::ProvisionalEntry;
|
use self::cache::ProvisionalEntry;
|
||||||
pub(super) use crate::solve::search_graph::overflow::OverflowHandler;
|
|
||||||
use cache::ProvisionalCache;
|
use cache::ProvisionalCache;
|
||||||
use overflow::OverflowData;
|
use overflow::OverflowData;
|
||||||
use rustc_index::vec::IndexVec;
|
use rustc_index::vec::IndexVec;
|
||||||
|
@ -11,6 +12,8 @@ use rustc_middle::traits::solve::{CanonicalGoal, Certainty, MaybeCause, QueryRes
|
||||||
use rustc_middle::ty::TyCtxt;
|
use rustc_middle::ty::TyCtxt;
|
||||||
use std::{collections::hash_map::Entry, mem};
|
use std::{collections::hash_map::Entry, mem};
|
||||||
|
|
||||||
|
use super::SolverMode;
|
||||||
|
|
||||||
rustc_index::newtype_index! {
|
rustc_index::newtype_index! {
|
||||||
pub struct StackDepth {}
|
pub struct StackDepth {}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +24,7 @@ struct StackElem<'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) struct SearchGraph<'tcx> {
|
pub(super) struct SearchGraph<'tcx> {
|
||||||
|
mode: SolverMode,
|
||||||
/// The stack of goals currently being computed.
|
/// The stack of goals currently being computed.
|
||||||
///
|
///
|
||||||
/// An element is *deeper* in the stack if its index is *lower*.
|
/// An element is *deeper* in the stack if its index is *lower*.
|
||||||
|
@ -30,14 +34,19 @@ pub(super) struct SearchGraph<'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> SearchGraph<'tcx> {
|
impl<'tcx> SearchGraph<'tcx> {
|
||||||
pub(super) fn new(tcx: TyCtxt<'tcx>) -> SearchGraph<'tcx> {
|
pub(super) fn new(tcx: TyCtxt<'tcx>, mode: SolverMode) -> SearchGraph<'tcx> {
|
||||||
Self {
|
Self {
|
||||||
|
mode,
|
||||||
stack: Default::default(),
|
stack: Default::default(),
|
||||||
overflow_data: OverflowData::new(tcx),
|
overflow_data: OverflowData::new(tcx),
|
||||||
provisional_cache: ProvisionalCache::empty(),
|
provisional_cache: ProvisionalCache::empty(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn solver_mode(&self) -> SolverMode {
|
||||||
|
self.mode
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn is_empty(&self) -> bool {
|
pub(super) fn is_empty(&self) -> bool {
|
||||||
self.stack.is_empty() && self.provisional_cache.is_empty()
|
self.stack.is_empty() && self.provisional_cache.is_empty()
|
||||||
}
|
}
|
||||||
|
@ -245,7 +254,8 @@ impl<'tcx> SearchGraph<'tcx> {
|
||||||
// dependencies, our non-root goal may no longer appear as child of the root goal.
|
// dependencies, our non-root goal may no longer appear as child of the root goal.
|
||||||
//
|
//
|
||||||
// See https://github.com/rust-lang/rust/pull/108071 for some additional context.
|
// See https://github.com/rust-lang/rust/pull/108071 for some additional context.
|
||||||
let should_cache_globally = !self.overflow_data.did_overflow() || self.stack.is_empty();
|
let should_cache_globally = matches!(self.solver_mode(), SolverMode::Normal)
|
||||||
|
&& (!self.overflow_data.did_overflow() || self.stack.is_empty());
|
||||||
if should_cache_globally {
|
if should_cache_globally {
|
||||||
tcx.new_solver_evaluation_cache.insert(
|
tcx.new_solver_evaluation_cache.insert(
|
||||||
current_goal.goal,
|
current_goal.goal,
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
|
||||||
use super::{assembly, EvalCtxt};
|
use super::{assembly, EvalCtxt, SolverMode};
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::LangItem;
|
use rustc_hir::LangItem;
|
||||||
use rustc_infer::traits::query::NoSolution;
|
use rustc_infer::traits::query::NoSolution;
|
||||||
|
@ -20,6 +20,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||||
self.self_ty()
|
self.self_ty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn trait_ref(self, _: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> {
|
||||||
|
self.trait_ref
|
||||||
|
}
|
||||||
|
|
||||||
fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
|
fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
|
||||||
self.with_self_ty(tcx, self_ty)
|
self.with_self_ty(tcx, self_ty)
|
||||||
}
|
}
|
||||||
|
@ -43,6 +47,22 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||||
return Err(NoSolution);
|
return Err(NoSolution);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let impl_polarity = tcx.impl_polarity(impl_def_id);
|
||||||
|
// An upper bound of the certainty of this goal, used to lower the certainty
|
||||||
|
// of reservation impl to ambiguous during coherence.
|
||||||
|
let maximal_certainty = match impl_polarity {
|
||||||
|
ty::ImplPolarity::Positive | ty::ImplPolarity::Negative => {
|
||||||
|
match impl_polarity == goal.predicate.polarity {
|
||||||
|
true => Certainty::Yes,
|
||||||
|
false => return Err(NoSolution),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ty::ImplPolarity::Reservation => match ecx.solver_mode() {
|
||||||
|
SolverMode::Normal => return Err(NoSolution),
|
||||||
|
SolverMode::Coherence => Certainty::AMBIGUOUS,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
ecx.probe(|ecx| {
|
ecx.probe(|ecx| {
|
||||||
let impl_substs = ecx.fresh_substs_for_item(impl_def_id);
|
let impl_substs = ecx.fresh_substs_for_item(impl_def_id);
|
||||||
let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
|
let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
|
||||||
|
@ -55,7 +75,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|pred| goal.with(tcx, pred));
|
.map(|pred| goal.with(tcx, pred));
|
||||||
ecx.add_goals(where_clause_bounds);
|
ecx.add_goals(where_clause_bounds);
|
||||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
|
||||||
|
ecx.evaluate_added_goals_and_make_canonical_response(maximal_certainty)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -547,6 +568,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
goal: Goal<'tcx, TraitPredicate<'tcx>>,
|
goal: Goal<'tcx, TraitPredicate<'tcx>>,
|
||||||
) -> QueryResult<'tcx> {
|
) -> QueryResult<'tcx> {
|
||||||
let candidates = self.assemble_and_evaluate_candidates(goal);
|
let candidates = self.assemble_and_evaluate_candidates(goal);
|
||||||
self.merge_candidates_and_discard_reservation_impls(candidates)
|
self.merge_candidates(candidates)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,8 +95,11 @@ pub fn overlapping_impls(
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let infcx =
|
let infcx = tcx
|
||||||
tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bubble).intercrate().build();
|
.infer_ctxt()
|
||||||
|
.with_opaque_type_inference(DefiningAnchor::Bubble)
|
||||||
|
.intercrate(true)
|
||||||
|
.build();
|
||||||
let selcx = &mut SelectionContext::new(&infcx);
|
let selcx = &mut SelectionContext::new(&infcx);
|
||||||
let overlaps =
|
let overlaps =
|
||||||
overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).is_some();
|
overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).is_some();
|
||||||
|
@ -107,8 +110,11 @@ pub fn overlapping_impls(
|
||||||
// In the case where we detect an error, run the check again, but
|
// In the case where we detect an error, run the check again, but
|
||||||
// this time tracking intercrate ambiguity causes for better
|
// this time tracking intercrate ambiguity causes for better
|
||||||
// diagnostics. (These take time and can lead to false errors.)
|
// diagnostics. (These take time and can lead to false errors.)
|
||||||
let infcx =
|
let infcx = tcx
|
||||||
tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bubble).intercrate().build();
|
.infer_ctxt()
|
||||||
|
.with_opaque_type_inference(DefiningAnchor::Bubble)
|
||||||
|
.intercrate(true)
|
||||||
|
.build();
|
||||||
let selcx = &mut SelectionContext::new(&infcx);
|
let selcx = &mut SelectionContext::new(&infcx);
|
||||||
selcx.enable_tracking_intercrate_ambiguity_causes();
|
selcx.enable_tracking_intercrate_ambiguity_causes();
|
||||||
Some(overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).unwrap())
|
Some(overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).unwrap())
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
pub mod auto_trait;
|
pub mod auto_trait;
|
||||||
mod chalk_fulfill;
|
mod chalk_fulfill;
|
||||||
mod coherence;
|
pub(crate) mod coherence;
|
||||||
pub mod const_evaluatable;
|
pub mod const_evaluatable;
|
||||||
mod engine;
|
mod engine;
|
||||||
pub mod error_reporting;
|
pub mod error_reporting;
|
||||||
|
|
44
tests/ui/traits/new-solver/coherence/issue-102048.rs
Normal file
44
tests/ui/traits/new-solver/coherence/issue-102048.rs
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
// This must fail coherence.
|
||||||
|
//
|
||||||
|
// Getting this to pass was fairly difficult, so here's an explanation
|
||||||
|
// of what's happening:
|
||||||
|
//
|
||||||
|
// Normalizing projections currently tries to replace them with inference variables
|
||||||
|
// while emitting a nested `Projection` obligation. This cannot be done if the projection
|
||||||
|
// has bound variables which is the case here.
|
||||||
|
//
|
||||||
|
// So the projections stay until after normalization. When unifying two projections we
|
||||||
|
// currently treat them as if they are injective, so we **incorrectly** unify their
|
||||||
|
// substs. This means that coherence for the two impls ends up unifying `?T` and `?U`
|
||||||
|
// as it tries to unify `<?T as WithAssoc1<'a>>::Assoc` with `<?U as WithAssoc1<'a>>::Assoc`.
|
||||||
|
//
|
||||||
|
// `impl1` therefore has the projection `<?T as WithAssoc2<'a>>::Assoc` and we have the
|
||||||
|
// assumption `?T: for<'a> WithAssoc2<'a, Assoc = i32>` in the `param_env`, so we normalize
|
||||||
|
// that to `i32`. We then try to unify `i32` from `impl1` with `u32` from `impl2` which fails,
|
||||||
|
// causing coherence to consider these two impls distinct.
|
||||||
|
|
||||||
|
// compile-flags: -Ztrait-solver=next
|
||||||
|
pub trait Trait<T> {}
|
||||||
|
|
||||||
|
pub trait WithAssoc1<'a> {
|
||||||
|
type Assoc;
|
||||||
|
}
|
||||||
|
pub trait WithAssoc2<'a> {
|
||||||
|
type Assoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// impl 1
|
||||||
|
impl<T, U> Trait<for<'a> fn(<T as WithAssoc1<'a>>::Assoc, <U as WithAssoc2<'a>>::Assoc)> for (T, U)
|
||||||
|
where
|
||||||
|
T: for<'a> WithAssoc1<'a> + for<'a> WithAssoc2<'a, Assoc = i32>,
|
||||||
|
U: for<'a> WithAssoc2<'a>,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// impl 2
|
||||||
|
impl<T, U> Trait<for<'a> fn(<U as WithAssoc1<'a>>::Assoc, u32)> for (T, U) where
|
||||||
|
U: for<'a> WithAssoc1<'a> //~^ ERROR conflicting implementations of trait
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
12
tests/ui/traits/new-solver/coherence/issue-102048.stderr
Normal file
12
tests/ui/traits/new-solver/coherence/issue-102048.stderr
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
error[E0119]: conflicting implementations of trait `Trait<for<'a> fn(<_ as WithAssoc1<'a>>::Assoc, <_ as WithAssoc2<'a>>::Assoc)>` for type `(_, _)`
|
||||||
|
--> $DIR/issue-102048.rs:39:1
|
||||||
|
|
|
||||||
|
LL | impl<T, U> Trait<for<'a> fn(<T as WithAssoc1<'a>>::Assoc, <U as WithAssoc2<'a>>::Assoc)> for (T, U)
|
||||||
|
| --------------------------------------------------------------------------------------------------- first implementation here
|
||||||
|
...
|
||||||
|
LL | impl<T, U> Trait<for<'a> fn(<U as WithAssoc1<'a>>::Assoc, u32)> for (T, U) where
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(_, _)`
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0119`.
|
|
@ -0,0 +1,11 @@
|
||||||
|
error[E0119]: conflicting implementations of trait `OtherTrait` for type `()`
|
||||||
|
--> $DIR/coherence-conflict.rs:12:1
|
||||||
|
|
|
||||||
|
LL | impl OtherTrait for () {}
|
||||||
|
| ---------------------- first implementation here
|
||||||
|
LL | impl<T: MyTrait> OtherTrait for T {}
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `()`
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0119`.
|
|
@ -1,5 +1,5 @@
|
||||||
error[E0119]: conflicting implementations of trait `OtherTrait` for type `()`
|
error[E0119]: conflicting implementations of trait `OtherTrait` for type `()`
|
||||||
--> $DIR/coherence-conflict.rs:11:1
|
--> $DIR/coherence-conflict.rs:12:1
|
||||||
|
|
|
|
||||||
LL | impl OtherTrait for () {}
|
LL | impl OtherTrait for () {}
|
||||||
| ---------------------- first implementation here
|
| ---------------------- first implementation here
|
|
@ -1,5 +1,6 @@
|
||||||
// check that reservation impls are accounted for in negative reasoning.
|
// check that reservation impls are accounted for in negative reasoning.
|
||||||
|
// revisions: old next
|
||||||
|
//[next] compile-flags: -Ztrait-solver=next
|
||||||
#![feature(rustc_attrs)]
|
#![feature(rustc_attrs)]
|
||||||
|
|
||||||
trait MyTrait {}
|
trait MyTrait {}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
error[E0277]: the trait bound `(): MyTrait` is not satisfied
|
error[E0277]: the trait bound `(): MyTrait` is not satisfied
|
||||||
--> $DIR/no-use.rs:10:26
|
--> $DIR/no-use.rs:11:26
|
||||||
|
|
|
|
||||||
LL | <() as MyTrait>::foo(&());
|
LL | <() as MyTrait>::foo(&());
|
||||||
| -------------------- ^^^ the trait `MyTrait` is not implemented for `()`
|
| -------------------- ^^^ the trait `MyTrait` is not implemented for `()`
|
13
tests/ui/traits/reservation-impl/no-use.old.stderr
Normal file
13
tests/ui/traits/reservation-impl/no-use.old.stderr
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
error[E0277]: the trait bound `(): MyTrait` is not satisfied
|
||||||
|
--> $DIR/no-use.rs:11:26
|
||||||
|
|
|
||||||
|
LL | <() as MyTrait>::foo(&());
|
||||||
|
| -------------------- ^^^ the trait `MyTrait` is not implemented for `()`
|
||||||
|
| |
|
||||||
|
| required by a bound introduced by this call
|
||||||
|
|
|
||||||
|
= help: the trait `MyTrait` is implemented for `()`
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0277`.
|
|
@ -1,5 +1,6 @@
|
||||||
// check that reservation impls can't be used as normal impls in positive reasoning.
|
// check that reservation impls can't be used as normal impls in positive reasoning.
|
||||||
|
// revisions: old next
|
||||||
|
//[next] compile-flags: -Ztrait-solver=next
|
||||||
#![feature(rustc_attrs)]
|
#![feature(rustc_attrs)]
|
||||||
|
|
||||||
trait MyTrait { fn foo(&self); }
|
trait MyTrait { fn foo(&self); }
|
||||||
|
|
|
@ -30,6 +30,12 @@
|
||||||
//
|
//
|
||||||
// [ii]: https://smallcultfollowing.com/babysteps/blog/2016/09/24/intersection-impls/
|
// [ii]: https://smallcultfollowing.com/babysteps/blog/2016/09/24/intersection-impls/
|
||||||
|
|
||||||
|
|
||||||
|
// check that reservation impls can't be used as normal impls in positive reasoning.
|
||||||
|
|
||||||
|
// revisions: old next
|
||||||
|
//[next] compile-flags: -Ztrait-solver=next
|
||||||
|
|
||||||
#![feature(rustc_attrs, never_type)]
|
#![feature(rustc_attrs, never_type)]
|
||||||
|
|
||||||
trait MyTrait {}
|
trait MyTrait {}
|
||||||
|
|
|
@ -3,6 +3,9 @@
|
||||||
// rpass test for reservation impls. Not 100% required because `From` uses them,
|
// rpass test for reservation impls. Not 100% required because `From` uses them,
|
||||||
// but still.
|
// but still.
|
||||||
|
|
||||||
|
// revisions: old next
|
||||||
|
//[next] compile-flags: -Ztrait-solver=next
|
||||||
|
|
||||||
#![feature(rustc_attrs)]
|
#![feature(rustc_attrs)]
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue