Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Be more careful about elaboration around trait aliases #104745

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions compiler/rustc_hir_analysis/src/astconv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1378,7 +1378,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {

let bound_predicate = obligation.predicate.kind();
match bound_predicate.skip_binder() {
ty::PredicateKind::Trait(pred) => {
ty::PredicateKind::Trait(pred) if pred.self_ty() == dummy_self => {
let pred = bound_predicate.rebind(pred);
associated_types.entry(span).or_default().extend(
tcx.associated_items(pred.def_id())
Expand All @@ -1387,7 +1387,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
.map(|item| item.def_id),
);
}
ty::PredicateKind::Projection(pred) => {
ty::PredicateKind::Projection(pred)
if pred.projection_ty.self_ty() == dummy_self =>
{
let pred = bound_predicate.rebind(pred);
// A `Self` within the original bound will be substituted with a
// `trait_object_dummy_self`, so check for that.
Expand Down Expand Up @@ -1510,7 +1512,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {

let existential_projections = bounds.projection_bounds.iter().map(|(bound, _)| {
bound.map_bound(|mut b| {
assert_eq!(b.projection_ty.self_ty(), dummy_self);
assert_eq!(
b.projection_ty.self_ty(),
dummy_self,
"projection doesn't have the dummy self as its `Self` type: {b:?}"
);

// Like for trait refs, verify that `dummy_self` did not leak inside default type
// parameters.
Expand Down
25 changes: 20 additions & 5 deletions compiler/rustc_hir_typeck/src/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) {
match *expected_ty.kind() {
ty::Opaque(def_id, substs) => self.deduce_signature_from_predicates(
// Elaborating expectations from explicit_item_bounds shouldn't include any variable shenanigans
|ty| ty == expected_ty,
self.tcx.bound_explicit_item_bounds(def_id).subst_iter_copied(self.tcx, substs),
),
ty::Dynamic(ref object_type, ..) => {
Expand All @@ -181,9 +183,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.and_then(|did| self.tcx.fn_trait_kind_from_lang_item(did));
(sig, kind)
}
ty::Infer(ty::TyVar(vid)) => self.deduce_signature_from_predicates(
self.obligations_for_self_ty(vid).map(|obl| (obl.predicate, obl.cause.span)),
),
ty::Infer(ty::TyVar(vid)) => {
let root_vid = self.root_var(vid);
self.deduce_signature_from_predicates(
// We need to do equality "modulo root vids" here, since that's
// how `obligations_for_self_ty` filters its predicates.
|ty| self.self_type_matches_expected_vid(ty, root_vid),
self.obligations_for_self_ty(root_vid)
.map(|obl| (obl.predicate, obl.cause.span)),
)
}
ty::FnPtr(sig) => {
let expected_sig = ExpectedSig { cause_span: None, sig };
(Some(expected_sig), Some(ty::ClosureKind::Fn))
Expand All @@ -194,6 +203,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

fn deduce_signature_from_predicates(
&self,
eq_expected_ty: impl Fn(Ty<'tcx>) -> bool,
predicates: impl DoubleEndedIterator<Item = (ty::Predicate<'tcx>, Span)>,
) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) {
let mut expected_sig = None;
Expand All @@ -213,6 +223,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// the complete signature.
if expected_sig.is_none()
&& let ty::PredicateKind::Projection(proj_predicate) = bound_predicate.skip_binder()
&& eq_expected_ty(proj_predicate.projection_ty.self_ty())
{
expected_sig = self.normalize_associated_types_in(
obligation.cause.span,
Expand All @@ -228,10 +239,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// like `F : Fn<A>`. Note that due to subtyping we could encounter
// many viable options, so pick the most restrictive.
let trait_def_id = match bound_predicate.skip_binder() {
ty::PredicateKind::Projection(data) => {
ty::PredicateKind::Projection(data)
if eq_expected_ty(data.projection_ty.self_ty()) =>
{
Some(data.projection_ty.trait_def_id(self.tcx))
}
ty::PredicateKind::Trait(data) => Some(data.def_id()),
ty::PredicateKind::Trait(data) if eq_expected_ty(data.self_ty()) => {
Some(data.def_id())
}
_ => None,
};
if let Some(closure_kind) =
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -640,7 +640,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}

#[instrument(skip(self), level = "debug")]
fn self_type_matches_expected_vid(&self, self_ty: Ty<'tcx>, expected_vid: ty::TyVid) -> bool {
pub(in super::super) fn self_type_matches_expected_vid(
&self,
self_ty: Ty<'tcx>,
expected_vid: ty::TyVid,
) -> bool {
let self_ty = self.shallow_resolve(self_ty);
debug!(?self_ty);

Expand Down
34 changes: 22 additions & 12 deletions compiler/rustc_infer/src/traits/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use smallvec::smallvec;
use crate::infer::outlives::components::{push_outlives_components, Component};
use crate::traits::{Obligation, ObligationCause, PredicateObligation};
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
use rustc_middle::ty::{self, ToPredicate, TyCtxt};
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
use rustc_span::symbol::Ident;
use rustc_span::Span;

Expand Down Expand Up @@ -132,8 +132,11 @@ fn predicate_obligation<'tcx>(
}

impl<'tcx> Elaborator<'tcx> {
pub fn filter_to_traits(self) -> FilterToTraits<Self> {
FilterToTraits::new(self)
pub fn filter_to_traits(
self,
filter_self_ty: Option<ty::Binder<'tcx, Ty<'tcx>>>,
) -> FilterToTraits<'tcx, Self> {
FilterToTraits::new(self, filter_self_ty)
}

fn elaborate(&mut self, obligation: &PredicateObligation<'tcx>) {
Expand Down Expand Up @@ -312,20 +315,20 @@ impl<'tcx> Iterator for Elaborator<'tcx> {
// Supertrait iterator
///////////////////////////////////////////////////////////////////////////

pub type Supertraits<'tcx> = FilterToTraits<Elaborator<'tcx>>;
pub type Supertraits<'tcx> = FilterToTraits<'tcx, Elaborator<'tcx>>;

pub fn supertraits<'tcx>(
tcx: TyCtxt<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
) -> Supertraits<'tcx> {
elaborate_trait_ref(tcx, trait_ref).filter_to_traits()
elaborate_trait_ref(tcx, trait_ref).filter_to_traits(Some(trait_ref.self_ty()))
}

pub fn transitive_bounds<'tcx>(
tcx: TyCtxt<'tcx>,
bounds: impl Iterator<Item = ty::PolyTraitRef<'tcx>>,
) -> Supertraits<'tcx> {
elaborate_trait_refs(tcx, bounds).filter_to_traits()
elaborate_trait_refs(tcx, bounds).filter_to_traits(None)
}

/// A specialized variant of `elaborate_trait_refs` that only elaborates trait references that may
Expand Down Expand Up @@ -370,22 +373,29 @@ pub fn transitive_bounds_that_define_assoc_type<'tcx>(

/// A filter around an iterator of predicates that makes it yield up
/// just trait references.
pub struct FilterToTraits<I> {
pub struct FilterToTraits<'tcx, I> {
base_iterator: I,
filter_self_ty: Option<ty::Binder<'tcx, Ty<'tcx>>>,
}

impl<I> FilterToTraits<I> {
fn new(base: I) -> FilterToTraits<I> {
FilterToTraits { base_iterator: base }
impl<'tcx, I> FilterToTraits<'tcx, I> {
fn new(base: I, filter_self_ty: Option<ty::Binder<'tcx, Ty<'tcx>>>) -> FilterToTraits<'tcx, I> {
FilterToTraits { base_iterator: base, filter_self_ty }
}
}

impl<'tcx, I: Iterator<Item = PredicateObligation<'tcx>>> Iterator for FilterToTraits<I> {
impl<'tcx, I: Iterator<Item = PredicateObligation<'tcx>>> Iterator for FilterToTraits<'tcx, I> {
type Item = ty::PolyTraitRef<'tcx>;

fn next(&mut self) -> Option<ty::PolyTraitRef<'tcx>> {
while let Some(obligation) = self.base_iterator.next() {
if let Some(data) = obligation.predicate.to_opt_poly_trait_pred() {
if let Some(data) = obligation.predicate.to_opt_poly_trait_pred()
// A note on binders: Elaboration means that the output predicates
// may have more bound variables than the inputs. However, since we
// only append variables to the bound vars list, it is fine to
// compare self types outside of the binders.
&& self.filter_self_ty.map_or(true, |filter_self_ty| filter_self_ty.skip_binder() == data.skip_binder().self_ty())
{
return Some(data.map_bound(|t| t.trait_ref));
}
}
Expand Down
11 changes: 5 additions & 6 deletions compiler/rustc_lint/src/unused.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,19 +251,18 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
.map(|inner| MustUsePath::Boxed(Box::new(inner)))
}
ty::Adt(def, _) => is_def_must_use(cx, def.did(), span),
ty::Opaque(def, _) => {
ty::Opaque(def, substs) => {
elaborate_predicates_with_span(
cx.tcx,
cx.tcx.explicit_item_bounds(def).iter().cloned(),
cx.tcx.bound_explicit_item_bounds(def).subst_iter_copied(cx.tcx, substs),
)
.filter_map(|obligation| {
// We only look at the `DefId`, so it is safe to skip the binder here.
if let ty::PredicateKind::Trait(ref poly_trait_predicate) =
if let ty::PredicateKind::Trait(ref trait_predicate) =
obligation.predicate.kind().skip_binder()
&& trait_predicate.self_ty() == ty
{
let def_id = poly_trait_predicate.trait_ref.def_id;

is_def_must_use(cx, def_id, span)
is_def_must_use(cx, trait_predicate.def_id(), span)
} else {
None
}
Expand Down
14 changes: 14 additions & 0 deletions src/test/ui/closures/self-supertrait-bounds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// check-pass

// Makes sure that we only consider `Self` supertrait predicates while
// elaborating during closure signature deduction.

#![feature(trait_alias)]

trait Confusing<F> = Fn(i32) where F: Fn(u32);

fn alias<T: Confusing<F>, F>(_: T, _: F) {}

fn main() {
alias(|_| {}, |_| {});
}
15 changes: 15 additions & 0 deletions src/test/ui/lint/unused/trait-alias-supertrait.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// check-pass

// Make sure that we only consider *Self* supertrait predicates
// in the `unused_must_use` lint.

#![feature(trait_alias)]
#![deny(unused_must_use)]

trait Foo<T> = Sized where T: Iterator;

fn test<T: Iterator>() -> impl Foo<T> {}

fn main() {
test::<std::iter::Once<()>>();
}
10 changes: 10 additions & 0 deletions src/test/ui/traits/alias/dont-elaborate-non-self.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#![feature(trait_alias)]

use std::future::Future;

trait F<Fut: Future<Output = usize>> = Fn() -> Fut;

fn f<Fut>(a: dyn F<Fut>) {}
//~^ ERROR the size for values of type `(dyn Fn() -> Fut + 'static)` cannot be known at compilation time

fn main() {}
16 changes: 16 additions & 0 deletions src/test/ui/traits/alias/dont-elaborate-non-self.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
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: 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,33 @@
#![feature(trait_upcasting)]
#![feature(trait_alias)]

// Although we *elaborate* `T: Alias` to `i32: B`, we should
// not consider `B` to be a supertrait of the type.
trait Alias = A where i32: B;

trait A {}

trait B {
fn test(&self);
}

trait C: Alias {}

impl A for () {}

impl C for () {}

impl B for i32 {
fn test(&self) {
println!("hi {self}");
}
}

fn test(x: &dyn C) -> &dyn B {
x
//~^ ERROR mismatched types
}

fn main() {
let x: &dyn C = &();
}
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`.