From 213be324fec8f56200529f5b003ded879de84056 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 22 Nov 2022 18:14:38 +0000 Subject: [PATCH 1/4] Filter out non-Self supertrait predicates in unused_must_use --- compiler/rustc_lint/src/unused.rs | 11 +++++------ src/test/ui/lint/unused/trait-alias-supertrait.rs | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 src/test/ui/lint/unused/trait-alias-supertrait.rs diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 5c9217db11844..371ba659a1fc6 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -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 } diff --git a/src/test/ui/lint/unused/trait-alias-supertrait.rs b/src/test/ui/lint/unused/trait-alias-supertrait.rs new file mode 100644 index 0000000000000..46f00c06bf1ca --- /dev/null +++ b/src/test/ui/lint/unused/trait-alias-supertrait.rs @@ -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 = Sized where T: Iterator; + +fn test() -> impl Foo {} + +fn main() { + test::>(); +} From c8ebde4bc89a8424e24b4c99bcc75759280cb747 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 22 Nov 2022 18:22:38 +0000 Subject: [PATCH 2/4] Filter out non-Self supertrait predicates in deduce_signature_from_predicates --- compiler/rustc_hir_typeck/src/closure.rs | 25 +++++++++++++++---- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 6 ++++- .../ui/closures/self-supertrait-bounds.rs | 14 +++++++++++ 3 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 src/test/ui/closures/self-supertrait-bounds.rs diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 6cf9e23b40b0e..b3fb4e0239910 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -169,6 +169,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> (Option>, Option) { 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, ..) => { @@ -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)) @@ -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, Span)>, ) -> (Option>, Option) { let mut expected_sig = None; @@ -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, @@ -228,10 +239,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // like `F : Fn`. 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) = diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index c7bfe99aa9a8a..8148eaa59d348 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -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); diff --git a/src/test/ui/closures/self-supertrait-bounds.rs b/src/test/ui/closures/self-supertrait-bounds.rs new file mode 100644 index 0000000000000..f4f1cea6b8176 --- /dev/null +++ b/src/test/ui/closures/self-supertrait-bounds.rs @@ -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 = Fn(i32) where F: Fn(u32); + +fn alias, F>(_: T, _: F) {} + +fn main() { + alias(|_| {}, |_| {}); +} From 6172aa8e248dc3d94fcb115b8beabbe290c3784a Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 22 Nov 2022 21:19:13 +0000 Subject: [PATCH 3/4] Filter out non-Self supertrait predicates in object type astconv --- compiler/rustc_hir_analysis/src/astconv/mod.rs | 12 +++++++++--- .../ui/traits/alias/dont-elaborate-non-self.rs | 10 ++++++++++ .../traits/alias/dont-elaborate-non-self.stderr | 16 ++++++++++++++++ 3 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 src/test/ui/traits/alias/dont-elaborate-non-self.rs create mode 100644 src/test/ui/traits/alias/dont-elaborate-non-self.stderr diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index e744ed2dcc547..b397e72458c19 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -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()) @@ -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. @@ -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. diff --git a/src/test/ui/traits/alias/dont-elaborate-non-self.rs b/src/test/ui/traits/alias/dont-elaborate-non-self.rs new file mode 100644 index 0000000000000..4f9eaacb8ed06 --- /dev/null +++ b/src/test/ui/traits/alias/dont-elaborate-non-self.rs @@ -0,0 +1,10 @@ +#![feature(trait_alias)] + +use std::future::Future; + +trait F> = Fn() -> Fut; + +fn f(a: dyn F) {} +//~^ ERROR the size for values of type `(dyn Fn() -> Fut + 'static)` cannot be known at compilation time + +fn main() {} diff --git a/src/test/ui/traits/alias/dont-elaborate-non-self.stderr b/src/test/ui/traits/alias/dont-elaborate-non-self.stderr new file mode 100644 index 0000000000000..9f43a2e8cef33 --- /dev/null +++ b/src/test/ui/traits/alias/dont-elaborate-non-self.stderr @@ -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(a: dyn F) {} + | ^ 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(a: &dyn F) {} + | + + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. From 1e39dc23b21a6ca0bfbcb596f9b954715bae59fc Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 22 Nov 2022 21:50:42 +0000 Subject: [PATCH 4/4] Elaborate only supertraits that share the same self type as the predicate --- compiler/rustc_infer/src/traits/util.rs | 34 ++++++++++++------- .../alias-where-clause-isnt-supertrait.rs | 33 ++++++++++++++++++ .../alias-where-clause-isnt-supertrait.stderr | 14 ++++++++ 3 files changed, 69 insertions(+), 12 deletions(-) create mode 100644 src/test/ui/traits/trait-upcasting/alias-where-clause-isnt-supertrait.rs create mode 100644 src/test/ui/traits/trait-upcasting/alias-where-clause-isnt-supertrait.stderr diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index b2a31ac7e6f1a..06c8e5b64f586 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -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; @@ -132,8 +132,11 @@ fn predicate_obligation<'tcx>( } impl<'tcx> Elaborator<'tcx> { - pub fn filter_to_traits(self) -> FilterToTraits { - FilterToTraits::new(self) + pub fn filter_to_traits( + self, + filter_self_ty: Option>>, + ) -> FilterToTraits<'tcx, Self> { + FilterToTraits::new(self, filter_self_ty) } fn elaborate(&mut self, obligation: &PredicateObligation<'tcx>) { @@ -312,20 +315,20 @@ impl<'tcx> Iterator for Elaborator<'tcx> { // Supertrait iterator /////////////////////////////////////////////////////////////////////////// -pub type Supertraits<'tcx> = FilterToTraits>; +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>, ) -> 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 @@ -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 { +pub struct FilterToTraits<'tcx, I> { base_iterator: I, + filter_self_ty: Option>>, } -impl FilterToTraits { - fn new(base: I) -> FilterToTraits { - FilterToTraits { base_iterator: base } +impl<'tcx, I> FilterToTraits<'tcx, I> { + fn new(base: I, filter_self_ty: Option>>) -> FilterToTraits<'tcx, I> { + FilterToTraits { base_iterator: base, filter_self_ty } } } -impl<'tcx, I: Iterator>> Iterator for FilterToTraits { +impl<'tcx, I: Iterator>> Iterator for FilterToTraits<'tcx, I> { type Item = ty::PolyTraitRef<'tcx>; fn next(&mut self) -> Option> { 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)); } } diff --git a/src/test/ui/traits/trait-upcasting/alias-where-clause-isnt-supertrait.rs b/src/test/ui/traits/trait-upcasting/alias-where-clause-isnt-supertrait.rs new file mode 100644 index 0000000000000..4a5e445d1efbe --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/alias-where-clause-isnt-supertrait.rs @@ -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 = &(); +} diff --git a/src/test/ui/traits/trait-upcasting/alias-where-clause-isnt-supertrait.stderr b/src/test/ui/traits/trait-upcasting/alias-where-clause-isnt-supertrait.stderr new file mode 100644 index 0000000000000..5574a0320895e --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/alias-where-clause-isnt-supertrait.stderr @@ -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`.