Rollup merge of #105661 - lcnr:evaluate-new, r=compiler-errors
implement the skeleton of the updated trait solver cc ```@rust-lang/initiative-trait-system-refactor``` This is mostly following the architecture discussed in the types team meetup. After discussing the desired changes for the trait solver, we encountered cyclic dependencies between them. Most notably between changing evaluate to be canonical and returning inference constraints. We cannot canonicalize evaluate without returning inference constraints due to coinductive cycles. However, caching inference constraints also relies on canonicalization. Implementing both of these changes at once in-place is not feasible. This somewhat closely mirrors the current `evaluate` implementation with the following notable differences: - it moves `project` into the core solver, allowing us to correctly deal with coinductive projections (will be required for implied bounds, perfect derive) - it changes trait solver overflow to be non-fatal (required to backcompat breakage from changes to the iteration order of nested goals, deferred projection equality, generally very useful) - it returns inference constraints and canonicalizes inputs and outputs (required for a lot things, most notably merging fulfill and evaluate, and deferred projection equality) - it is implemented to work with lazy normalization A lot of things aren't yet implemented, but the remaining FIXMEs should all be fairly self-contained and parallelizable. If the architecture looks correct and is what we want here, I would like to quickly merge this and then split the work. r? ```@compiler-errors``` / ```@rust-lang/types``` :3
This commit is contained in:
@@ -25,7 +25,6 @@ use rustc_data_structures::sso::SsoHashSet;
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_infer::infer::at::At;
|
||||
use rustc_infer::infer::resolve::OpportunisticRegionResolver;
|
||||
@@ -1553,7 +1552,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
|
||||
// NOTE: This should be kept in sync with the similar code in
|
||||
// `rustc_ty_utils::instance::resolve_associated_item()`.
|
||||
let node_item =
|
||||
assoc_def(selcx, impl_data.impl_def_id, obligation.predicate.def_id)
|
||||
specialization_graph::assoc_def(selcx.tcx(), impl_data.impl_def_id, obligation.predicate.def_id)
|
||||
.map_err(|ErrorGuaranteed { .. }| ())?;
|
||||
|
||||
if node_item.is_final() {
|
||||
@@ -2113,7 +2112,7 @@ fn confirm_impl_candidate<'cx, 'tcx>(
|
||||
let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap();
|
||||
|
||||
let param_env = obligation.param_env;
|
||||
let Ok(assoc_ty) = assoc_def(selcx, impl_def_id, assoc_item_id) else {
|
||||
let Ok(assoc_ty) = specialization_graph::assoc_def(tcx, impl_def_id, assoc_item_id) else {
|
||||
return Progress { term: tcx.ty_error().into(), obligations: nested };
|
||||
};
|
||||
|
||||
@@ -2210,7 +2209,7 @@ fn confirm_impl_trait_in_trait_candidate<'tcx>(
|
||||
let mut obligations = data.nested;
|
||||
|
||||
let trait_fn_def_id = tcx.impl_trait_in_trait_parent(obligation.predicate.def_id);
|
||||
let Ok(leaf_def) = assoc_def(selcx, data.impl_def_id, trait_fn_def_id) else {
|
||||
let Ok(leaf_def) = specialization_graph::assoc_def(tcx, data.impl_def_id, trait_fn_def_id) else {
|
||||
return Progress { term: tcx.ty_error().into(), obligations };
|
||||
};
|
||||
if !leaf_def.item.defaultness(tcx).has_value() {
|
||||
@@ -2347,58 +2346,6 @@ fn assoc_ty_own_obligations<'cx, 'tcx>(
|
||||
}
|
||||
}
|
||||
|
||||
/// Locate the definition of an associated type in the specialization hierarchy,
|
||||
/// starting from the given impl.
|
||||
///
|
||||
/// Based on the "projection mode", this lookup may in fact only examine the
|
||||
/// topmost impl. See the comments for `Reveal` for more details.
|
||||
fn assoc_def(
|
||||
selcx: &SelectionContext<'_, '_>,
|
||||
impl_def_id: DefId,
|
||||
assoc_def_id: DefId,
|
||||
) -> Result<specialization_graph::LeafDef, ErrorGuaranteed> {
|
||||
let tcx = selcx.tcx();
|
||||
let trait_def_id = tcx.impl_trait_ref(impl_def_id).unwrap().def_id;
|
||||
let trait_def = tcx.trait_def(trait_def_id);
|
||||
|
||||
// This function may be called while we are still building the
|
||||
// specialization graph that is queried below (via TraitDef::ancestors()),
|
||||
// so, in order to avoid unnecessary infinite recursion, we manually look
|
||||
// for the associated item at the given impl.
|
||||
// If there is no such item in that impl, this function will fail with a
|
||||
// cycle error if the specialization graph is currently being built.
|
||||
if let Some(&impl_item_id) = tcx.impl_item_implementor_ids(impl_def_id).get(&assoc_def_id) {
|
||||
let item = tcx.associated_item(impl_item_id);
|
||||
let impl_node = specialization_graph::Node::Impl(impl_def_id);
|
||||
return Ok(specialization_graph::LeafDef {
|
||||
item: *item,
|
||||
defining_node: impl_node,
|
||||
finalizing_node: if item.defaultness(tcx).is_default() {
|
||||
None
|
||||
} else {
|
||||
Some(impl_node)
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
let ancestors = trait_def.ancestors(tcx, impl_def_id)?;
|
||||
if let Some(assoc_item) = ancestors.leaf_def(tcx, assoc_def_id) {
|
||||
Ok(assoc_item)
|
||||
} else {
|
||||
// This is saying that neither the trait nor
|
||||
// the impl contain a definition for this
|
||||
// associated type. Normally this situation
|
||||
// could only arise through a compiler bug --
|
||||
// if the user wrote a bad item name, it
|
||||
// should have failed in astconv.
|
||||
bug!(
|
||||
"No associated type `{}` for {}",
|
||||
tcx.item_name(assoc_def_id),
|
||||
tcx.def_path_str(impl_def_id)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait ProjectionCacheKeyExt<'cx, 'tcx>: Sized {
|
||||
fn from_poly_projection_predicate(
|
||||
selcx: &mut SelectionContext<'cx, 'tcx>,
|
||||
|
||||
@@ -1171,19 +1171,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
where
|
||||
I: Iterator<Item = ty::Predicate<'tcx>>,
|
||||
{
|
||||
cycle.all(|predicate| self.coinductive_predicate(predicate))
|
||||
}
|
||||
|
||||
fn coinductive_predicate(&self, predicate: ty::Predicate<'tcx>) -> bool {
|
||||
let result = match predicate.kind().skip_binder() {
|
||||
ty::PredicateKind::Clause(ty::Clause::Trait(ref data)) => {
|
||||
self.tcx().trait_is_coinductive(data.def_id())
|
||||
}
|
||||
ty::PredicateKind::WellFormed(_) => true,
|
||||
_ => false,
|
||||
};
|
||||
debug!(?predicate, ?result, "coinductive_predicate");
|
||||
result
|
||||
cycle.all(|predicate| predicate.is_coinductive(self.tcx()))
|
||||
}
|
||||
|
||||
/// Further evaluates `candidate` to decide whether all type parameters match and whether nested
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use super::OverlapError;
|
||||
|
||||
use crate::traits;
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::ty::fast_reject::{self, SimplifiedType, TreatParams};
|
||||
use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
|
||||
@@ -379,3 +380,51 @@ impl<'tcx> GraphExt<'tcx> for Graph {
|
||||
self.children.entry(parent).or_default().insert_blindly(tcx, child);
|
||||
}
|
||||
}
|
||||
|
||||
/// Locate the definition of an associated type in the specialization hierarchy,
|
||||
/// starting from the given impl.
|
||||
pub(crate) fn assoc_def(
|
||||
tcx: TyCtxt<'_>,
|
||||
impl_def_id: DefId,
|
||||
assoc_def_id: DefId,
|
||||
) -> Result<LeafDef, ErrorGuaranteed> {
|
||||
let trait_def_id = tcx.impl_trait_ref(impl_def_id).unwrap().def_id;
|
||||
let trait_def = tcx.trait_def(trait_def_id);
|
||||
|
||||
// This function may be called while we are still building the
|
||||
// specialization graph that is queried below (via TraitDef::ancestors()),
|
||||
// so, in order to avoid unnecessary infinite recursion, we manually look
|
||||
// for the associated item at the given impl.
|
||||
// If there is no such item in that impl, this function will fail with a
|
||||
// cycle error if the specialization graph is currently being built.
|
||||
if let Some(&impl_item_id) = tcx.impl_item_implementor_ids(impl_def_id).get(&assoc_def_id) {
|
||||
let &item = tcx.associated_item(impl_item_id);
|
||||
let impl_node = Node::Impl(impl_def_id);
|
||||
return Ok(LeafDef {
|
||||
item,
|
||||
defining_node: impl_node,
|
||||
finalizing_node: if item.defaultness(tcx).is_default() {
|
||||
None
|
||||
} else {
|
||||
Some(impl_node)
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
let ancestors = trait_def.ancestors(tcx, impl_def_id)?;
|
||||
if let Some(assoc_item) = ancestors.leaf_def(tcx, assoc_def_id) {
|
||||
Ok(assoc_item)
|
||||
} else {
|
||||
// This is saying that neither the trait nor
|
||||
// the impl contain a definition for this
|
||||
// associated type. Normally this situation
|
||||
// could only arise through a compiler bug --
|
||||
// if the user wrote a bad item name, it
|
||||
// should have failed in astconv.
|
||||
bug!(
|
||||
"No associated type `{}` for {}",
|
||||
tcx.item_name(assoc_def_id),
|
||||
tcx.def_path_str(impl_def_id)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user