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:
commit
f8ed97ecc1
22 changed files with 163 additions and 164 deletions
|
@ -33,9 +33,9 @@ use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKin
|
||||||
use rustc_middle::middle::stability::AllowUnstable;
|
use rustc_middle::middle::stability::AllowUnstable;
|
||||||
use rustc_middle::ty::fold::FnMutDelegate;
|
use rustc_middle::ty::fold::FnMutDelegate;
|
||||||
use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, SubstsRef};
|
use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, SubstsRef};
|
||||||
use rustc_middle::ty::DynKind;
|
|
||||||
use rustc_middle::ty::GenericParamDefKind;
|
use rustc_middle::ty::GenericParamDefKind;
|
||||||
use rustc_middle::ty::{self, Const, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
|
use rustc_middle::ty::{self, Const, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
|
||||||
|
use rustc_middle::ty::{DynKind, ToPredicate};
|
||||||
use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, BARE_TRAIT_OBJECTS};
|
use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, BARE_TRAIT_OBJECTS};
|
||||||
use rustc_span::edit_distance::find_best_match_for_name;
|
use rustc_span::edit_distance::find_best_match_for_name;
|
||||||
use rustc_span::edition::Edition;
|
use rustc_span::edition::Edition;
|
||||||
|
@ -1526,8 +1526,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
|
|
||||||
for (base_trait_ref, span, constness) in regular_traits_refs_spans {
|
for (base_trait_ref, span, constness) in regular_traits_refs_spans {
|
||||||
assert_eq!(constness, ty::BoundConstness::NotConst);
|
assert_eq!(constness, ty::BoundConstness::NotConst);
|
||||||
|
let base_pred: ty::Predicate<'tcx> = base_trait_ref.to_predicate(tcx);
|
||||||
for pred in traits::elaborate_trait_ref(tcx, base_trait_ref) {
|
for pred in traits::elaborate(tcx, [base_pred]) {
|
||||||
debug!("conv_object_ty_poly_trait_ref: observing object predicate `{:?}`", pred);
|
debug!("conv_object_ty_poly_trait_ref: observing object predicate `{:?}`", pred);
|
||||||
|
|
||||||
let bound_predicate = pred.kind();
|
let bound_predicate = pred.kind();
|
||||||
|
|
|
@ -2034,7 +2034,7 @@ pub(super) fn check_type_bounds<'tcx>(
|
||||||
ObligationCause::new(impl_ty_span, impl_ty_def_id, code)
|
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)
|
.bound_explicit_item_bounds(trait_ty.def_id)
|
||||||
.subst_iter_copied(tcx, rebased_substs)
|
.subst_iter_copied(tcx, rebased_substs)
|
||||||
.map(|(concrete_ty_bound, span)| {
|
.map(|(concrete_ty_bound, span)| {
|
||||||
|
@ -2044,7 +2044,7 @@ pub(super) fn check_type_bounds<'tcx>(
|
||||||
.collect();
|
.collect();
|
||||||
debug!("check_type_bounds: item_bounds={:?}", obligations);
|
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 =
|
let normalized_predicate =
|
||||||
ocx.normalize(&normalize_cause, normalize_param_env, obligation.predicate);
|
ocx.normalize(&normalize_cause, normalize_param_env, obligation.predicate);
|
||||||
debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate);
|
debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate);
|
||||||
|
|
|
@ -1908,7 +1908,7 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
|
||||||
|
|
||||||
let predicates_with_span = tcx.predicates_of(self.body_def_id).predicates.iter().copied();
|
let predicates_with_span = tcx.predicates_of(self.body_def_id).predicates.iter().copied();
|
||||||
// Check elaborated bounds.
|
// 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 {
|
for (pred, obligation_span) in implied_obligations {
|
||||||
// We lower empty bounds like `Vec<dyn Copy>:` as
|
// We lower empty bounds like `Vec<dyn Copy>:` as
|
||||||
|
|
|
@ -130,7 +130,7 @@ pub(super) fn item_bounds(
|
||||||
tcx: TyCtxt<'_>,
|
tcx: TyCtxt<'_>,
|
||||||
def_id: DefId,
|
def_id: DefId,
|
||||||
) -> ty::EarlyBinder<&'_ ty::List<ty::Predicate<'_>>> {
|
) -> 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,
|
||||||
tcx.explicit_item_bounds(def_id).iter().map(|&(bound, _span)| bound),
|
tcx.explicit_item_bounds(def_id).iter().map(|&(bound, _span)| bound),
|
||||||
));
|
));
|
||||||
|
|
|
@ -318,15 +318,14 @@ fn check_predicates<'tcx>(
|
||||||
span: Span,
|
span: Span,
|
||||||
) {
|
) {
|
||||||
let instantiated = tcx.predicates_of(impl1_def_id).instantiate(tcx, impl1_substs);
|
let instantiated = tcx.predicates_of(impl1_def_id).instantiate(tcx, impl1_substs);
|
||||||
let impl1_predicates: Vec<_> =
|
let impl1_predicates: Vec<_> = traits::elaborate(tcx, instantiated.into_iter()).collect();
|
||||||
traits::elaborate_predicates_with_span(tcx, instantiated.into_iter()).collect();
|
|
||||||
|
|
||||||
let mut impl2_predicates = if impl2_node.is_from_trait() {
|
let mut impl2_predicates = if impl2_node.is_from_trait() {
|
||||||
// Always applicable traits have to be always applicable without any
|
// Always applicable traits have to be always applicable without any
|
||||||
// assumptions.
|
// assumptions.
|
||||||
Vec::new()
|
Vec::new()
|
||||||
} else {
|
} else {
|
||||||
traits::elaborate_predicates(
|
traits::elaborate(
|
||||||
tcx,
|
tcx,
|
||||||
tcx.predicates_of(impl2_node.def_id())
|
tcx.predicates_of(impl2_node.def_id())
|
||||||
.instantiate(tcx, impl2_substs)
|
.instantiate(tcx, impl2_substs)
|
||||||
|
@ -371,11 +370,10 @@ fn check_predicates<'tcx>(
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert!(!obligations.needs_infer());
|
assert!(!obligations.needs_infer());
|
||||||
impl2_predicates.extend(
|
impl2_predicates
|
||||||
traits::elaborate_obligations(tcx, obligations).map(|obligation| obligation.predicate),
|
.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 {
|
for (predicate, span) in impl1_predicates {
|
||||||
if !impl2_predicates.iter().any(|pred2| trait_predicates_eq(tcx, predicate, *pred2, span)) {
|
if !impl2_predicates.iter().any(|pred2| trait_predicates_eq(tcx, predicate, *pred2, span)) {
|
||||||
|
|
|
@ -204,7 +204,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
let mut expected_sig = None;
|
let mut expected_sig = None;
|
||||||
let mut expected_kind = None;
|
let mut expected_kind = None;
|
||||||
|
|
||||||
for (pred, span) in traits::elaborate_predicates_with_span(
|
for (pred, span) in traits::elaborate(
|
||||||
self.tcx,
|
self.tcx,
|
||||||
// Reverse the obligations here, since `elaborate_*` uses a stack,
|
// Reverse the obligations here, since `elaborate_*` uses a stack,
|
||||||
// and we want to keep inference generally in the same order of
|
// and we want to keep inference generally in the same order of
|
||||||
|
|
|
@ -574,7 +574,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
|
||||||
) -> Option<Span> {
|
) -> Option<Span> {
|
||||||
let sized_def_id = self.tcx.lang_items().sized_trait()?;
|
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.
|
// We don't care about regions here.
|
||||||
.filter_map(|pred| match pred.kind().skip_binder() {
|
.filter_map(|pred| match pred.kind().skip_binder() {
|
||||||
ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred))
|
ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred))
|
||||||
|
|
|
@ -1555,8 +1555,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
||||||
if !self.predicate_may_hold(&o) {
|
if !self.predicate_may_hold(&o) {
|
||||||
result = ProbeResult::NoMatch;
|
result = ProbeResult::NoMatch;
|
||||||
let parent_o = o.clone();
|
let parent_o = o.clone();
|
||||||
let implied_obligations =
|
let implied_obligations = traits::elaborate(self.tcx, vec![o]);
|
||||||
traits::elaborate_obligations(self.tcx, vec![o]);
|
|
||||||
for o in implied_obligations {
|
for o in implied_obligations {
|
||||||
let parent = if o == parent_o {
|
let parent = if o == parent_o {
|
||||||
None
|
None
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use smallvec::smallvec;
|
use smallvec::smallvec;
|
||||||
|
|
||||||
use crate::infer::outlives::components::{push_outlives_components, Component};
|
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_data_structures::fx::{FxHashSet, FxIndexSet};
|
||||||
use rustc_middle::ty::{self, ToPredicate, TyCtxt};
|
use rustc_middle::ty::{self, ToPredicate, TyCtxt};
|
||||||
use rustc_span::symbol::Ident;
|
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`
|
/// 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
|
/// holds as well. Similarly, if we have `trait Foo: 'static`, and we know that
|
||||||
/// `T: Foo`, then we know that `T: 'static`.
|
/// `T: Foo`, then we know that `T: 'static`.
|
||||||
pub struct Elaborator<'tcx> {
|
pub struct Elaborator<'tcx, O> {
|
||||||
stack: Vec<PredicateObligation<'tcx>>,
|
stack: Vec<O>,
|
||||||
visited: PredicateSet<'tcx>,
|
visited: PredicateSet<'tcx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn elaborate_trait_ref<'tcx>(
|
/// Describes how to elaborate an obligation into a sub-obligation.
|
||||||
tcx: TyCtxt<'tcx>,
|
///
|
||||||
trait_ref: ty::PolyTraitRef<'tcx>,
|
/// For [`Obligation`], a sub-obligation is combined with the current obligation's
|
||||||
) -> impl Iterator<Item = ty::Predicate<'tcx>> {
|
/// param-env and cause code. For [`ty::Predicate`], none of this is needed, since
|
||||||
elaborate_predicates(tcx, std::iter::once(trait_ref.without_const().to_predicate(tcx)))
|
/// 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>(
|
impl<'tcx> Elaboratable<'tcx> for PredicateObligation<'tcx> {
|
||||||
tcx: TyCtxt<'tcx>,
|
fn predicate(&self) -> ty::Predicate<'tcx> {
|
||||||
trait_refs: impl Iterator<Item = ty::PolyTraitRef<'tcx>>,
|
self.predicate
|
||||||
) -> 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)
|
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> {
|
||||||
tcx: TyCtxt<'tcx>,
|
fn predicate(&self) -> ty::Predicate<'tcx> {
|
||||||
predicates: impl Iterator<Item = ty::Predicate<'tcx>>,
|
*self
|
||||||
) -> impl Iterator<Item = ty::Predicate<'tcx>> {
|
}
|
||||||
elaborate_obligations(
|
|
||||||
tcx,
|
fn child(&self, predicate: ty::Predicate<'tcx>) -> Self {
|
||||||
predicates
|
predicate
|
||||||
.map(|predicate| {
|
}
|
||||||
Obligation::new(
|
|
||||||
tcx,
|
fn child_with_derived_cause(
|
||||||
// We'll dump the cause/param-env later
|
&self,
|
||||||
ObligationCause::dummy(),
|
predicate: ty::Predicate<'tcx>,
|
||||||
ty::ParamEnv::empty(),
|
_span: Span,
|
||||||
predicate,
|
_parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
|
||||||
)
|
_index: usize,
|
||||||
})
|
) -> Self {
|
||||||
.collect(),
|
predicate
|
||||||
)
|
}
|
||||||
.map(|obl| obl.predicate)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn elaborate_predicates_with_span<'tcx>(
|
impl<'tcx> Elaboratable<'tcx> for (ty::Predicate<'tcx>, Span) {
|
||||||
tcx: TyCtxt<'tcx>,
|
fn predicate(&self) -> ty::Predicate<'tcx> {
|
||||||
predicates: impl Iterator<Item = (ty::Predicate<'tcx>, Span)>,
|
self.0
|
||||||
) -> impl Iterator<Item = (ty::Predicate<'tcx>, Span)> {
|
}
|
||||||
elaborate_obligations(
|
|
||||||
tcx,
|
fn child(&self, predicate: ty::Predicate<'tcx>) -> Self {
|
||||||
predicates
|
(predicate, self.1)
|
||||||
.map(|(predicate, span)| {
|
}
|
||||||
Obligation::new(
|
|
||||||
tcx,
|
fn child_with_derived_cause(
|
||||||
// We'll dump the cause/param-env later
|
&self,
|
||||||
ObligationCause::dummy_with_span(span),
|
predicate: ty::Predicate<'tcx>,
|
||||||
ty::ParamEnv::empty(),
|
_span: Span,
|
||||||
predicate,
|
_parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
|
||||||
)
|
_index: usize,
|
||||||
})
|
) -> Self {
|
||||||
.collect(),
|
(predicate, self.1)
|
||||||
)
|
}
|
||||||
.map(|obl| (obl.predicate, obl.cause.span))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn elaborate_obligations<'tcx>(
|
pub fn elaborate<'tcx, O: Elaboratable<'tcx>>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
obligations: Vec<PredicateObligation<'tcx>>,
|
obligations: impl IntoIterator<Item = O>,
|
||||||
) -> Elaborator<'tcx> {
|
) -> Elaborator<'tcx, O> {
|
||||||
let mut elaborator = Elaborator { stack: Vec::new(), visited: PredicateSet::new(tcx) };
|
let mut elaborator = Elaborator { stack: Vec::new(), visited: PredicateSet::new(tcx) };
|
||||||
elaborator.extend_deduped(obligations);
|
elaborator.extend_deduped(obligations);
|
||||||
elaborator
|
elaborator
|
||||||
}
|
}
|
||||||
|
|
||||||
fn predicate_obligation<'tcx>(
|
impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> {
|
||||||
predicate: ty::Predicate<'tcx>,
|
fn extend_deduped(&mut self, obligations: impl IntoIterator<Item = O>) {
|
||||||
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>>) {
|
|
||||||
// Only keep those bounds that we haven't already seen.
|
// Only keep those bounds that we haven't already seen.
|
||||||
// This is necessary to prevent infinite recursion in some
|
// This is necessary to prevent infinite recursion in some
|
||||||
// cases. One common case is when people define
|
// cases. One common case is when people define
|
||||||
// `trait Sized: Sized { }` rather than `trait Sized { }`.
|
// `trait Sized: Sized { }` rather than `trait Sized { }`.
|
||||||
// let visited = &mut self.visited;
|
// 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 tcx = self.visited.tcx;
|
||||||
|
|
||||||
let bound_predicate = obligation.predicate.kind();
|
let bound_predicate = elaboratable.predicate().kind();
|
||||||
match bound_predicate.skip_binder() {
|
match bound_predicate.skip_binder() {
|
||||||
ty::PredicateKind::Clause(ty::Clause::Trait(data)) => {
|
ty::PredicateKind::Clause(ty::Clause::Trait(data)) => {
|
||||||
// Get predicates declared on the trait.
|
// Get predicates declared on the trait.
|
||||||
|
@ -170,24 +200,11 @@ impl<'tcx> Elaborator<'tcx> {
|
||||||
if data.constness == ty::BoundConstness::NotConst {
|
if data.constness == ty::BoundConstness::NotConst {
|
||||||
pred = pred.without_const(tcx);
|
pred = pred.without_const(tcx);
|
||||||
}
|
}
|
||||||
|
elaboratable.child_with_derived_cause(
|
||||||
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(
|
|
||||||
pred.subst_supertrait(tcx, &bound_predicate.rebind(data.trait_ref)),
|
pred.subst_supertrait(tcx, &bound_predicate.rebind(data.trait_ref)),
|
||||||
obligation.param_env,
|
span,
|
||||||
cause,
|
bound_predicate.rebind(data),
|
||||||
|
index,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
debug!(?data, ?obligations, "super_predicates");
|
debug!(?data, ?obligations, "super_predicates");
|
||||||
|
@ -290,13 +307,7 @@ impl<'tcx> Elaborator<'tcx> {
|
||||||
.map(|predicate_kind| {
|
.map(|predicate_kind| {
|
||||||
bound_predicate.rebind(predicate_kind).to_predicate(tcx)
|
bound_predicate.rebind(predicate_kind).to_predicate(tcx)
|
||||||
})
|
})
|
||||||
.map(|predicate| {
|
.map(|predicate| elaboratable.child(predicate)),
|
||||||
predicate_obligation(
|
|
||||||
predicate,
|
|
||||||
obligation.param_env,
|
|
||||||
obligation.cause.clone(),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
ty::PredicateKind::TypeWellFormedFromEnv(..) => {
|
ty::PredicateKind::TypeWellFormedFromEnv(..) => {
|
||||||
|
@ -313,8 +324,8 @@ impl<'tcx> Elaborator<'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> Iterator for Elaborator<'tcx> {
|
impl<'tcx, O: Elaboratable<'tcx>> Iterator for Elaborator<'tcx, O> {
|
||||||
type Item = PredicateObligation<'tcx>;
|
type Item = O;
|
||||||
|
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
(self.stack.len(), None)
|
(self.stack.len(), None)
|
||||||
|
@ -339,17 +350,21 @@ pub fn supertraits<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
trait_ref: ty::PolyTraitRef<'tcx>,
|
trait_ref: ty::PolyTraitRef<'tcx>,
|
||||||
) -> impl Iterator<Item = 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>(
|
pub fn transitive_bounds<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
trait_refs: impl Iterator<Item = ty::PolyTraitRef<'tcx>>,
|
trait_refs: impl Iterator<Item = ty::PolyTraitRef<'tcx>>,
|
||||||
) -> 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
|
/// define the given associated type `assoc_name`. It uses the
|
||||||
/// `super_predicates_that_define_assoc_type` query to avoid enumerating super-predicates that
|
/// `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
|
/// aren't related to `assoc_item`. This is used when resolving types like `Self::Item` or
|
||||||
|
|
|
@ -12,7 +12,7 @@ use rustc_errors::{pluralize, MultiSpan};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def::{DefKind, Res};
|
use rustc_hir::def::{DefKind, Res};
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_infer::traits::util::elaborate_predicates_with_span;
|
use rustc_infer::traits::util::elaborate;
|
||||||
use rustc_middle::ty::adjustment;
|
use rustc_middle::ty::adjustment;
|
||||||
use rustc_middle::ty::{self, Ty};
|
use rustc_middle::ty::{self, Ty};
|
||||||
use rustc_span::symbol::Symbol;
|
use rustc_span::symbol::Symbol;
|
||||||
|
@ -254,24 +254,21 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
|
||||||
}
|
}
|
||||||
ty::Adt(def, _) => is_def_must_use(cx, def.did(), span),
|
ty::Adt(def, _) => is_def_must_use(cx, def.did(), span),
|
||||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id: def, .. }) => {
|
ty::Alias(ty::Opaque, ty::AliasTy { def_id: def, .. }) => {
|
||||||
elaborate_predicates_with_span(
|
elaborate(cx.tcx, cx.tcx.explicit_item_bounds(def).iter().cloned())
|
||||||
cx.tcx,
|
.find_map(|(pred, _span)| {
|
||||||
cx.tcx.explicit_item_bounds(def).iter().cloned(),
|
// We only look at the `DefId`, so it is safe to skip the binder here.
|
||||||
)
|
if let ty::PredicateKind::Clause(ty::Clause::Trait(
|
||||||
.find_map(|(pred, _span)| {
|
ref poly_trait_predicate,
|
||||||
// We only look at the `DefId`, so it is safe to skip the binder here.
|
)) = pred.kind().skip_binder()
|
||||||
if let ty::PredicateKind::Clause(ty::Clause::Trait(
|
{
|
||||||
ref poly_trait_predicate,
|
let def_id = poly_trait_predicate.trait_ref.def_id;
|
||||||
)) = pred.kind().skip_binder()
|
|
||||||
{
|
|
||||||
let def_id = poly_trait_predicate.trait_ref.def_id;
|
|
||||||
|
|
||||||
is_def_must_use(cx, def_id, span)
|
is_def_must_use(cx, def_id, span)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.map(|inner| MustUsePath::Opaque(Box::new(inner)))
|
.map(|inner| MustUsePath::Opaque(Box::new(inner)))
|
||||||
}
|
}
|
||||||
ty::Dynamic(binders, _, _) => binders.iter().find_map(|predicate| {
|
ty::Dynamic(binders, _, _) => binders.iter().find_map(|predicate| {
|
||||||
if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder()
|
if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder()
|
||||||
|
|
|
@ -115,10 +115,7 @@ impl<'tcx> MirPass<'tcx> for ConstProp {
|
||||||
.predicates
|
.predicates
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
|
.filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
|
||||||
if traits::impossible_predicates(
|
if traits::impossible_predicates(tcx, traits::elaborate(tcx, predicates).collect()) {
|
||||||
tcx,
|
|
||||||
traits::elaborate_predicates(tcx, predicates).collect(),
|
|
||||||
) {
|
|
||||||
trace!("ConstProp skipped for {:?}: found unsatisfiable predicates", def_id);
|
trace!("ConstProp skipped for {:?}: found unsatisfiable predicates", def_id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,10 +91,7 @@ impl<'tcx> MirLint<'tcx> for ConstProp {
|
||||||
.predicates
|
.predicates
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
|
.filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
|
||||||
if traits::impossible_predicates(
|
if traits::impossible_predicates(tcx, traits::elaborate(tcx, predicates).collect()) {
|
||||||
tcx,
|
|
||||||
traits::elaborate_predicates(tcx, predicates).collect(),
|
|
||||||
) {
|
|
||||||
trace!("ConstProp skipped for {:?}: found unsatisfiable predicates", def_id);
|
trace!("ConstProp skipped for {:?}: found unsatisfiable predicates", def_id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ use itertools::Itertools;
|
||||||
use rustc_data_structures::fx::FxIndexSet;
|
use rustc_data_structures::fx::FxIndexSet;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_infer::traits::query::NoSolution;
|
use rustc_infer::traits::query::NoSolution;
|
||||||
use rustc_infer::traits::util::elaborate_predicates;
|
use rustc_infer::traits::util::elaborate;
|
||||||
use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult};
|
use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult};
|
||||||
use rustc_middle::ty::fast_reject::TreatProjections;
|
use rustc_middle::ty::fast_reject::TreatProjections;
|
||||||
use rustc_middle::ty::TypeFoldable;
|
use rustc_middle::ty::TypeFoldable;
|
||||||
|
@ -498,7 +498,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
let tcx = self.tcx();
|
let tcx = self.tcx();
|
||||||
let own_bounds: FxIndexSet<_> =
|
let own_bounds: FxIndexSet<_> =
|
||||||
bounds.iter().map(|bound| bound.with_self_ty(tcx, self_ty)).collect();
|
bounds.iter().map(|bound| bound.with_self_ty(tcx, self_ty)).collect();
|
||||||
for assumption in elaborate_predicates(tcx, own_bounds.iter().copied()) {
|
for assumption in elaborate(tcx, own_bounds.iter().copied()) {
|
||||||
// FIXME: Predicates are fully elaborated in the object type's existential bounds
|
// FIXME: Predicates are fully elaborated in the object type's existential bounds
|
||||||
// list. We want to only consider these pre-elaborated projections, and not other
|
// list. We want to only consider these pre-elaborated projections, and not other
|
||||||
// projection predicates that we reach by elaborating the principal trait ref,
|
// projection predicates that we reach by elaborating the principal trait ref,
|
||||||
|
|
|
@ -234,7 +234,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
|
||||||
/// constructed once for a given type. As part of the construction process, the `ParamEnv` will
|
/// constructed once for a given type. As part of the construction process, the `ParamEnv` will
|
||||||
/// have any supertrait bounds normalized -- e.g., if we have a type `struct Foo<T: Copy>`, the
|
/// have any supertrait bounds normalized -- e.g., if we have a type `struct Foo<T: Copy>`, the
|
||||||
/// `ParamEnv` will contain `T: Copy` and `T: Clone`, since `Copy: Clone`. When we construct our
|
/// `ParamEnv` will contain `T: Copy` and `T: Clone`, since `Copy: Clone`. When we construct our
|
||||||
/// own `ParamEnv`, we need to do this ourselves, through `traits::elaborate_predicates`, or
|
/// own `ParamEnv`, we need to do this ourselves, through `traits::elaborate`, or
|
||||||
/// else `SelectionContext` will choke on the missing predicates. However, this should never
|
/// else `SelectionContext` will choke on the missing predicates. However, this should never
|
||||||
/// show up in the final synthesized generics: we don't want our generated docs page to contain
|
/// show up in the final synthesized generics: we don't want our generated docs page to contain
|
||||||
/// something like `T: Copy + Clone`, as that's redundant. Therefore, we keep track of a
|
/// something like `T: Copy + Clone`, as that's redundant. Therefore, we keep track of a
|
||||||
|
@ -346,10 +346,8 @@ impl<'tcx> AutoTraitFinder<'tcx> {
|
||||||
_ => panic!("Unexpected error for '{:?}': {:?}", ty, result),
|
_ => panic!("Unexpected error for '{:?}': {:?}", ty, result),
|
||||||
};
|
};
|
||||||
|
|
||||||
let normalized_preds = elaborate_predicates(
|
let normalized_preds =
|
||||||
tcx,
|
elaborate(tcx, computed_preds.clone().chain(user_computed_preds.iter().cloned()));
|
||||||
computed_preds.clone().chain(user_computed_preds.iter().cloned()),
|
|
||||||
);
|
|
||||||
new_env = ty::ParamEnv::new(
|
new_env = ty::ParamEnv::new(
|
||||||
tcx.mk_predicates_from_iter(normalized_preds),
|
tcx.mk_predicates_from_iter(normalized_preds),
|
||||||
param_env.reveal(),
|
param_env.reveal(),
|
||||||
|
|
|
@ -368,7 +368,7 @@ fn negative_impl_exists<'tcx>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to prove a negative obligation exists for super predicates
|
// Try to prove a negative obligation exists for super predicates
|
||||||
for pred in util::elaborate_predicates(infcx.tcx, iter::once(o.predicate)) {
|
for pred in util::elaborate(infcx.tcx, iter::once(o.predicate)) {
|
||||||
if resolve_negative_obligation(infcx.fork(), &o.with(infcx.tcx, pred), body_def_id) {
|
if resolve_negative_obligation(infcx.fork(), &o.with(infcx.tcx, pred), body_def_id) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_infer::infer::{InferCtxt, LateBoundRegionConversionTime};
|
use rustc_infer::infer::{InferCtxt, LateBoundRegionConversionTime};
|
||||||
use rustc_infer::traits::util::elaborate_predicates_with_span;
|
use rustc_infer::traits::util::elaborate;
|
||||||
use rustc_infer::traits::{Obligation, ObligationCause, TraitObligation};
|
use rustc_infer::traits::{Obligation, ObligationCause, TraitObligation};
|
||||||
use rustc_middle::ty;
|
use rustc_middle::ty;
|
||||||
use rustc_span::{Span, DUMMY_SP};
|
use rustc_span::{Span, DUMMY_SP};
|
||||||
|
@ -82,7 +82,7 @@ pub fn recompute_applicable_impls<'tcx>(
|
||||||
|
|
||||||
let predicates =
|
let predicates =
|
||||||
tcx.predicates_of(obligation.cause.body_id.to_def_id()).instantiate_identity(tcx);
|
tcx.predicates_of(obligation.cause.body_id.to_def_id()).instantiate_identity(tcx);
|
||||||
for (pred, span) in elaborate_predicates_with_span(tcx, predicates.into_iter()) {
|
for (pred, span) in elaborate(tcx, predicates.into_iter()) {
|
||||||
let kind = pred.kind();
|
let kind = pred.kind();
|
||||||
if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = kind.skip_binder()
|
if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = kind.skip_binder()
|
||||||
&& param_env_candidate_may_apply(kind.rebind(trait_pred))
|
&& param_env_candidate_may_apply(kind.rebind(trait_pred))
|
||||||
|
|
|
@ -1624,7 +1624,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for pred in super::elaborate_predicates(self.tcx, std::iter::once(cond)) {
|
for pred in super::elaborate(self.tcx, std::iter::once(cond)) {
|
||||||
let bound_predicate = pred.kind();
|
let bound_predicate = pred.kind();
|
||||||
if let ty::PredicateKind::Clause(ty::Clause::Trait(implication)) =
|
if let ty::PredicateKind::Clause(ty::Clause::Trait(implication)) =
|
||||||
bound_predicate.skip_binder()
|
bound_predicate.skip_binder()
|
||||||
|
|
|
@ -58,10 +58,7 @@ pub use self::specialize::{specialization_graph, translate_substs, OverlapError}
|
||||||
pub use self::structural_match::{
|
pub use self::structural_match::{
|
||||||
search_for_adt_const_param_violation, search_for_structural_match_violation,
|
search_for_adt_const_param_violation, search_for_structural_match_violation,
|
||||||
};
|
};
|
||||||
pub use self::util::{
|
pub use self::util::elaborate;
|
||||||
elaborate_obligations, elaborate_predicates, elaborate_predicates_with_span,
|
|
||||||
elaborate_trait_ref, elaborate_trait_refs,
|
|
||||||
};
|
|
||||||
pub use self::util::{expand_trait_aliases, TraitAliasExpander};
|
pub use self::util::{expand_trait_aliases, TraitAliasExpander};
|
||||||
pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices};
|
pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices};
|
||||||
pub use self::util::{
|
pub use self::util::{
|
||||||
|
@ -267,7 +264,7 @@ pub fn normalize_param_env_or_error<'tcx>(
|
||||||
// and errors will get reported then; so outside of type inference we
|
// and errors will get reported then; so outside of type inference we
|
||||||
// can be sure that no errors should occur.
|
// can be sure that no errors should occur.
|
||||||
let mut predicates: Vec<_> =
|
let mut predicates: Vec<_> =
|
||||||
util::elaborate_predicates(tcx, unnormalized_env.caller_bounds().into_iter()).collect();
|
util::elaborate(tcx, unnormalized_env.caller_bounds().into_iter()).collect();
|
||||||
|
|
||||||
debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates);
|
debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates);
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
//! - not reference the erased type `Self` except for in this receiver;
|
//! - not reference the erased type `Self` except for in this receiver;
|
||||||
//! - not have generic type parameters.
|
//! - not have generic type parameters.
|
||||||
|
|
||||||
use super::{elaborate_predicates, elaborate_trait_ref};
|
use super::elaborate;
|
||||||
|
|
||||||
use crate::infer::TyCtxtInferExt;
|
use crate::infer::TyCtxtInferExt;
|
||||||
use crate::traits::query::evaluate_obligation::InferCtxtExt;
|
use crate::traits::query::evaluate_obligation::InferCtxtExt;
|
||||||
|
@ -379,7 +379,7 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
|
||||||
// Search for a predicate like `Self : Sized` amongst the trait bounds.
|
// Search for a predicate like `Self : Sized` amongst the trait bounds.
|
||||||
let predicates = tcx.predicates_of(def_id);
|
let predicates = tcx.predicates_of(def_id);
|
||||||
let predicates = predicates.instantiate_identity(tcx).predicates;
|
let predicates = predicates.instantiate_identity(tcx).predicates;
|
||||||
elaborate_predicates(tcx, predicates.into_iter()).any(|pred| match pred.kind().skip_binder() {
|
elaborate(tcx, predicates.into_iter()).any(|pred| match pred.kind().skip_binder() {
|
||||||
ty::PredicateKind::Clause(ty::Clause::Trait(ref trait_pred)) => {
|
ty::PredicateKind::Clause(ty::Clause::Trait(ref trait_pred)) => {
|
||||||
trait_pred.def_id() == sized_def_id && trait_pred.self_ty().is_param(0)
|
trait_pred.def_id() == sized_def_id && trait_pred.self_ty().is_param(0)
|
||||||
}
|
}
|
||||||
|
@ -666,7 +666,8 @@ fn object_ty_for_trait<'tcx>(
|
||||||
});
|
});
|
||||||
debug!(?trait_predicate);
|
debug!(?trait_predicate);
|
||||||
|
|
||||||
let mut elaborated_predicates: Vec<_> = elaborate_trait_ref(tcx, trait_ref)
|
let pred: ty::Predicate<'tcx> = trait_ref.to_predicate(tcx);
|
||||||
|
let mut elaborated_predicates: Vec<_> = elaborate(tcx, [pred])
|
||||||
.filter_map(|pred| {
|
.filter_map(|pred| {
|
||||||
debug!(?pred);
|
debug!(?pred);
|
||||||
let pred = pred.to_opt_poly_projection_pred()?;
|
let pred = pred.to_opt_poly_projection_pred()?;
|
||||||
|
|
|
@ -364,7 +364,7 @@ impl<'tcx> WfPredicates<'tcx> {
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Elaborate::All = elaborate {
|
if let Elaborate::All = elaborate {
|
||||||
let implied_obligations = traits::util::elaborate_obligations(tcx, obligations);
|
let implied_obligations = traits::util::elaborate(tcx, obligations);
|
||||||
let implied_obligations = implied_obligations.map(extend);
|
let implied_obligations = implied_obligations.map(extend);
|
||||||
self.out.extend(implied_obligations);
|
self.out.extend(implied_obligations);
|
||||||
} else {
|
} else {
|
||||||
|
@ -920,7 +920,7 @@ pub(crate) fn required_region_bounds<'tcx>(
|
||||||
) -> Vec<ty::Region<'tcx>> {
|
) -> Vec<ty::Region<'tcx>> {
|
||||||
assert!(!erased_self_ty.has_escaping_bound_vars());
|
assert!(!erased_self_ty.has_escaping_bound_vars());
|
||||||
|
|
||||||
traits::elaborate_predicates(tcx, predicates)
|
traits::elaborate(tcx, predicates)
|
||||||
.filter_map(|pred| {
|
.filter_map(|pred| {
|
||||||
debug!(?pred);
|
debug!(?pred);
|
||||||
match pred.kind().skip_binder() {
|
match pred.kind().skip_binder() {
|
||||||
|
|
|
@ -122,7 +122,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
|
||||||
|
|
||||||
let sized_trait = need!(cx.tcx.lang_items().sized_trait());
|
let sized_trait = need!(cx.tcx.lang_items().sized_trait());
|
||||||
|
|
||||||
let preds = traits::elaborate_predicates(cx.tcx, cx.param_env.caller_bounds().iter())
|
let preds = traits::elaborate(cx.tcx, cx.param_env.caller_bounds().iter())
|
||||||
.filter(|p| !p.is_global())
|
.filter(|p| !p.is_global())
|
||||||
.filter_map(|pred| {
|
.filter_map(|pred| {
|
||||||
// Note that we do not want to deal with qualified predicates here.
|
// Note that we do not want to deal with qualified predicates here.
|
||||||
|
|
|
@ -2104,7 +2104,7 @@ pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
|
||||||
.filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
|
.filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
|
||||||
traits::impossible_predicates(
|
traits::impossible_predicates(
|
||||||
cx.tcx,
|
cx.tcx,
|
||||||
traits::elaborate_predicates(cx.tcx, predicates)
|
traits::elaborate(cx.tcx, predicates)
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue