rust/compiler/rustc_trait_selection/src/solve/mod.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

333 lines
12 KiB
Rust
Raw Normal View History

//! The new trait solver, currently still WIP.
//!
//! As a user of the trait system, you can use `TyCtxt::evaluate_goal` to
//! interact with this solver.
//!
//! For a high-level overview of how this solver works, check out the relevant
//! section of the rustc-dev-guide.
//!
//! FIXME(@lcnr): Write that section. If you read this before then ask me
//! about it on zulip.
2023-01-20 18:38:33 +00:00
use rustc_hir::def_id::DefId;
use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues};
use rustc_infer::traits::query::NoSolution;
2023-02-15 02:08:05 +00:00
use rustc_middle::traits::solve::{
2023-03-24 17:27:06 +00:00
CanonicalResponse, Certainty, ExternalConstraintsData, Goal, QueryResult, Response,
2023-02-15 02:08:05 +00:00
};
2023-02-17 09:32:33 +00:00
use rustc_middle::ty::{self, Ty, TyCtxt};
2023-01-20 03:05:06 +00:00
use rustc_middle::ty::{
2023-02-15 02:08:05 +00:00
CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate,
2023-01-20 03:05:06 +00:00
};
2022-12-19 07:01:38 +00:00
mod assembly;
2023-03-24 17:27:06 +00:00
mod canonicalize;
mod eval_ctxt;
mod fulfill;
mod project_goals;
2023-01-17 09:21:30 +00:00
mod search_graph;
mod trait_goals;
pub use eval_ctxt::{EvalCtxt, InferCtxtEvalExt};
pub use fulfill::FulfillmentCtxt;
2023-03-21 15:26:23 +00:00
#[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,
}
2023-02-10 14:54:50 +00:00
trait CanonicalResponseExt {
fn has_no_inference_or_external_constraints(&self) -> bool;
}
impl<'tcx> CanonicalResponseExt for Canonical<'tcx, Response<'tcx>> {
fn has_no_inference_or_external_constraints(&self) -> bool {
2023-02-20 11:37:28 +00:00
self.value.external_constraints.region_constraints.is_empty()
&& self.value.var_values.is_identity()
2023-02-10 14:54:50 +00:00
&& self.value.external_constraints.opaque_types.is_empty()
}
}
2023-01-17 09:21:30 +00:00
impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
#[instrument(level = "debug", skip(self))]
fn compute_type_outlives_goal(
&mut self,
2023-02-20 11:37:28 +00:00
goal: Goal<'tcx, TypeOutlivesPredicate<'tcx>>,
) -> QueryResult<'tcx> {
2023-02-20 11:37:28 +00:00
let ty::OutlivesPredicate(ty, lt) = goal.predicate;
2023-03-23 05:40:50 +00:00
self.register_ty_outlives(ty, lt);
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
#[instrument(level = "debug", skip(self))]
fn compute_region_outlives_goal(
&mut self,
2023-02-20 11:37:28 +00:00
goal: Goal<'tcx, RegionOutlivesPredicate<'tcx>>,
) -> QueryResult<'tcx> {
2023-03-23 05:40:50 +00:00
let ty::OutlivesPredicate(a, b) = goal.predicate;
self.register_region_outlives(a, b);
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
2023-01-20 03:05:06 +00:00
#[instrument(level = "debug", skip(self))]
2023-01-20 18:38:33 +00:00
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,
},
})
}
#[instrument(level = "debug", skip(self))]
2023-01-20 03:05:06 +00:00
fn compute_subtype_goal(
&mut self,
goal: Goal<'tcx, SubtypePredicate<'tcx>>,
) -> QueryResult<'tcx> {
2023-01-20 18:38:33 +00:00
if goal.predicate.a.is_ty_var() && goal.predicate.b.is_ty_var() {
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
2023-01-20 18:38:33 +00:00
} else {
self.sub(goal.param_env, goal.predicate.a, goal.predicate.b)?;
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
2023-01-20 18:38:33 +00:00
}
2023-01-20 03:05:06 +00:00
}
#[instrument(level = "debug", skip(self))]
2023-01-20 03:05:06 +00:00
fn compute_closure_kind_goal(
&mut self,
2023-01-20 18:38:33 +00:00
goal: Goal<'tcx, (DefId, ty::SubstsRef<'tcx>, ty::ClosureKind)>,
2023-01-20 03:05:06 +00:00
) -> QueryResult<'tcx> {
2023-01-20 18:38:33 +00:00
let (_, substs, expected_kind) = goal.predicate;
let found_kind = substs.as_closure().kind_ty().to_opt_closure_kind();
2023-01-20 03:05:06 +00:00
let Some(found_kind) = found_kind else {
return self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
2023-01-20 03:05:06 +00:00
};
if found_kind.extends(expected_kind) {
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
2023-01-20 03:05:06 +00:00
} else {
Err(NoSolution)
}
}
#[instrument(level = "debug", skip(self))]
fn compute_object_safe_goal(&mut self, trait_def_id: DefId) -> QueryResult<'tcx> {
if self.tcx().check_is_object_safe(trait_def_id) {
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
} else {
Err(NoSolution)
}
}
#[instrument(level = "debug", skip(self))]
fn compute_well_formed_goal(
&mut self,
goal: Goal<'tcx, ty::GenericArg<'tcx>>,
) -> QueryResult<'tcx> {
2023-03-23 05:40:50 +00:00
match self.well_formed_goals(goal.param_env, goal.predicate) {
Some(goals) => {
self.add_goals(goals);
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
None => self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS),
2023-01-27 04:00:37 +00:00
}
}
2023-02-10 14:54:50 +00:00
#[instrument(level = "debug", skip(self), ret)]
2023-03-21 22:11:40 +00:00
fn compute_alias_relate_goal(
2023-02-10 14:54:50 +00:00
&mut self,
goal: Goal<'tcx, (ty::Term<'tcx>, ty::Term<'tcx>, ty::AliasRelationDirection)>,
2023-02-10 14:54:50 +00:00
) -> QueryResult<'tcx> {
let tcx = self.tcx();
// We may need to invert the alias relation direction if dealing an alias on the RHS.
enum Invert {
No,
Yes,
}
let evaluate_normalizes_to =
|ecx: &mut EvalCtxt<'_, 'tcx>, alias, other, direction, invert| {
debug!("evaluate_normalizes_to(alias={:?}, other={:?})", alias, other);
let result = ecx.probe(|ecx| {
let other = match direction {
// This is purely an optimization.
ty::AliasRelationDirection::Equate => other,
2023-02-10 14:54:50 +00:00
ty::AliasRelationDirection::Subtype => {
let fresh = ecx.next_term_infer_of_kind(other);
let (sub, sup) = match invert {
Invert::No => (fresh, other),
Invert::Yes => (other, fresh),
};
ecx.sub(goal.param_env, sub, sup)?;
fresh
}
};
ecx.add_goal(goal.with(
tcx,
ty::Binder::dummy(ty::ProjectionPredicate {
projection_ty: alias,
term: other,
}),
));
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
});
debug!("evaluate_normalizes_to({alias}, {other}, {direction:?}) -> {result:?}");
result
};
2023-02-10 14:54:50 +00:00
let (lhs, rhs, direction) = goal.predicate;
if lhs.is_infer() || rhs.is_infer() {
2023-02-10 14:54:50 +00:00
bug!(
2023-03-21 22:11:40 +00:00
"`AliasRelate` goal with an infer var on lhs or rhs which should have been instantiated"
2023-02-10 14:54:50 +00:00
);
}
match (lhs.to_projection_term(tcx), rhs.to_projection_term(tcx)) {
2023-03-21 22:11:40 +00:00
(None, None) => bug!("`AliasRelate` goal without an alias on either lhs or rhs"),
// RHS is not a projection, only way this is true is if LHS normalizes-to RHS
(Some(alias_lhs), None) => {
evaluate_normalizes_to(self, alias_lhs, rhs, direction, Invert::No)
}
// LHS is not a projection, only way this is true is if RHS normalizes-to LHS
(None, Some(alias_rhs)) => {
evaluate_normalizes_to(self, alias_rhs, lhs, direction, Invert::Yes)
}
2023-02-10 14:54:50 +00:00
(Some(alias_lhs), Some(alias_rhs)) => {
2023-03-21 22:11:40 +00:00
debug!("compute_alias_relate_goal: both sides are aliases");
2023-02-10 14:54:50 +00:00
let candidates = vec![
// LHS normalizes-to RHS
evaluate_normalizes_to(self, alias_lhs, rhs, direction, Invert::No),
// RHS normalizes-to RHS
evaluate_normalizes_to(self, alias_rhs, lhs, direction, Invert::Yes),
// Relate via substs
self.probe(|ecx| {
2023-03-21 22:11:40 +00:00
debug!(
"compute_alias_relate_goal: alias defids are equal, equating substs"
);
2023-02-10 14:54:50 +00:00
match direction {
ty::AliasRelationDirection::Equate => {
ecx.eq(goal.param_env, alias_lhs, alias_rhs)?;
}
ty::AliasRelationDirection::Subtype => {
ecx.sub(goal.param_env, alias_lhs, alias_rhs)?;
}
}
2023-02-10 14:54:50 +00:00
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}),
];
2023-02-10 14:54:50 +00:00
debug!(?candidates);
self.try_merge_responses(candidates.into_iter())
}
}
}
2023-02-17 09:32:33 +00:00
#[instrument(level = "debug", skip(self), ret)]
fn compute_const_arg_has_type_goal(
&mut self,
goal: Goal<'tcx, (ty::Const<'tcx>, Ty<'tcx>)>,
) -> QueryResult<'tcx> {
let (ct, ty) = goal.predicate;
self.eq(goal.param_env, ct.ty(), ty)?;
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
2023-02-17 09:32:33 +00:00
}
}
2023-01-17 09:21:30 +00:00
impl<'tcx> EvalCtxt<'_, 'tcx> {
#[instrument(level = "debug", skip(self))]
2023-03-17 14:04:39 +00:00
fn set_normalizes_to_hack_goal(&mut self, goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>) {
assert!(
2023-03-17 14:04:39 +00:00
self.nested_goals.normalizes_to_hack_goal.is_none(),
"attempted to set the projection eq hack goal when one already exists"
);
2023-03-17 14:04:39 +00:00
self.nested_goals.normalizes_to_hack_goal = Some(goal);
}
#[instrument(level = "debug", skip(self))]
fn add_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) {
self.nested_goals.goals.push(goal);
}
#[instrument(level = "debug", skip(self, goals))]
fn add_goals(&mut self, goals: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>) {
let current_len = self.nested_goals.goals.len();
self.nested_goals.goals.extend(goals);
debug!("added_goals={:?}", &self.nested_goals.goals[current_len..]);
}
2023-02-10 14:54:50 +00:00
fn try_merge_responses(
&mut self,
responses: impl Iterator<Item = QueryResult<'tcx>>,
) -> QueryResult<'tcx> {
let candidates = responses.into_iter().flatten().collect::<Box<[_]>>();
if candidates.is_empty() {
return Err(NoSolution);
}
2023-03-21 15:26:23 +00:00
// FIXME(-Ztrait-solver=next): We should instead try to find a `Certainty::Yes` response with
2023-02-10 14:54:50 +00:00
// a subset of the constraints that all the other responses have.
let one = candidates[0];
if candidates[1..].iter().all(|resp| resp == &one) {
return Ok(one);
}
if let Some(response) = candidates.iter().find(|response| {
response.value.certainty == Certainty::Yes
&& response.has_no_inference_or_external_constraints()
}) {
2023-02-15 22:18:40 +00:00
return Ok(*response);
2023-02-10 14:54:50 +00:00
}
let certainty = candidates.iter().fold(Certainty::AMBIGUOUS, |certainty, response| {
certainty.unify_and(response.value.certainty)
});
// FIXME(-Ztrait-solver=next): We should take the intersection of the constraints on all the
// responses and use that for the constraints of this ambiguous response.
let response = self.evaluate_added_goals_and_make_canonical_response(certainty);
2023-02-10 14:54:50 +00:00
if let Ok(response) = &response {
assert!(response.has_no_inference_or_external_constraints());
}
response
}
}
2023-01-17 09:21:30 +00:00
pub(super) fn response_no_constraints<'tcx>(
tcx: TyCtxt<'tcx>,
goal: Canonical<'tcx, impl Sized>,
certainty: Certainty,
) -> QueryResult<'tcx> {
Ok(Canonical {
max_universe: goal.max_universe,
variables: goal.variables,
value: Response {
var_values: CanonicalVarValues::make_identity(tcx, goal.variables),
2023-02-03 02:29:52 +00:00
// FIXME: maybe we should store the "no response" version in tcx, like
// we do for tcx.types and stuff.
Rename many interner functions. (This is a large commit. The changes to `compiler/rustc_middle/src/ty/context.rs` are the most important ones.) The current naming scheme is a mess, with a mix of `_intern_`, `intern_` and `mk_` prefixes, with little consistency. In particular, in many cases it's easy to use an iterator interner when a (preferable) slice interner is available. The guiding principles of the new naming system: - No `_intern_` prefixes. - The `intern_` prefix is for internal operations. - The `mk_` prefix is for external operations. - For cases where there is a slice interner and an iterator interner, the former is `mk_foo` and the latter is `mk_foo_from_iter`. Also, `slice_interners!` and `direct_interners!` can now be `pub` or non-`pub`, which helps enforce the internal/external operations division. It's not perfect, but I think it's a clear improvement. The following lists show everything that was renamed. slice_interners - const_list - mk_const_list -> mk_const_list_from_iter - intern_const_list -> mk_const_list - substs - mk_substs -> mk_substs_from_iter - intern_substs -> mk_substs - check_substs -> check_and_mk_substs (this is a weird one) - canonical_var_infos - intern_canonical_var_infos -> mk_canonical_var_infos - poly_existential_predicates - mk_poly_existential_predicates -> mk_poly_existential_predicates_from_iter - intern_poly_existential_predicates -> mk_poly_existential_predicates - _intern_poly_existential_predicates -> intern_poly_existential_predicates - predicates - mk_predicates -> mk_predicates_from_iter - intern_predicates -> mk_predicates - _intern_predicates -> intern_predicates - projs - intern_projs -> mk_projs - place_elems - mk_place_elems -> mk_place_elems_from_iter - intern_place_elems -> mk_place_elems - bound_variable_kinds - mk_bound_variable_kinds -> mk_bound_variable_kinds_from_iter - intern_bound_variable_kinds -> mk_bound_variable_kinds direct_interners - region - intern_region (unchanged) - const - mk_const_internal -> intern_const - const_allocation - intern_const_alloc -> mk_const_alloc - layout - intern_layout -> mk_layout - adt_def - intern_adt_def -> mk_adt_def_from_data (unusual case, hard to avoid) - alloc_adt_def(!) -> mk_adt_def - external_constraints - intern_external_constraints -> mk_external_constraints Other - type_list - mk_type_list -> mk_type_list_from_iter - intern_type_list -> mk_type_list - tup - mk_tup -> mk_tup_from_iter - intern_tup -> mk_tup
2023-02-17 03:33:08 +00:00
external_constraints: tcx.mk_external_constraints(ExternalConstraintsData::default()),
2023-01-17 09:21:30 +00:00
certainty,
},
})
}