Skip to content

Commit

Permalink
Make elaborator generic
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed Apr 6, 2023
1 parent de74dab commit 758bedc
Show file tree
Hide file tree
Showing 21 changed files with 164 additions and 156 deletions.
4 changes: 2 additions & 2 deletions compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2034,7 +2034,7 @@ pub(super) fn check_type_bounds<'tcx>(
ObligationCause::new(impl_ty_span, impl_ty_def_id, code)
};

let obligations = tcx
let obligations: Vec<_> = tcx
.bound_explicit_item_bounds(trait_ty.def_id)
.subst_iter_copied(tcx, rebased_substs)
.map(|(concrete_ty_bound, span)| {
Expand All @@ -2044,7 +2044,7 @@ pub(super) fn check_type_bounds<'tcx>(
.collect();
debug!("check_type_bounds: item_bounds={:?}", obligations);

for mut obligation in util::elaborate_obligations(tcx, obligations) {
for mut obligation in util::elaborate(tcx, obligations) {
let normalized_predicate =
ocx.normalize(&normalize_cause, normalize_param_env, obligation.predicate);
debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate);
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1908,7 +1908,7 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {

let predicates_with_span = tcx.predicates_of(self.body_def_id).predicates.iter().copied();
// Check elaborated bounds.
let implied_obligations = traits::elaborate_predicates_with_span(tcx, predicates_with_span);
let implied_obligations = traits::elaborate(tcx, predicates_with_span);

for (pred, obligation_span) in implied_obligations {
// We lower empty bounds like `Vec<dyn Copy>:` as
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/collect/item_bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ pub(super) fn item_bounds(
tcx: TyCtxt<'_>,
def_id: DefId,
) -> ty::EarlyBinder<&'_ ty::List<ty::Predicate<'_>>> {
let bounds = tcx.mk_predicates_from_iter(util::elaborate_predicates(
let bounds = tcx.mk_predicates_from_iter(util::elaborate(
tcx,
tcx.explicit_item_bounds(def_id).iter().map(|&(bound, _span)| bound),
));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -318,15 +318,14 @@ fn check_predicates<'tcx>(
span: Span,
) {
let instantiated = tcx.predicates_of(impl1_def_id).instantiate(tcx, impl1_substs);
let impl1_predicates: Vec<_> =
traits::elaborate_predicates_with_span(tcx, instantiated.into_iter()).collect();
let impl1_predicates: Vec<_> = traits::elaborate(tcx, instantiated.into_iter()).collect();

let mut impl2_predicates = if impl2_node.is_from_trait() {
// Always applicable traits have to be always applicable without any
// assumptions.
Vec::new()
} else {
traits::elaborate_predicates(
traits::elaborate(
tcx,
tcx.predicates_of(impl2_node.def_id())
.instantiate(tcx, impl2_substs)
Expand Down Expand Up @@ -371,11 +370,10 @@ fn check_predicates<'tcx>(
.unwrap();

assert!(!obligations.needs_infer());
impl2_predicates.extend(
traits::elaborate_obligations(tcx, obligations).map(|obligation| obligation.predicate),
)
impl2_predicates
.extend(traits::elaborate(tcx, obligations).map(|obligation| obligation.predicate))
}
impl2_predicates.extend(traits::elaborate_predicates(tcx, always_applicable_traits));
impl2_predicates.extend(traits::elaborate(tcx, always_applicable_traits));

for (predicate, span) in impl1_predicates {
if !impl2_predicates.iter().any(|pred2| trait_predicates_eq(tcx, predicate, *pred2, span)) {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut expected_sig = None;
let mut expected_kind = None;

for (pred, span) in traits::elaborate_predicates_with_span(
for (pred, span) in traits::elaborate(
self.tcx,
// Reverse the obligations here, since `elaborate_*` uses a stack,
// and we want to keep inference generally in the same order of
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/method/confirm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
) -> Option<Span> {
let sized_def_id = self.tcx.lang_items().sized_trait()?;

traits::elaborate_predicates(self.tcx, predicates.predicates.iter().copied())
traits::elaborate(self.tcx, predicates.predicates.iter().copied())
// We don't care about regions here.
.filter_map(|pred| match pred.kind().skip_binder() {
ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred))
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1555,8 +1555,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
if !self.predicate_may_hold(&o) {
result = ProbeResult::NoMatch;
let parent_o = o.clone();
let implied_obligations =
traits::elaborate_obligations(self.tcx, vec![o]);
let implied_obligations = traits::elaborate(self.tcx, vec![o]);
for o in implied_obligations {
let parent = if o == parent_o {
None
Expand Down
209 changes: 117 additions & 92 deletions compiler/rustc_infer/src/traits/util.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -66,99 +66,143 @@ 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>(
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
}
}

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_trait_ref<'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)
trait_ref: ty::PolyTraitRef<'tcx>,
) -> Elaborator<'tcx, ty::Predicate<'tcx>> {
elaborate(tcx, std::iter::once(trait_ref.without_const().to_predicate(tcx)))
}

pub fn elaborate_predicates_with_span<'tcx>(
pub fn elaborate_trait_refs<'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))
trait_refs: impl Iterator<Item = ty::PolyTraitRef<'tcx>>,
) -> Elaborator<'tcx, ty::Predicate<'tcx>> {
elaborate(tcx, trait_refs.map(|trait_ref| trait_ref.to_predicate(tcx)))
}

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.
Expand All @@ -170,24 +214,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");
Expand Down Expand Up @@ -290,13 +321,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(..) => {
Expand All @@ -313,8 +338,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)
Expand Down
Loading

0 comments on commit 758bedc

Please sign in to comment.