diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 56746666e2f1f..0ffd08381dafd 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2419,7 +2419,8 @@ impl<'tcx> TyCtxt<'tcx> { eps: &[ExistentialPredicate<'tcx>], ) -> &'tcx List> { assert!(!eps.is_empty()); - assert!(eps.windows(2).all(|w| w[0].stable_cmp(self, &w[1]) != Ordering::Greater)); + // Do not allow duplicate existential predicates. + assert!(eps.windows(2).all(|w| w[0].stable_cmp(self, w[1]) == Ordering::Less)); self._intern_existential_predicates(eps) } diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 7d3634a75b0a7..230a4d1957a03 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -598,20 +598,11 @@ impl<'tcx> Relate<'tcx> for &'tcx ty::List> { ) -> RelateResult<'tcx, Self> { let tcx = relation.tcx(); - // 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.sort_by(|a, b| a.stable_cmp(tcx, b)); - a_v.dedup(); - b_v.sort_by(|a, b| a.stable_cmp(tcx, b)); - b_v.dedup(); - if a_v.len() != b_v.len() { + if a.len() != b.len() { return Err(TypeError::ExistentialMismatch(expected_found(relation, a, b))); } - let v = a_v.into_iter().zip(b_v.into_iter()).map(|(ep_a, ep_b)| { + let v = a.into_iter().zip(b.into_iter()).map(|(ep_a, ep_b)| { use crate::ty::ExistentialPredicate::*; match (ep_a, ep_b) { (Trait(a), Trait(b)) => Ok(Trait(relation.relate(a, b)?)), diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index d4c8ba082751e..b83cd5888b0f7 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -19,6 +19,7 @@ use rustc_hir::def_id::DefId; use rustc_index::vec::Idx; use rustc_macros::HashStable; use rustc_span::symbol::{kw, Ident, Symbol}; +use rustc_span::DUMMY_SP; use rustc_target::abi::VariantIdx; use rustc_target::spec::abi; use std::borrow::Cow; @@ -672,15 +673,34 @@ pub enum ExistentialPredicate<'tcx> { impl<'tcx> ExistentialPredicate<'tcx> { /// Compares via an ordering that will not change if modules are reordered or other changes are /// made to the tree. In particular, this ordering is preserved across incremental compilations. - pub fn stable_cmp(&self, tcx: TyCtxt<'tcx>, other: &Self) -> Ordering { + pub fn stable_cmp(self, tcx: TyCtxt<'tcx>, other: Self) -> Ordering { use self::ExistentialPredicate::*; - match (*self, *other) { - (Trait(_), Trait(_)) => Ordering::Equal, - (Projection(ref a), Projection(ref b)) => { + // Note that we only call this method after checking that the + // given predicates represent a valid trait object. + // + // This means that we have at most one `ExistentialPredicate::Trait` + // and at most one `ExistentialPredicate::Projection` for each associated item. + match (self, other) { + (Trait(a), Trait(b)) => { + if a != b { + tcx.sess.delay_span_bug( + DUMMY_SP, + &format!("unexpected existential predicates: {:?}, {:?}", a, b), + ); + } + Ordering::Equal + } + (Projection(a), Projection(b)) => { + if a.item_def_id == b.item_def_id && a != b { + tcx.sess.delay_span_bug( + DUMMY_SP, + &format!("unexpected existential predicates: {:?}, {:?}", a, b), + ); + } tcx.def_path_hash(a.item_def_id).cmp(&tcx.def_path_hash(b.item_def_id)) } - (AutoTrait(ref a), AutoTrait(ref b)) => { - tcx.trait_def(*a).def_path_hash.cmp(&tcx.trait_def(*b).def_path_hash) + (AutoTrait(a), AutoTrait(b)) => { + tcx.trait_def(a).def_path_hash.cmp(&tcx.trait_def(b).def_path_hash) } (Trait(_), _) => Ordering::Less, (Projection(_), Trait(_)) => Ordering::Greater, diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index a743dc1cd2086..38eeaf7e337ab 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -35,6 +35,7 @@ use rustc_trait_selection::traits::error_reporting::report_object_safety_error; use rustc_trait_selection::traits::wf::object_region_bounds; use smallvec::SmallVec; +use std::cmp::Ordering; use std::collections::BTreeSet; use std::iter; use std::slice; @@ -1191,8 +1192,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .map(|x| ty::ExistentialPredicate::Projection(x.skip_binder())), ) .collect::>(); - v.sort_by(|a, b| a.stable_cmp(tcx, b)); - v.dedup(); + v.sort_by(|&a, &b| a.stable_cmp(tcx, b)); + v.dedup_by(|&mut a, &mut b| a.stable_cmp(tcx, b) == Ordering::Equal); let existential_predicates = ty::Binder::bind(tcx.mk_existential_predicates(v.into_iter())); // Use explicitly-specified region bound. diff --git a/src/test/ui/associated-types/associated-types-overridden-binding-2.rs b/src/test/ui/associated-types/associated-types-overridden-binding-2.rs index 109feb8e969a5..21aa0da05f470 100644 --- a/src/test/ui/associated-types/associated-types-overridden-binding-2.rs +++ b/src/test/ui/associated-types/associated-types-overridden-binding-2.rs @@ -1,8 +1,8 @@ +// check-pass #![feature(trait_alias)] trait I32Iterator = Iterator; fn main() { let _: &dyn I32Iterator = &vec![42].into_iter(); - //~^ ERROR type mismatch } diff --git a/src/test/ui/associated-types/associated-types-overridden-binding-2.stderr b/src/test/ui/associated-types/associated-types-overridden-binding-2.stderr deleted file mode 100644 index 9f1abf2a6c4b6..0000000000000 --- a/src/test/ui/associated-types/associated-types-overridden-binding-2.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0271]: type mismatch resolving ` as Iterator>::Item == i32` - --> $DIR/associated-types-overridden-binding-2.rs:6:43 - | -LL | let _: &dyn I32Iterator = &vec![42].into_iter(); - | ^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `u32` - | - = note: required for the cast to the object type `dyn Iterator` - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0271`. diff --git a/src/test/ui/issues/issue-59326-compile-fail.rs b/src/test/ui/issues/issue-59326-compile-fail.rs new file mode 100644 index 0000000000000..e01f42cb9b89a --- /dev/null +++ b/src/test/ui/issues/issue-59326-compile-fail.rs @@ -0,0 +1,28 @@ +trait Service { + type S; +} + +trait Framing { + type F; +} + +impl Framing for () { + type F = (); +} + +impl Framing for u32 { + type F = u32; +} + +trait HttpService: Service {} + +fn build_server Box>>(_: F) {} + +fn make_server() -> Box> { + unimplemented!() +} + +fn main() { + build_server(|| make_server()) + //~^ ERROR type mismatch +} diff --git a/src/test/ui/issues/issue-59326-compile-fail.stderr b/src/test/ui/issues/issue-59326-compile-fail.stderr new file mode 100644 index 0000000000000..93644a92800c0 --- /dev/null +++ b/src/test/ui/issues/issue-59326-compile-fail.stderr @@ -0,0 +1,9 @@ +error[E0271]: type mismatch resolving `::F == ()` + --> $DIR/issue-59326-compile-fail.rs:26:21 + | +LL | build_server(|| make_server()) + | ^^^^^^^^^^^^^ expected `u32`, found `()` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0271`. diff --git a/src/test/ui/issues/issue-59326.rs b/src/test/ui/issues/issue-59326-compile-run.rs similarity index 100% rename from src/test/ui/issues/issue-59326.rs rename to src/test/ui/issues/issue-59326-compile-run.rs