diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs index 31b87717a4b13..24dba2f9f4197 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs @@ -1,4 +1,4 @@ -use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::struct_span_code_err; use rustc_hir as hir; @@ -61,7 +61,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } let mut trait_bounds = vec![]; - let mut projection_bounds = vec![]; + let mut projection_bounds = FxIndexMap::default(); for (pred, span) in bounds.clauses() { let bound_pred = pred.kind(); match bound_pred.skip_binder() { @@ -70,7 +70,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { trait_bounds.push((bound_pred.rebind(trait_pred.trait_ref), span)); } ty::ClauseKind::Projection(proj) => { - projection_bounds.push((bound_pred.rebind(proj), span)); + let anon = tcx.anonymize_bound_vars(bound_pred.rebind(proj)); + projection_bounds.insert(anon, (bound_pred.rebind(proj), span)); } ty::ClauseKind::TypeOutlives(_) => { // Do nothing, we deal with regions separately @@ -87,8 +88,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // Expand trait aliases recursively and check that only one regular (non-auto) trait // is used and no 'maybe' bounds are used. - let expanded_traits = - traits::expand_trait_aliases(tcx, trait_bounds.iter().map(|&(a, b)| (a, b))); + let mut trait_alias_projection_bounds = FxIndexSet::default(); + let expanded_traits = traits::expand_trait_aliases( + tcx, + &mut trait_alias_projection_bounds, + trait_bounds.iter().map(|&(a, b)| (a, b)), + ); let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) = expanded_traits.partition(|i| tcx.trait_is_auto(i.trait_ref().def_id())); @@ -162,6 +167,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } ty::PredicateKind::Clause(ty::ClauseKind::Projection(pred)) => { let pred = bound_predicate.rebind(pred); + + let pred_key = tcx.anonymize_bound_vars(pred); + if !trait_alias_projection_bounds.contains(&pred_key) { + projection_bounds.shift_remove(&pred_key); + } + // A `Self` within the original bound will be instantiated with a // `trait_object_dummy_self`, so check for that. let references_self = match pred.skip_binder().term.unpack() { @@ -188,8 +199,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // `dyn MyTrait`, which is uglier but works. See // the discussion in #56288 for alternatives. if !references_self { - // Include projections defined on supertraits. - implied_projection_bounds.push(pred); + if trait_alias_projection_bounds.contains(&pred_key) { + // Include projections defined on supertraits. + projection_bounds.insert(pred_key, (pred, supertrait_span)); + } else { + // Include projections defined on supertraits. + implied_projection_bounds.push(pred); + } } self.check_elaborated_projection_mentions_input_lifetimes( @@ -207,7 +223,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // So every `Projection` clause is an `Assoc = Foo` bound. `associated_types` contains all associated // types's `DefId`, so the following loop removes all the `DefIds` of the associated types that have a // corresponding `Projection` clause - for (projection_bound, span) in &projection_bounds { + for (projection_bound, span) in projection_bounds.values() { let def_id = projection_bound.projection_def_id(); let trait_ref = tcx.anonymize_bound_vars( projection_bound.map_bound(|p| p.projection_term.trait_ref(tcx)), @@ -314,7 +330,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }) }); - let existential_projections = projection_bounds.iter().map(|(bound, _)| { + let existential_projections = projection_bounds.values().map(|(bound, _)| { bound.map_bound(|mut b| { assert_eq!(b.projection_term.self_ty(), dummy_self); diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 039c117c09995..8613f1c83f868 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -903,6 +903,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // For trait aliases, recursively assume all explicitly named traits are relevant for expansion in traits::expand_trait_aliases( self.tcx, + &mut Default::default(), iter::once((ty::Binder::dummy(trait_ref), self.span)), ) { let bound_trait_ref = expansion.trait_ref(); diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index afdec7a86d445..5bc2a40016e17 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -80,19 +80,12 @@ impl<'tcx> Relate> for &'tcx ty::List RelateResult<'tcx, Self> { let tcx = relation.cx(); - // FIXME: this is wasteful, but want to do a perf run to see how slow it is. - // We need to perform this deduplication as we sometimes generate duplicate projections - // in `a`. - let mut a_v: Vec<_> = a.into_iter().collect(); - let mut b_v: Vec<_> = b.into_iter().collect(); - a_v.dedup(); - b_v.dedup(); - if a_v.len() != b_v.len() { + if a.len() != b.len() { return Err(TypeError::ExistentialMismatch(ExpectedFound::new(a, b))); } - let v = iter::zip(a_v, b_v).map(|(ep_a, ep_b)| { - match (ep_a.skip_binder(), ep_b.skip_binder()) { + let v = + iter::zip(a, b).map(|(ep_a, ep_b)| match (ep_a.skip_binder(), ep_b.skip_binder()) { (ty::ExistentialPredicate::Trait(a), ty::ExistentialPredicate::Trait(b)) => { Ok(ep_a.rebind(ty::ExistentialPredicate::Trait( relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(), @@ -109,8 +102,7 @@ impl<'tcx> Relate> for &'tcx ty::List Ok(ep_a.rebind(ty::ExistentialPredicate::AutoTrait(a))), _ => Err(TypeError::ExistentialMismatch(ExpectedFound::new(a, b))), - } - }); + }); tcx.mk_poly_existential_predicates_from_iter(v) } } diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index da1045b664afc..4eb090c114028 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -1,6 +1,6 @@ use std::collections::BTreeMap; -use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::Diag; use rustc_hir::def_id::DefId; use rustc_infer::infer::InferCtxt; @@ -25,9 +25,11 @@ use tracing::debug; /// `Read + Write + Sync + Send`. /// Expansion is done via a DFS (depth-first search), and the `visited` field /// is used to avoid cycles. -pub struct TraitAliasExpander<'tcx> { +pub struct TraitAliasExpander<'a, 'tcx> { tcx: TyCtxt<'tcx>, stack: Vec>, + // TODO: + projection_bounds: &'a mut FxIndexSet>, } /// Stores information about the expansion of a trait via a path of zero or more trait aliases. @@ -85,16 +87,17 @@ impl<'tcx> TraitAliasExpansionInfo<'tcx> { } } -pub fn expand_trait_aliases<'tcx>( +pub fn expand_trait_aliases<'a, 'tcx>( tcx: TyCtxt<'tcx>, + projection_bounds: &'a mut FxIndexSet>, trait_refs: impl Iterator, Span)>, -) -> TraitAliasExpander<'tcx> { +) -> TraitAliasExpander<'a, 'tcx> { let items: Vec<_> = trait_refs.map(|(trait_ref, span)| TraitAliasExpansionInfo::new(trait_ref, span)).collect(); - TraitAliasExpander { tcx, stack: items } + TraitAliasExpander { tcx, stack: items, projection_bounds } } -impl<'tcx> TraitAliasExpander<'tcx> { +impl<'tcx> TraitAliasExpander<'_, 'tcx> { /// If `item` is a trait alias and its predicate has not yet been visited, then expands `item` /// to the definition, pushes the resulting expansion onto `self.stack`, and returns `false`. /// Otherwise, immediately returns `true` if `item` is a regular trait, or `false` if it is a @@ -129,20 +132,26 @@ impl<'tcx> TraitAliasExpander<'tcx> { let predicates = tcx.explicit_super_predicates_of(trait_ref.def_id()); debug!(?predicates); - let items = predicates.skip_binder().iter().rev().filter_map(|(pred, span)| { - pred.instantiate_supertrait(tcx, trait_ref) - .as_trait_clause() - .map(|trait_ref| item.clone_and_push(trait_ref.map_bound(|t| t.trait_ref), *span)) - }); - debug!("expand_trait_aliases: items={:?}", items.clone().collect::>()); - - self.stack.extend(items); + for &(pred, span) in predicates.skip_binder().iter().rev() { + let pred = pred.instantiate_supertrait(tcx, trait_ref); + if let Some(trait_pred) = pred.as_trait_clause() { + // FIXME(negative_bounds): Polarity + self.stack.push( + item.clone_and_push( + trait_pred.map_bound(|trait_pred| trait_pred.trait_ref), + span, + ), + ); + } else if let Some(proj) = pred.as_projection_clause() { + self.projection_bounds.insert(self.tcx.anonymize_bound_vars(proj)); + } + } false } } -impl<'tcx> Iterator for TraitAliasExpander<'tcx> { +impl<'tcx> Iterator for TraitAliasExpander<'_, 'tcx> { type Item = TraitAliasExpansionInfo<'tcx>; fn size_hint(&self) -> (usize, Option) { diff --git a/tests/ui/associated-types/issue-59324.rs b/tests/ui/associated-types/issue-59324.rs index 7421e08c89888..91ba5f615496f 100644 --- a/tests/ui/associated-types/issue-59324.rs +++ b/tests/ui/associated-types/issue-59324.rs @@ -21,8 +21,6 @@ pub trait ThriftService: } fn with_factory(factory: dyn ThriftService<()>) {} -//~^ ERROR the trait bound `(): Foo` is not satisfied -//~| ERROR the trait bound `(): Foo` is not satisfied -//~| ERROR cannot be known at compilation time +//~^ ERROR cannot be known at compilation time fn main() {} diff --git a/tests/ui/issues/issue-59326.rs b/tests/ui/issues/issue-59326.rs index e9634ad9fd8fe..adb4a25651447 100644 --- a/tests/ui/issues/issue-59326.rs +++ b/tests/ui/issues/issue-59326.rs @@ -1,4 +1,3 @@ -//@ check-pass trait Service { type S; } @@ -23,4 +22,5 @@ fn make_server() -> Box> { fn main() { build_server(|| make_server()) + //~^ ERROR mismatched types } diff --git a/tests/ui/issues/issue-59326.stderr b/tests/ui/issues/issue-59326.stderr new file mode 100644 index 0000000000000..593e416db15a6 --- /dev/null +++ b/tests/ui/issues/issue-59326.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/issue-59326.rs:24:21 + | +LL | build_server(|| make_server()) + | ^^^^^^^^^^^^^ expected trait `HttpService<(), S = ()>`, found trait `HttpService<_>` + | + = note: expected struct `Box<(dyn HttpService<(), S = ()> + 'static)>` + found struct `Box<(dyn HttpService<_> + 'static)>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/traits/alias/dont-elaborate-non-self.stderr b/tests/ui/traits/alias/dont-elaborate-non-self.stderr index 7e76108837ce1..952f78dd3da13 100644 --- a/tests/ui/traits/alias/dont-elaborate-non-self.stderr +++ b/tests/ui/traits/alias/dont-elaborate-non-self.stderr @@ -1,10 +1,10 @@ -error[E0277]: the size for values of type `(dyn Fn<()> + 'static)` cannot be known at compilation time +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:14 | 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<()> + 'static)` + = 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 |