1
Fork 0

Allow the elaborator to only filter to real supertraits

This commit is contained in:
Michael Goulet 2023-02-02 21:22:02 +00:00
parent 4560b61cd1
commit 7ec72efe10
7 changed files with 106 additions and 47 deletions

View file

@ -1663,39 +1663,45 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
}) })
}); });
let existential_projections = projection_bounds.iter().map(|(bound, _)| { let existential_projections = projection_bounds
bound.map_bound(|mut b| { .iter()
assert_eq!(b.projection_ty.self_ty(), dummy_self); // We filter out traits that don't have `Self` as their self type above,
// we need to do the same for projections.
.filter(|(bound, _)| bound.skip_binder().self_ty() == dummy_self)
.map(|(bound, _)| {
bound.map_bound(|mut b| {
assert_eq!(b.projection_ty.self_ty(), dummy_self);
// Like for trait refs, verify that `dummy_self` did not leak inside default type // Like for trait refs, verify that `dummy_self` did not leak inside default type
// parameters. // parameters.
let references_self = b.projection_ty.substs.iter().skip(1).any(|arg| { let references_self = b.projection_ty.substs.iter().skip(1).any(|arg| {
if arg.walk().any(|arg| arg == dummy_self.into()) { if arg.walk().any(|arg| arg == dummy_self.into()) {
return true; return true;
}
false
});
if references_self {
let guar = tcx.sess.delay_span_bug(
span,
"trait object projection bounds reference `Self`",
);
let substs: Vec<_> = b
.projection_ty
.substs
.iter()
.map(|arg| {
if arg.walk().any(|arg| arg == dummy_self.into()) {
return tcx.ty_error(guar).into();
}
arg
})
.collect();
b.projection_ty.substs = tcx.mk_substs(&substs);
} }
false
});
if references_self {
let guar = tcx
.sess
.delay_span_bug(span, "trait object projection bounds reference `Self`");
let substs: Vec<_> = b
.projection_ty
.substs
.iter()
.map(|arg| {
if arg.walk().any(|arg| arg == dummy_self.into()) {
return tcx.ty_error(guar).into();
}
arg
})
.collect();
b.projection_ty.substs = tcx.mk_substs(&substs);
}
ty::ExistentialProjection::erase_self_ty(tcx, b) ty::ExistentialProjection::erase_self_ty(tcx, b)
}) })
}); });
let regular_trait_predicates = existential_trait_refs let regular_trait_predicates = existential_trait_refs
.map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait)); .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait));

View file

@ -210,7 +210,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// and we want to keep inference generally in the same order of // and we want to keep inference generally in the same order of
// the registered obligations. // the registered obligations.
predicates.rev(), predicates.rev(),
) { )
// We only care about self bounds
.filter_only_self()
{
debug!(?pred); debug!(?pred);
let bound_predicate = pred.kind(); let bound_predicate = pred.kind();

View file

@ -69,6 +69,7 @@ impl<'tcx> Extend<ty::Predicate<'tcx>> for PredicateSet<'tcx> {
pub struct Elaborator<'tcx, O> { pub struct Elaborator<'tcx, O> {
stack: Vec<O>, stack: Vec<O>,
visited: PredicateSet<'tcx>, visited: PredicateSet<'tcx>,
only_self: bool,
} }
/// Describes how to elaborate an obligation into a sub-obligation. /// Describes how to elaborate an obligation into a sub-obligation.
@ -170,7 +171,8 @@ pub fn elaborate<'tcx, O: Elaboratable<'tcx>>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
obligations: impl IntoIterator<Item = O>, obligations: impl IntoIterator<Item = O>,
) -> Elaborator<'tcx, O> { ) -> 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), only_self: false };
elaborator.extend_deduped(obligations); elaborator.extend_deduped(obligations);
elaborator elaborator
} }
@ -185,14 +187,25 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> {
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())));
} }
/// Filter to only the supertraits of trait predicates, i.e. only the predicates
/// that have `Self` as their self type, instead of all implied predicates.
pub fn filter_only_self(mut self) -> Self {
self.only_self = true;
self
}
fn elaborate(&mut self, elaboratable: &O) { fn elaborate(&mut self, elaboratable: &O) {
let tcx = self.visited.tcx; let tcx = self.visited.tcx;
let bound_predicate = elaboratable.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 implied by the trait, or only super predicates if we only care about self predicates.
let predicates = tcx.implied_predicates_of(data.def_id()); let predicates = if self.only_self {
tcx.super_predicates_of(data.def_id())
} else {
tcx.implied_predicates_of(data.def_id())
};
let obligations = let obligations =
predicates.predicates.iter().enumerate().map(|(index, &(mut pred, span))| { predicates.predicates.iter().enumerate().map(|(index, &(mut pred, span))| {
@ -350,18 +363,16 @@ 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>> {
let pred: ty::Predicate<'tcx> = trait_ref.to_predicate(tcx); elaborate(tcx, [trait_ref.to_predicate(tcx)]).filter_only_self().filter_to_traits()
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( elaborate(tcx, trait_refs.map(|trait_ref| trait_ref.to_predicate(tcx)))
tcx, .filter_only_self()
trait_refs.map(|trait_ref| -> ty::Predicate<'tcx> { trait_ref.to_predicate(tcx) }), .filter_to_traits()
))
} }
/// A specialized variant of `elaborate` that only elaborates trait references that may /// A specialized variant of `elaborate` that only elaborates trait references that may
@ -402,18 +413,18 @@ pub fn transitive_bounds_that_define_assoc_type<'tcx>(
// Other // Other
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
impl<'tcx> Elaborator<'tcx, ty::Predicate<'tcx>> {
fn filter_to_traits(self) -> FilterToTraits<Self> {
FilterToTraits { base_iterator: self }
}
}
/// A filter around an iterator of predicates that makes it yield up /// A filter around an iterator of predicates that makes it yield up
/// just trait references. /// just trait references.
pub struct FilterToTraits<I> { pub struct FilterToTraits<I> {
base_iterator: I, base_iterator: I,
} }
impl<I> FilterToTraits<I> {
fn new(base: I) -> FilterToTraits<I> {
FilterToTraits { base_iterator: base }
}
}
impl<'tcx, I: Iterator<Item = ty::Predicate<'tcx>>> Iterator for FilterToTraits<I> { impl<'tcx, I: Iterator<Item = ty::Predicate<'tcx>>> Iterator for FilterToTraits<I> {
type Item = ty::PolyTraitRef<'tcx>; type Item = ty::PolyTraitRef<'tcx>;

View file

@ -255,6 +255,8 @@ 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(cx.tcx, cx.tcx.explicit_item_bounds(def).iter().cloned()) elaborate(cx.tcx, cx.tcx.explicit_item_bounds(def).iter().cloned())
// We only care about self bounds for the impl-trait
.filter_only_self()
.find_map(|(pred, _span)| { .find_map(|(pred, _span)| {
// We only look at the `DefId`, so it is safe to skip the binder here. // We only look at the `DefId`, so it is safe to skip the binder here.
if let ty::PredicateKind::Clause(ty::Clause::Trait( if let ty::PredicateKind::Clause(ty::Clause::Trait(

View file

@ -498,7 +498,10 @@ 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(tcx, own_bounds.iter().copied()) { for assumption in elaborate(tcx, own_bounds.iter().copied())
// we only care about bounds that match the `Self` type
.filter_only_self()
{
// 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,

View file

@ -0,0 +1,20 @@
error[E0277]: the size for values of type `(dyn Fn() -> Fut + 'static)` cannot be known at compilation time
--> $DIR/dont-elaborate-non-self.rs:7:11
|
LL | fn f<Fut>(a: dyn F<Fut>) {}
| ^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `(dyn Fn() -> Fut + 'static)`
= help: unsized fn params are gated as an unstable feature
help: you can use `impl Trait` as the argument type
|
LL | fn f<Fut>(a: impl F<Fut>) {}
| ~~~~
help: function arguments must have a statically known size, borrowed types always have a known size
|
LL | fn f<Fut>(a: &dyn F<Fut>) {}
| +
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.

View file

@ -0,0 +1,14 @@
error[E0308]: mismatched types
--> $DIR/alias-where-clause-isnt-supertrait.rs:27:5
|
LL | fn test(x: &dyn C) -> &dyn B {
| ------ expected `&dyn B` because of return type
LL | x
| ^ expected trait `B`, found trait `C`
|
= note: expected reference `&dyn B`
found reference `&dyn C`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.