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.

335 lines
13 KiB
Rust
Raw Normal View History

2023-07-23 12:30:52 -07:00
//! The next-generation trait solver, currently still WIP.
//!
2023-12-14 13:11:28 +01:00
//! As a user of rust, you can use `-Znext-solver` to enable the new trait solver.
2023-07-23 12:30:52 -07:00
//!
//! As a developer of rustc, you shouldn't be using the new trait
2023-07-23 12:30:52 -07:00
//! solver without asking the trait-system-refactor-initiative, but it can
//! be enabled with `InferCtxtBuilder::with_next_trait_solver`. This will
//! ensure that trait solving using that inference context will be routed
//! to the new trait 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};
2024-05-18 10:10:40 -04:00
use rustc_infer::infer::InferCtxt;
use rustc_infer::traits::query::NoSolution;
use rustc_macros::extension;
use rustc_middle::bug;
use rustc_middle::infer::canonical::CanonicalVarInfos;
2023-02-15 02:08:05 +00:00
use rustc_middle::traits::solve::{
CanonicalResponse, Certainty, ExternalConstraintsData, Goal, GoalSource, QueryResult, Response,
2023-02-15 02:08:05 +00:00
};
2023-01-20 03:05:06 +00:00
use rustc_middle::ty::{
self, AliasRelationDirection, CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, Ty,
TyCtxt, TypeOutlivesPredicate, UniverseIndex,
2023-01-20 03:05:06 +00:00
};
2023-05-27 19:05:09 +00:00
mod alias_relate;
2022-12-19 07:01:38 +00:00
mod assembly;
mod eval_ctxt;
mod fulfill;
2023-06-08 19:10:07 +01:00
pub mod inspect;
mod normalize;
2023-12-07 18:20:27 +01:00
mod normalizes_to;
mod project_goals;
2023-01-17 10:21:30 +01:00
mod search_graph;
mod trait_goals;
pub use eval_ctxt::{EvalCtxt, GenerateProofTree, InferCtxtEvalExt, InferCtxtSelectExt};
pub use fulfill::FulfillmentCtxt;
pub(crate) use normalize::deeply_normalize_for_diagnostics;
pub use normalize::{deeply_normalize, deeply_normalize_with_skipped_universes};
/// How many fixpoint iterations we should attempt inside of the solver before bailing
/// with overflow.
///
/// We previously used `tcx.recursion_limit().0.checked_ilog2().unwrap_or(0)` for this.
/// However, it feels unlikely that uncreasing the recursion limit by a power of two
/// to get one more itereation is every useful or desirable. We now instead used a constant
/// here. If there ever ends up some use-cases where a bigger number of fixpoint iterations
/// is required, we can add a new attribute for that or revert this to be dependant on the
/// recursion limit again. However, this feels very unlikely.
const FIXPOINT_STEP_LIMIT: usize = 8;
2023-03-21 16:26:23 +01: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-09-14 15:10:45 +02:00
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum GoalEvaluationKind {
Root,
2024-03-18 17:00:37 +01:00
Nested,
2023-09-14 15:10:45 +02:00
}
2024-02-14 17:18:56 +00:00
#[extension(trait CanonicalResponseExt)]
2024-05-17 12:16:36 -04:00
impl<'tcx> Canonical<'tcx, Response<TyCtxt<'tcx>>> {
2023-02-10 14:54:50 +00:00
fn has_no_inference_or_external_constraints(&self) -> bool {
2023-02-20 12:37:28 +01: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()
}
}
2024-05-18 10:10:40 -04:00
impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
#[instrument(level = "trace", skip(self))]
fn compute_type_outlives_goal(
&mut self,
2023-02-20 12:37:28 +01:00
goal: Goal<'tcx, TypeOutlivesPredicate<'tcx>>,
) -> QueryResult<'tcx> {
2023-02-20 12:37:28 +01: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 = "trace", skip(self))]
fn compute_region_outlives_goal(
&mut self,
2023-02-20 12:37:28 +01: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 = "trace", 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 = "trace", 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
}
fn compute_object_safe_goal(&mut self, trait_def_id: DefId) -> QueryResult<'tcx> {
if self.interner().check_is_object_safe(trait_def_id) {
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
} else {
Err(NoSolution)
}
}
#[instrument(level = "trace", 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) => {
2023-12-18 07:49:46 +01:00
self.add_goals(GoalSource::Misc, 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 = "trace", skip(self))]
fn compute_const_evaluatable_goal(
&mut self,
Goal { param_env, predicate: ct }: Goal<'tcx, ty::Const<'tcx>>,
) -> QueryResult<'tcx> {
match ct.kind() {
ty::ConstKind::Unevaluated(uv) => {
// We never return `NoSolution` here as `try_const_eval_resolve` emits an
// error itself when failing to evaluate, so emitting an additional fulfillment
// error in that case is unnecessary noise. This may change in the future once
// evaluation failures are allowed to impact selection, e.g. generic const
// expressions in impl headers or `where`-clauses.
// FIXME(generic_const_exprs): Implement handling for generic
// const expressions here.
if let Some(_normalized) = self.try_const_eval_resolve(param_env, uv, ct.ty()) {
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
} else {
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
}
}
ty::ConstKind::Infer(_) => {
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
}
ty::ConstKind::Placeholder(_) | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) => {
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
// We can freely ICE here as:
// - `Param` gets replaced with a placeholder during canonicalization
// - `Bound` cannot exist as we don't have a binder around the self Type
// - `Expr` is part of `feature(generic_const_exprs)` and is not implemented yet
ty::ConstKind::Param(_) | ty::ConstKind::Bound(_, _) | ty::ConstKind::Expr(_) => {
bug!("unexpect const kind: {:?}", ct)
}
}
}
#[instrument(level = "trace", skip(self), ret)]
2023-02-17 09:32:33 +00:00
fn compute_const_arg_has_type_goal(
&mut self,
goal: Goal<'tcx, (ty::Const<'tcx>, Ty<'tcx>)>,
) -> QueryResult<'tcx> {
let (ct, ty) = goal.predicate;
2024-05-29 17:06:50 +01:00
// FIXME(BoxyUwU): Really we should not be calling `ct.ty()` for any variant
// other than `ConstKind::Value`. Unfortunately this would require looking in the
// env for any `ConstArgHasType` assumptions for parameters and placeholders. I
// have not yet gotten around to implementing this though.
//
// We do still stall on infer vars though as otherwise a goal like:
// `ConstArgHasType(?x: usize, usize)` can succeed even though it might later
// get unified with some const that is not of type `usize`.
match ct.kind() {
// FIXME: Ignore effect vars because canonicalization doesn't handle them correctly
// and if we stall on the var then we wind up creating ambiguity errors in a probe
// for this goal which contains an effect var. Which then ends up ICEing.
ty::ConstKind::Infer(ty::InferConst::Var(_)) => {
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
}
ty::ConstKind::Error(_) => {
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
_ => {
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
}
}
2024-05-18 10:10:40 -04:00
impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
#[instrument(level = "trace", skip(self, goals))]
2023-12-18 07:49:46 +01:00
fn add_goals(
&mut self,
source: GoalSource,
goals: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
) {
for goal in goals {
2023-12-18 07:49:46 +01:00
self.add_goal(source, goal);
}
}
2023-03-29 15:36:17 +02:00
/// Try to merge multiple possible ways to prove a goal, if that is not possible returns `None`.
///
/// In this case we tend to flounder and return ambiguity by calling `[EvalCtxt::flounder]`.
#[instrument(level = "trace", skip(self), ret)]
2023-02-10 14:54:50 +00:00
fn try_merge_responses(
&mut self,
2023-03-29 15:36:17 +02:00
responses: &[CanonicalResponse<'tcx>],
) -> Option<CanonicalResponse<'tcx>> {
if responses.is_empty() {
return None;
2023-02-10 14:54:50 +00:00
}
2023-12-14 13:11:28 +01:00
// FIXME(-Znext-solver): 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.
2023-03-29 15:36:17 +02:00
let one = responses[0];
if responses[1..].iter().all(|&resp| resp == one) {
return Some(one);
2023-02-10 14:54:50 +00:00
}
2023-03-29 15:36:17 +02:00
responses
.iter()
.find(|response| {
response.value.certainty == Certainty::Yes
&& response.has_no_inference_or_external_constraints()
})
.copied()
}
2023-02-10 14:54:50 +00:00
2023-03-29 15:36:17 +02:00
/// If we fail to merge responses we flounder and return overflow or ambiguity.
#[instrument(level = "trace", skip(self), ret)]
2023-03-29 15:36:17 +02:00
fn flounder(&mut self, responses: &[CanonicalResponse<'tcx>]) -> QueryResult<'tcx> {
if responses.is_empty() {
return Err(NoSolution);
}
let Certainty::Maybe(maybe_cause) =
responses.iter().fold(Certainty::AMBIGUOUS, |certainty, response| {
certainty.unify_with(response.value.certainty)
})
else {
bug!("expected flounder response to be ambiguous")
};
Ok(self.make_ambiguous_response_no_constraints(maybe_cause))
2023-02-10 14:54:50 +00:00
}
/// Normalize a type for when it is structurally matched on.
///
/// This function is necessary in nearly all cases before matching on a type.
/// Not doing so is likely to be incomplete and therefore unsound during
/// coherence.
#[instrument(level = "trace", skip(self, param_env), ret)]
fn structurally_normalize_ty(
&mut self,
param_env: ty::ParamEnv<'tcx>,
ty: Ty<'tcx>,
) -> Result<Ty<'tcx>, NoSolution> {
if let ty::Alias(..) = ty.kind() {
let normalized_ty = self.next_ty_infer();
let alias_relate_goal = Goal::new(
self.interner(),
param_env,
ty::PredicateKind::AliasRelate(
ty.into(),
normalized_ty.into(),
AliasRelationDirection::Equate,
),
);
self.add_goal(GoalSource::Misc, alias_relate_goal);
self.try_evaluate_added_goals()?;
Ok(self.resolve_vars_if_possible(normalized_ty))
} else {
Ok(ty)
}
}
}
fn response_no_constraints_raw<'tcx>(
2023-01-17 10:21:30 +01:00
tcx: TyCtxt<'tcx>,
max_universe: UniverseIndex,
variables: CanonicalVarInfos<'tcx>,
2023-01-17 10:21:30 +01:00
certainty: Certainty,
) -> CanonicalResponse<'tcx> {
Canonical {
max_universe,
variables,
2023-01-17 10:21:30 +01:00
value: Response {
var_values: CanonicalVarValues::make_identity(tcx, 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 14:33:08 +11:00
external_constraints: tcx.mk_external_constraints(ExternalConstraintsData::default()),
2023-01-17 10:21:30 +01:00
certainty,
},
defining_opaque_types: Default::default(),
}
2023-01-17 10:21:30 +01:00
}