Skip to content

Commit

Permalink
Allow the elaborator to only filter to real supertraits
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed Apr 11, 2023
1 parent 4560b61 commit 7ec72ef
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 48 deletions.
68 changes: 37 additions & 31 deletions compiler/rustc_hir_analysis/src/astconv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1663,39 +1663,45 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
})
});

let existential_projections = projection_bounds.iter().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
// parameters.
let references_self = b.projection_ty.substs.iter().skip(1).any(|arg| {
if arg.walk().any(|arg| arg == dummy_self.into()) {
return true;
let existential_projections = projection_bounds
.iter()
// 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
// parameters.
let references_self = b.projection_ty.substs.iter().skip(1).any(|arg| {
if arg.walk().any(|arg| arg == dummy_self.into()) {
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
.map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait));
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_hir_typeck/src/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// and we want to keep inference generally in the same order of
// the registered obligations.
predicates.rev(),
) {
)
// We only care about self bounds
.filter_only_self()
{
debug!(?pred);
let bound_predicate = pred.kind();

Expand Down
41 changes: 26 additions & 15 deletions compiler/rustc_infer/src/traits/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ impl<'tcx> Extend<ty::Predicate<'tcx>> for PredicateSet<'tcx> {
pub struct Elaborator<'tcx, O> {
stack: Vec<O>,
visited: PredicateSet<'tcx>,
only_self: bool,
}

/// Describes how to elaborate an obligation into a sub-obligation.
Expand Down Expand Up @@ -170,7 +171,8 @@ pub fn elaborate<'tcx, O: Elaboratable<'tcx>>(
tcx: TyCtxt<'tcx>,
obligations: impl IntoIterator<Item = 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
}
Expand All @@ -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())));
}

/// 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) {
let tcx = self.visited.tcx;

let bound_predicate = elaboratable.predicate().kind();
match bound_predicate.skip_binder() {
ty::PredicateKind::Clause(ty::Clause::Trait(data)) => {
// Get predicates declared on the trait.
let predicates = tcx.implied_predicates_of(data.def_id());
// Get predicates implied by the trait, or only super predicates if we only care about self predicates.
let predicates = if self.only_self {
tcx.super_predicates_of(data.def_id())
} else {
tcx.implied_predicates_of(data.def_id())
};

let obligations =
predicates.predicates.iter().enumerate().map(|(index, &(mut pred, span))| {
Expand Down Expand Up @@ -350,18 +363,16 @@ pub fn supertraits<'tcx>(
tcx: TyCtxt<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
) -> impl Iterator<Item = ty::PolyTraitRef<'tcx>> {
let pred: ty::Predicate<'tcx> = trait_ref.to_predicate(tcx);
FilterToTraits::new(elaborate(tcx, [pred]))
elaborate(tcx, [trait_ref.to_predicate(tcx)]).filter_only_self().filter_to_traits()
}

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(
tcx,
trait_refs.map(|trait_ref| -> ty::Predicate<'tcx> { trait_ref.to_predicate(tcx) }),
))
elaborate(tcx, trait_refs.map(|trait_ref| trait_ref.to_predicate(tcx)))
.filter_only_self()
.filter_to_traits()
}

/// A specialized variant of `elaborate` that only elaborates trait references that may
Expand Down Expand Up @@ -402,18 +413,18 @@ pub fn transitive_bounds_that_define_assoc_type<'tcx>(
// 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
/// just trait references.
pub struct FilterToTraits<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> {
type Item = ty::PolyTraitRef<'tcx>;

Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_lint/src/unused.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,8 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
ty::Adt(def, _) => is_def_must_use(cx, def.did(), span),
ty::Alias(ty::Opaque, ty::AliasTy { def_id: def, .. }) => {
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)| {
// We only look at the `DefId`, so it is safe to skip the binder here.
if let ty::PredicateKind::Clause(ty::Clause::Trait(
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_trait_selection/src/solve/assembly/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
let tcx = self.tcx();
let own_bounds: FxIndexSet<_> =
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
// list. We want to only consider these pre-elaborated projections, and not other
// projection predicates that we reach by elaborating the principal trait ref,
Expand Down
20 changes: 20 additions & 0 deletions tests/ui/traits/alias/dont-elaborate-non-self.stderr
Original file line number Diff line number Diff line change
@@ -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`.
Original file line number Diff line number Diff line change
@@ -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`.

0 comments on commit 7ec72ef

Please sign in to comment.