1
Fork 0

Rollup merge of #107102 - compiler-errors:new-solver-new-candidats-4, r=lcnr

Implement some more predicates in the new solver

Implement a few more goals. The subtype goal specifically is important, since it's required for this code to compile:

```
fn main() {
  let mut x = vec![];
  x.push(1i32);
}
```

(I think we emit a subtype goal here because of coercion).

Drive-by: Also implements `--compare-mode=next-solver` -- I've been using this locally a lot to find out what works and what doesn't. I'm also happy to split this out into another PR.

r? `@lcnr`
This commit is contained in:
Matthias Krüger 2023-01-22 11:43:05 +01:00 committed by GitHub
commit d022013eb7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 91 additions and 18 deletions

View file

@ -1,7 +1,7 @@
//! Code shared by trait and projection goals for candidate assembly.
use super::infcx_ext::InferCtxtExt;
use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, MaybeCause, QueryResult};
use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, QueryResult};
use rustc_hir::def_id::DefId;
use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::util::elaborate_predicates;
@ -148,9 +148,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
if goal.predicate.self_ty().is_ty_var() {
return vec![Candidate {
source: CandidateSource::BuiltinImpl,
result: self
.make_canonical_response(Certainty::Maybe(MaybeCause::Ambiguity))
.unwrap(),
result: self.make_canonical_response(Certainty::AMBIGUOUS).unwrap(),
}];
}

View file

@ -19,6 +19,7 @@
use std::mem;
use rustc_hir::def_id::DefId;
use rustc_infer::infer::canonical::{Canonical, CanonicalVarKind, CanonicalVarValues};
use rustc_infer::infer::canonical::{OriginalQueryValues, QueryRegionConstraints, QueryResponse};
use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt};
@ -26,7 +27,9 @@ use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::Obligation;
use rustc_middle::infer::canonical::Certainty as OldCertainty;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::ty::{RegionOutlivesPredicate, ToPredicate, TypeOutlivesPredicate};
use rustc_middle::ty::{
CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, ToPredicate, TypeOutlivesPredicate,
};
use rustc_span::DUMMY_SP;
use crate::traits::ObligationCause;
@ -87,6 +90,8 @@ pub enum Certainty {
}
impl Certainty {
pub const AMBIGUOUS: Certainty = Certainty::Maybe(MaybeCause::Ambiguity);
/// When proving multiple goals using **AND**, e.g. nested obligations for an impl,
/// use this function to unify the certainty of these goals
pub fn unify_and(self, other: Certainty) -> Certainty {
@ -243,16 +248,28 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
ty::PredicateKind::Clause(ty::Clause::RegionOutlives(predicate)) => {
self.compute_region_outlives_goal(Goal { param_env, predicate })
}
ty::PredicateKind::Subtype(predicate) => {
self.compute_subtype_goal(Goal { param_env, predicate })
}
ty::PredicateKind::Coerce(predicate) => {
self.compute_coerce_goal(Goal { param_env, predicate })
}
ty::PredicateKind::ClosureKind(def_id, substs, kind) => self
.compute_closure_kind_goal(Goal {
param_env,
predicate: (def_id, substs, kind),
}),
ty::PredicateKind::Ambiguous => self.make_canonical_response(Certainty::AMBIGUOUS),
// FIXME: implement these predicates :)
ty::PredicateKind::WellFormed(_)
| ty::PredicateKind::ObjectSafe(_)
| ty::PredicateKind::ClosureKind(_, _, _)
| ty::PredicateKind::Subtype(_)
| ty::PredicateKind::Coerce(_)
| ty::PredicateKind::ConstEvaluatable(_)
| ty::PredicateKind::ConstEquate(_, _)
| ty::PredicateKind::TypeWellFormedFromEnv(_)
| ty::PredicateKind::Ambiguous => self.make_canonical_response(Certainty::Yes),
| ty::PredicateKind::ConstEquate(_, _) => {
self.make_canonical_response(Certainty::Yes)
}
ty::PredicateKind::TypeWellFormedFromEnv(..) => {
bug!("TypeWellFormedFromEnv is only used for Chalk")
}
}
} else {
let kind = self.infcx.replace_bound_vars_with_placeholders(kind);
@ -275,6 +292,58 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
) -> QueryResult<'tcx> {
self.make_canonical_response(Certainty::Yes)
}
fn compute_coerce_goal(
&mut self,
goal: Goal<'tcx, CoercePredicate<'tcx>>,
) -> QueryResult<'tcx> {
self.compute_subtype_goal(Goal {
param_env: goal.param_env,
predicate: SubtypePredicate {
a_is_expected: false,
a: goal.predicate.a,
b: goal.predicate.b,
},
})
}
fn compute_subtype_goal(
&mut self,
goal: Goal<'tcx, SubtypePredicate<'tcx>>,
) -> QueryResult<'tcx> {
if goal.predicate.a.is_ty_var() && goal.predicate.b.is_ty_var() {
// FIXME: Do we want to register a subtype relation between these vars?
// That won't actually reflect in the query response, so it seems moot.
self.make_canonical_response(Certainty::AMBIGUOUS)
} else {
self.infcx.probe(|_| {
let InferOk { value: (), obligations } = self
.infcx
.at(&ObligationCause::dummy(), goal.param_env)
.sub(goal.predicate.a, goal.predicate.b)?;
self.evaluate_all_and_make_canonical_response(
obligations.into_iter().map(|pred| pred.into()).collect(),
)
})
}
}
fn compute_closure_kind_goal(
&mut self,
goal: Goal<'tcx, (DefId, ty::SubstsRef<'tcx>, ty::ClosureKind)>,
) -> QueryResult<'tcx> {
let (_, substs, expected_kind) = goal.predicate;
let found_kind = substs.as_closure().kind_ty().to_opt_closure_kind();
let Some(found_kind) = found_kind else {
return self.make_canonical_response(Certainty::AMBIGUOUS);
};
if found_kind.extends(expected_kind) {
self.make_canonical_response(Certainty::Yes)
} else {
Err(NoSolution)
}
}
}
impl<'tcx> EvalCtxt<'_, 'tcx> {

View file

@ -3,7 +3,7 @@ use crate::traits::{specialization_graph, translate_substs};
use super::assembly::{self, Candidate, CandidateSource};
use super::infcx_ext::InferCtxtExt;
use super::trait_goals::structural_traits;
use super::{Certainty, EvalCtxt, Goal, MaybeCause, QueryResult};
use super::{Certainty, EvalCtxt, Goal, QueryResult};
use rustc_errors::ErrorGuaranteed;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
@ -229,8 +229,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
goal.predicate.def_id(),
impl_def_id
)? else {
let certainty = Certainty::Maybe(MaybeCause::Ambiguity);
return ecx.make_canonical_response(trait_ref_certainty.unify_and(certainty));
return ecx.make_canonical_response(trait_ref_certainty.unify_and(Certainty::AMBIGUOUS));
};
if !assoc_def.item.defaultness(tcx).has_value() {
@ -382,7 +381,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
.to_predicate(ecx.tcx());
Self::consider_assumption(ecx, goal, pred)
} else {
ecx.make_canonical_response(Certainty::Maybe(MaybeCause::Ambiguity))
ecx.make_canonical_response(Certainty::AMBIGUOUS)
}
}

View file

@ -4,7 +4,7 @@ use std::iter;
use super::assembly::{self, Candidate, CandidateSource};
use super::infcx_ext::InferCtxtExt;
use super::{Certainty, EvalCtxt, Goal, MaybeCause, QueryResult};
use super::{Certainty, EvalCtxt, Goal, QueryResult};
use rustc_hir::def_id::DefId;
use rustc_infer::infer::InferCtxt;
use rustc_infer::traits::query::NoSolution;
@ -133,7 +133,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
if goal.predicate.self_ty().has_non_region_infer() {
return ecx.make_canonical_response(Certainty::Maybe(MaybeCause::Ambiguity));
return ecx.make_canonical_response(Certainty::AMBIGUOUS);
}
let tcx = ecx.tcx();
@ -171,7 +171,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
.to_predicate(ecx.tcx());
Self::consider_assumption(ecx, goal, pred)
} else {
ecx.make_canonical_response(Certainty::Maybe(MaybeCause::Ambiguity))
ecx.make_canonical_response(Certainty::AMBIGUOUS)
}
}