Rollup merge of #107786 - compiler-errors:new-solver-some-tweaks, r=lcnr
Implement some tweaks in the new solver I've been testing the new solver on some small codebases, and these are a few small changes I've needed to make. The most "controversial" here is implementing `trait_candidate_should_be_dropped_in_favor_of`, which I just implemented to always return false. This surprisingly allows some code to compile, without us having to actually decide on any semantics yet. r? `@rust-lang/initiative-trait-system-refactor`
This commit is contained in:
commit
39ba11036a
@ -3,7 +3,7 @@
|
|||||||
use super::infcx_ext::InferCtxtExt;
|
use super::infcx_ext::InferCtxtExt;
|
||||||
#[cfg(doc)]
|
#[cfg(doc)]
|
||||||
use super::trait_goals::structural_traits::*;
|
use super::trait_goals::structural_traits::*;
|
||||||
use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, QueryResult};
|
use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, MaybeCause, QueryResult};
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_infer::traits::query::NoSolution;
|
use rustc_infer::traits::query::NoSolution;
|
||||||
use rustc_infer::traits::util::elaborate_predicates;
|
use rustc_infer::traits::util::elaborate_predicates;
|
||||||
@ -399,10 +399,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
|||||||
ty::Alias(_, alias_ty) => alias_ty,
|
ty::Alias(_, alias_ty) => alias_ty,
|
||||||
};
|
};
|
||||||
|
|
||||||
for (assumption, _) in self
|
for assumption in self.tcx().item_bounds(alias_ty.def_id).subst(self.tcx(), alias_ty.substs)
|
||||||
.tcx()
|
|
||||||
.bound_explicit_item_bounds(alias_ty.def_id)
|
|
||||||
.subst_iter_copied(self.tcx(), alias_ty.substs)
|
|
||||||
{
|
{
|
||||||
match G::consider_assumption(self, goal, assumption) {
|
match G::consider_assumption(self, goal, assumption) {
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
@ -462,4 +459,78 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip(self), ret)]
|
||||||
|
pub(super) fn merge_candidates_and_discard_reservation_impls(
|
||||||
|
&mut self,
|
||||||
|
mut candidates: Vec<Candidate<'tcx>>,
|
||||||
|
) -> QueryResult<'tcx> {
|
||||||
|
match candidates.len() {
|
||||||
|
0 => return Err(NoSolution),
|
||||||
|
1 => return Ok(self.discard_reservation_impl(candidates.pop().unwrap()).result),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if candidates.len() > 1 {
|
||||||
|
let mut i = 0;
|
||||||
|
'outer: while i < candidates.len() {
|
||||||
|
for j in (0..candidates.len()).filter(|&j| i != j) {
|
||||||
|
if self.trait_candidate_should_be_dropped_in_favor_of(
|
||||||
|
&candidates[i],
|
||||||
|
&candidates[j],
|
||||||
|
) {
|
||||||
|
debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len());
|
||||||
|
candidates.swap_remove(i);
|
||||||
|
continue 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len());
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are *STILL* multiple candidates, give up
|
||||||
|
// and report ambiguity.
|
||||||
|
if candidates.len() > 1 {
|
||||||
|
let certainty = if candidates.iter().all(|x| {
|
||||||
|
matches!(x.result.value.certainty, Certainty::Maybe(MaybeCause::Overflow))
|
||||||
|
}) {
|
||||||
|
Certainty::Maybe(MaybeCause::Overflow)
|
||||||
|
} else {
|
||||||
|
Certainty::AMBIGUOUS
|
||||||
|
};
|
||||||
|
return self.make_canonical_response(certainty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(self.discard_reservation_impl(candidates.pop().unwrap()).result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trait_candidate_should_be_dropped_in_favor_of(
|
||||||
|
&self,
|
||||||
|
candidate: &Candidate<'tcx>,
|
||||||
|
other: &Candidate<'tcx>,
|
||||||
|
) -> bool {
|
||||||
|
// FIXME: implement this
|
||||||
|
match (candidate.source, other.source) {
|
||||||
|
(CandidateSource::Impl(_), _)
|
||||||
|
| (CandidateSource::ParamEnv(_), _)
|
||||||
|
| (CandidateSource::AliasBound, _)
|
||||||
|
| (CandidateSource::BuiltinImpl, _) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn discard_reservation_impl(&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.make_canonical_response(Certainty::AMBIGUOUS).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
candidate
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::traits::{specialization_graph, translate_substs};
|
use crate::traits::{specialization_graph, translate_substs};
|
||||||
|
|
||||||
use super::assembly::{self, Candidate, CandidateSource};
|
use super::assembly;
|
||||||
use super::infcx_ext::InferCtxtExt;
|
use super::infcx_ext::InferCtxtExt;
|
||||||
use super::trait_goals::structural_traits;
|
use super::trait_goals::structural_traits;
|
||||||
use super::{Certainty, EvalCtxt, Goal, QueryResult};
|
use super::{Certainty, EvalCtxt, Goal, QueryResult};
|
||||||
@ -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_project_candidates(candidates)
|
self.merge_candidates_and_discard_reservation_impls(candidates)
|
||||||
} else {
|
} else {
|
||||||
let predicate = goal.predicate;
|
let predicate = goal.predicate;
|
||||||
let unconstrained_rhs = match predicate.term.unpack() {
|
let unconstrained_rhs = match predicate.term.unpack() {
|
||||||
@ -153,59 +153,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
|||||||
|
|
||||||
self.make_canonical_response(normalization_certainty.unify_and(rhs_certainty))
|
self.make_canonical_response(normalization_certainty.unify_and(rhs_certainty))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn merge_project_candidates(
|
|
||||||
&mut self,
|
|
||||||
mut candidates: Vec<Candidate<'tcx>>,
|
|
||||||
) -> QueryResult<'tcx> {
|
|
||||||
match candidates.len() {
|
|
||||||
0 => return Err(NoSolution),
|
|
||||||
1 => return Ok(candidates.pop().unwrap().result),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
if candidates.len() > 1 {
|
|
||||||
let mut i = 0;
|
|
||||||
'outer: while i < candidates.len() {
|
|
||||||
for j in (0..candidates.len()).filter(|&j| i != j) {
|
|
||||||
if self.project_candidate_should_be_dropped_in_favor_of(
|
|
||||||
&candidates[i],
|
|
||||||
&candidates[j],
|
|
||||||
) {
|
|
||||||
debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len());
|
|
||||||
candidates.swap_remove(i);
|
|
||||||
continue 'outer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len());
|
|
||||||
// If there are *STILL* multiple candidates, give up
|
|
||||||
// and report ambiguity.
|
|
||||||
i += 1;
|
|
||||||
if i > 1 {
|
|
||||||
debug!("multiple matches, ambig");
|
|
||||||
// FIXME: return overflow if all candidates overflow, otherwise return ambiguity.
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(candidates.pop().unwrap().result)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn project_candidate_should_be_dropped_in_favor_of(
|
|
||||||
&self,
|
|
||||||
candidate: &Candidate<'tcx>,
|
|
||||||
other: &Candidate<'tcx>,
|
|
||||||
) -> bool {
|
|
||||||
// FIXME: implement this
|
|
||||||
match (candidate.source, other.source) {
|
|
||||||
(CandidateSource::Impl(_), _)
|
|
||||||
| (CandidateSource::ParamEnv(_), _)
|
|
||||||
| (CandidateSource::BuiltinImpl, _)
|
|
||||||
| (CandidateSource::AliasBound, _) => unimplemented!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||||
@ -452,7 +399,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
|||||||
[ty::GenericArg::from(goal.predicate.self_ty())],
|
[ty::GenericArg::from(goal.predicate.self_ty())],
|
||||||
));
|
));
|
||||||
|
|
||||||
let is_sized_certainty = ecx.evaluate_goal(goal.with(tcx, sized_predicate))?.1;
|
let (_, is_sized_certainty) =
|
||||||
|
ecx.evaluate_goal(goal.with(tcx, sized_predicate))?;
|
||||||
return ecx.eq_term_and_make_canonical_response(
|
return ecx.eq_term_and_make_canonical_response(
|
||||||
goal,
|
goal,
|
||||||
is_sized_certainty,
|
is_sized_certainty,
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
|
||||||
use super::assembly::{self, Candidate, CandidateSource};
|
use super::assembly;
|
||||||
use super::infcx_ext::InferCtxtExt;
|
use super::infcx_ext::InferCtxtExt;
|
||||||
use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, QueryResult};
|
use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, QueryResult};
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
@ -479,73 +479,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_trait_candidates_discard_reservation_impls(candidates)
|
self.merge_candidates_and_discard_reservation_impls(candidates)
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(level = "debug", skip(self), ret)]
|
|
||||||
pub(super) fn merge_trait_candidates_discard_reservation_impls(
|
|
||||||
&mut self,
|
|
||||||
mut candidates: Vec<Candidate<'tcx>>,
|
|
||||||
) -> QueryResult<'tcx> {
|
|
||||||
match candidates.len() {
|
|
||||||
0 => return Err(NoSolution),
|
|
||||||
1 => return Ok(self.discard_reservation_impl(candidates.pop().unwrap()).result),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
if candidates.len() > 1 {
|
|
||||||
let mut i = 0;
|
|
||||||
'outer: while i < candidates.len() {
|
|
||||||
for j in (0..candidates.len()).filter(|&j| i != j) {
|
|
||||||
if self.trait_candidate_should_be_dropped_in_favor_of(
|
|
||||||
&candidates[i],
|
|
||||||
&candidates[j],
|
|
||||||
) {
|
|
||||||
debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len());
|
|
||||||
candidates.swap_remove(i);
|
|
||||||
continue 'outer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len());
|
|
||||||
// If there are *STILL* multiple candidates, give up
|
|
||||||
// and report ambiguity.
|
|
||||||
i += 1;
|
|
||||||
if i > 1 {
|
|
||||||
debug!("multiple matches, ambig");
|
|
||||||
// FIXME: return overflow if all candidates overflow, otherwise return ambiguity.
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(self.discard_reservation_impl(candidates.pop().unwrap()).result)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn trait_candidate_should_be_dropped_in_favor_of(
|
|
||||||
&self,
|
|
||||||
candidate: &Candidate<'tcx>,
|
|
||||||
other: &Candidate<'tcx>,
|
|
||||||
) -> bool {
|
|
||||||
// FIXME: implement this
|
|
||||||
match (candidate.source, other.source) {
|
|
||||||
(CandidateSource::Impl(_), _)
|
|
||||||
| (CandidateSource::ParamEnv(_), _)
|
|
||||||
| (CandidateSource::AliasBound, _)
|
|
||||||
| (CandidateSource::BuiltinImpl, _) => unimplemented!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn discard_reservation_impl(&self, 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");
|
|
||||||
// FIXME: reduce candidate to ambiguous
|
|
||||||
// FIXME: replace `var_values` with identity, yeet external constraints.
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
candidate
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -646,11 +646,9 @@ fn object_ty_for_trait<'tcx>(
|
|||||||
debug!(?obligation);
|
debug!(?obligation);
|
||||||
let pred = obligation.predicate.to_opt_poly_projection_pred()?;
|
let pred = obligation.predicate.to_opt_poly_projection_pred()?;
|
||||||
Some(pred.map_bound(|p| {
|
Some(pred.map_bound(|p| {
|
||||||
ty::ExistentialPredicate::Projection(ty::ExistentialProjection {
|
ty::ExistentialPredicate::Projection(ty::ExistentialProjection::erase_self_ty(
|
||||||
def_id: p.projection_ty.def_id,
|
tcx, p,
|
||||||
substs: p.projection_ty.substs,
|
))
|
||||||
term: p.term,
|
|
||||||
})
|
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
12
tests/ui/traits/new-solver/elaborate-item-bounds.rs
Normal file
12
tests/ui/traits/new-solver/elaborate-item-bounds.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// compile-flags: -Ztrait-solver=next
|
||||||
|
// check-pass
|
||||||
|
|
||||||
|
trait Foo {
|
||||||
|
type Bar: Bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Bar: Baz {}
|
||||||
|
|
||||||
|
trait Baz {}
|
||||||
|
|
||||||
|
fn main() {}
|
@ -1,9 +1,5 @@
|
|||||||
// known-bug: unknown
|
// known-bug: unknown
|
||||||
// compile-flags: -Ztrait-solver=next
|
// compile-flags: -Ztrait-solver=next
|
||||||
// failure-status: 101
|
|
||||||
// normalize-stderr-test "note: .*\n\n" -> ""
|
|
||||||
// normalize-stderr-test "thread 'rustc' panicked.*\n" -> ""
|
|
||||||
// rustc-env:RUST_BACKTRACE=0
|
|
||||||
|
|
||||||
// This tests checks that we update results in the provisional cache when
|
// This tests checks that we update results in the provisional cache when
|
||||||
// we pop a goal from the stack.
|
// we pop a goal from the stack.
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
error: the compiler unexpectedly panicked. this is a bug.
|
error[E0283]: type annotations needed: cannot satisfy `Bar<T>: Coinductive`
|
||||||
|
--> $DIR/provisional-result-done.rs:16:25
|
||||||
|
|
|
||||||
|
LL | impl<T> Coinductive for Bar<T>
|
||||||
|
| ^^^^^^
|
||||||
|
|
|
||||||
|
= note: cannot satisfy `Bar<T>: Coinductive`
|
||||||
|
|
||||||
query stack during panic:
|
error: aborting due to previous error
|
||||||
#0 [check_well_formed] checking that `<impl at $DIR/provisional-result-done.rs:20:1: 20:31>` is well-formed
|
|
||||||
#1 [check_mod_type_wf] checking that types are well-formed in top-level module
|
For more information about this error, try `rustc --explain E0283`.
|
||||||
end of query stack
|
|
||||||
|
22
tests/ui/traits/new-solver/temporary-ambiguity.rs
Normal file
22
tests/ui/traits/new-solver/temporary-ambiguity.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// compile-flags: -Ztrait-solver=next
|
||||||
|
// check-pass
|
||||||
|
|
||||||
|
// Checks that we don't explode when we assemble >1 candidate for a goal.
|
||||||
|
|
||||||
|
struct Wrapper<T>(T);
|
||||||
|
|
||||||
|
trait Foo {}
|
||||||
|
|
||||||
|
impl Foo for Wrapper<i32> {}
|
||||||
|
|
||||||
|
impl Foo for Wrapper<()> {}
|
||||||
|
|
||||||
|
fn needs_foo(_: impl Foo) {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut x = Default::default();
|
||||||
|
let w = Wrapper(x);
|
||||||
|
needs_foo(w);
|
||||||
|
x = 1;
|
||||||
|
drop(x);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user