1
Fork 0

Auto merge of #110031 - compiler-errors:generic-elaboration, r=b-naber

Make elaboration generic over input

Combines all the `elaborate_*` family of functions into just one, which is an iterator over the same type that you pass in (e.g. elaborating `Predicate` gives `Predicate`s, elaborating `Obligation`s gives `Obligation`s, etc.)
This commit is contained in:
bors 2023-04-09 00:18:10 +00:00
commit f8ed97ecc1
22 changed files with 163 additions and 164 deletions

View file

@ -1,7 +1,7 @@
use smallvec::smallvec;
use crate::infer::outlives::components::{push_outlives_components, Component};
use crate::traits::{self, Obligation, ObligationCause, PredicateObligation};
use crate::traits::{self, Obligation, PredicateObligation};
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
use rustc_middle::ty::{self, ToPredicate, TyCtxt};
use rustc_span::symbol::Ident;
@ -66,99 +66,129 @@ impl<'tcx> Extend<ty::Predicate<'tcx>> for PredicateSet<'tcx> {
/// if we know that `T: Ord`, the elaborator would deduce that `T: PartialOrd`
/// holds as well. Similarly, if we have `trait Foo: 'static`, and we know that
/// `T: Foo`, then we know that `T: 'static`.
pub struct Elaborator<'tcx> {
stack: Vec<PredicateObligation<'tcx>>,
pub struct Elaborator<'tcx, O> {
stack: Vec<O>,
visited: PredicateSet<'tcx>,
}
pub fn elaborate_trait_ref<'tcx>(
tcx: TyCtxt<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
) -> impl Iterator<Item = ty::Predicate<'tcx>> {
elaborate_predicates(tcx, std::iter::once(trait_ref.without_const().to_predicate(tcx)))
/// Describes how to elaborate an obligation into a sub-obligation.
///
/// For [`Obligation`], a sub-obligation is combined with the current obligation's
/// param-env and cause code. For [`ty::Predicate`], none of this is needed, since
/// there is no param-env or cause code to copy over.
pub trait Elaboratable<'tcx> {
fn predicate(&self) -> ty::Predicate<'tcx>;
// Makes a new `Self` but with a different predicate.
fn child(&self, predicate: ty::Predicate<'tcx>) -> Self;
// Makes a new `Self` but with a different predicate and a different cause
// code (if `Self` has one).
fn child_with_derived_cause(
&self,
predicate: ty::Predicate<'tcx>,
span: Span,
parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
index: usize,
) -> Self;
}
pub fn elaborate_trait_refs<'tcx>(
tcx: TyCtxt<'tcx>,
trait_refs: impl Iterator<Item = ty::PolyTraitRef<'tcx>>,
) -> impl Iterator<Item = ty::Predicate<'tcx>> {
let predicates = trait_refs.map(move |trait_ref| trait_ref.without_const().to_predicate(tcx));
elaborate_predicates(tcx, predicates)
impl<'tcx> Elaboratable<'tcx> for PredicateObligation<'tcx> {
fn predicate(&self) -> ty::Predicate<'tcx> {
self.predicate
}
fn child(&self, predicate: ty::Predicate<'tcx>) -> Self {
Obligation {
cause: self.cause.clone(),
param_env: self.param_env,
recursion_depth: 0,
predicate,
}
}
fn child_with_derived_cause(
&self,
predicate: ty::Predicate<'tcx>,
span: Span,
parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
index: usize,
) -> Self {
let cause = self.cause.clone().derived_cause(parent_trait_pred, |derived| {
traits::ImplDerivedObligation(Box::new(traits::ImplDerivedObligationCause {
derived,
impl_or_alias_def_id: parent_trait_pred.def_id(),
impl_def_predicate_index: Some(index),
span,
}))
});
Obligation { cause, param_env: self.param_env, recursion_depth: 0, predicate }
}
}
pub fn elaborate_predicates<'tcx>(
tcx: TyCtxt<'tcx>,
predicates: impl Iterator<Item = ty::Predicate<'tcx>>,
) -> impl Iterator<Item = ty::Predicate<'tcx>> {
elaborate_obligations(
tcx,
predicates
.map(|predicate| {
Obligation::new(
tcx,
// We'll dump the cause/param-env later
ObligationCause::dummy(),
ty::ParamEnv::empty(),
predicate,
)
})
.collect(),
)
.map(|obl| obl.predicate)
impl<'tcx> Elaboratable<'tcx> for ty::Predicate<'tcx> {
fn predicate(&self) -> ty::Predicate<'tcx> {
*self
}
fn child(&self, predicate: ty::Predicate<'tcx>) -> Self {
predicate
}
fn child_with_derived_cause(
&self,
predicate: ty::Predicate<'tcx>,
_span: Span,
_parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
_index: usize,
) -> Self {
predicate
}
}
pub fn elaborate_predicates_with_span<'tcx>(
tcx: TyCtxt<'tcx>,
predicates: impl Iterator<Item = (ty::Predicate<'tcx>, Span)>,
) -> impl Iterator<Item = (ty::Predicate<'tcx>, Span)> {
elaborate_obligations(
tcx,
predicates
.map(|(predicate, span)| {
Obligation::new(
tcx,
// We'll dump the cause/param-env later
ObligationCause::dummy_with_span(span),
ty::ParamEnv::empty(),
predicate,
)
})
.collect(),
)
.map(|obl| (obl.predicate, obl.cause.span))
impl<'tcx> Elaboratable<'tcx> for (ty::Predicate<'tcx>, Span) {
fn predicate(&self) -> ty::Predicate<'tcx> {
self.0
}
fn child(&self, predicate: ty::Predicate<'tcx>) -> Self {
(predicate, self.1)
}
fn child_with_derived_cause(
&self,
predicate: ty::Predicate<'tcx>,
_span: Span,
_parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
_index: usize,
) -> Self {
(predicate, self.1)
}
}
pub fn elaborate_obligations<'tcx>(
pub fn elaborate<'tcx, O: Elaboratable<'tcx>>(
tcx: TyCtxt<'tcx>,
obligations: Vec<PredicateObligation<'tcx>>,
) -> Elaborator<'tcx> {
obligations: impl IntoIterator<Item = O>,
) -> Elaborator<'tcx, O> {
let mut elaborator = Elaborator { stack: Vec::new(), visited: PredicateSet::new(tcx) };
elaborator.extend_deduped(obligations);
elaborator
}
fn predicate_obligation<'tcx>(
predicate: ty::Predicate<'tcx>,
param_env: ty::ParamEnv<'tcx>,
cause: ObligationCause<'tcx>,
) -> PredicateObligation<'tcx> {
Obligation { cause, param_env, recursion_depth: 0, predicate }
}
impl<'tcx> Elaborator<'tcx> {
fn extend_deduped(&mut self, obligations: impl IntoIterator<Item = PredicateObligation<'tcx>>) {
impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> {
fn extend_deduped(&mut self, obligations: impl IntoIterator<Item = O>) {
// Only keep those bounds that we haven't already seen.
// This is necessary to prevent infinite recursion in some
// cases. One common case is when people define
// `trait Sized: Sized { }` rather than `trait Sized { }`.
// let visited = &mut self.visited;
self.stack.extend(obligations.into_iter().filter(|o| self.visited.insert(o.predicate)));
self.stack.extend(obligations.into_iter().filter(|o| self.visited.insert(o.predicate())));
}
fn elaborate(&mut self, obligation: &PredicateObligation<'tcx>) {
fn elaborate(&mut self, elaboratable: &O) {
let tcx = self.visited.tcx;
let bound_predicate = obligation.predicate.kind();
let bound_predicate = elaboratable.predicate().kind();
match bound_predicate.skip_binder() {
ty::PredicateKind::Clause(ty::Clause::Trait(data)) => {
// Get predicates declared on the trait.
@ -170,24 +200,11 @@ impl<'tcx> Elaborator<'tcx> {
if data.constness == ty::BoundConstness::NotConst {
pred = pred.without_const(tcx);
}
let cause = obligation.cause.clone().derived_cause(
bound_predicate.rebind(data),
|derived| {
traits::ImplDerivedObligation(Box::new(
traits::ImplDerivedObligationCause {
derived,
impl_or_alias_def_id: data.def_id(),
impl_def_predicate_index: Some(index),
span,
},
))
},
);
predicate_obligation(
elaboratable.child_with_derived_cause(
pred.subst_supertrait(tcx, &bound_predicate.rebind(data.trait_ref)),
obligation.param_env,
cause,
span,
bound_predicate.rebind(data),
index,
)
});
debug!(?data, ?obligations, "super_predicates");
@ -290,13 +307,7 @@ impl<'tcx> Elaborator<'tcx> {
.map(|predicate_kind| {
bound_predicate.rebind(predicate_kind).to_predicate(tcx)
})
.map(|predicate| {
predicate_obligation(
predicate,
obligation.param_env,
obligation.cause.clone(),
)
}),
.map(|predicate| elaboratable.child(predicate)),
);
}
ty::PredicateKind::TypeWellFormedFromEnv(..) => {
@ -313,8 +324,8 @@ impl<'tcx> Elaborator<'tcx> {
}
}
impl<'tcx> Iterator for Elaborator<'tcx> {
type Item = PredicateObligation<'tcx>;
impl<'tcx, O: Elaboratable<'tcx>> Iterator for Elaborator<'tcx, O> {
type Item = O;
fn size_hint(&self) -> (usize, Option<usize>) {
(self.stack.len(), None)
@ -339,17 +350,21 @@ pub fn supertraits<'tcx>(
tcx: TyCtxt<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
) -> impl Iterator<Item = ty::PolyTraitRef<'tcx>> {
FilterToTraits::new(elaborate_trait_ref(tcx, trait_ref))
let pred: ty::Predicate<'tcx> = trait_ref.to_predicate(tcx);
FilterToTraits::new(elaborate(tcx, [pred]))
}
pub fn transitive_bounds<'tcx>(
tcx: TyCtxt<'tcx>,
trait_refs: impl Iterator<Item = ty::PolyTraitRef<'tcx>>,
) -> impl Iterator<Item = ty::PolyTraitRef<'tcx>> {
FilterToTraits::new(elaborate_trait_refs(tcx, trait_refs))
FilterToTraits::new(elaborate(
tcx,
trait_refs.map(|trait_ref| -> ty::Predicate<'tcx> { trait_ref.to_predicate(tcx) }),
))
}
/// A specialized variant of `elaborate_trait_refs` that only elaborates trait references that may
/// A specialized variant of `elaborate` that only elaborates trait references that may
/// define the given associated type `assoc_name`. It uses the
/// `super_predicates_that_define_assoc_type` query to avoid enumerating super-predicates that
/// aren't related to `assoc_item`. This is used when resolving types like `Self::Item` or