From: Oliver Scherer Date: Tue, 29 Jan 2019 15:33:53 +0000 (+0100) Subject: Restrict concrete types to equivalent types X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;h=6f83dcc192e989eba6f4e531f4931ae057e62f9e;p=rust.git Restrict concrete types to equivalent types --- diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index bd2349161f7..040494a2799 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -233,6 +233,11 @@ fn hash_stable(&self, abi }); +impl_stable_hash_for!(struct ty::ResolvedOpaqueTy<'tcx> { + concrete_type, + substs +}); + impl<'a, 'gcx, T> HashStable> for ty::Binder where T: HashStable> { diff --git a/src/librustc/infer/opaque_types/mod.rs b/src/librustc/infer/opaque_types/mod.rs index 5e94bb1f877..eabbb943c6e 100644 --- a/src/librustc/infer/opaque_types/mod.rs +++ b/src/librustc/infer/opaque_types/mod.rs @@ -26,7 +26,7 @@ pub struct OpaqueTypeDecl<'tcx> { /// /// winds up desugared to: /// - /// abstract type Foo<'x, T>: Trait<'x> + /// abstract type Foo<'x, X>: Trait<'x> /// fn foo<'a, 'b, T>() -> Foo<'a, T> /// /// then `substs` would be `['a, T]`. diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 881c0d4e6d2..350dcdf571b 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -315,6 +315,17 @@ pub fn remove(&mut self, id: hir::HirId) -> Option { } } +/// All information necessary to validate and reveal an `impl Trait` or `existential Type` +#[derive(RustcEncodable, RustcDecodable, Debug)] +pub struct ResolvedOpaqueTy<'tcx> { + /// The revealed type as seen by this function. + pub concrete_type: Ty<'tcx>, + /// Generic parameters on the opaque type as passed by this function. + /// For `existential type Foo; fn foo() -> Foo { .. }` this is `[T, U]`, not + /// `[A, B]` + pub substs: &'tcx Substs<'tcx>, +} + #[derive(RustcEncodable, RustcDecodable, Debug)] pub struct TypeckTables<'tcx> { /// The HirId::owner all ItemLocalIds in this table are relative to. @@ -417,7 +428,7 @@ pub struct TypeckTables<'tcx> { /// All the existential types that are restricted to concrete types /// by this function - pub concrete_existential_types: FxHashMap>, + pub concrete_existential_types: FxHashMap>, /// Given the closure ID this map provides the list of UpvarIDs used by it. /// The upvarID contains the HIR node ID and it also contains the full path diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index c9089428b23..3a7441e9215 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -74,7 +74,7 @@ pub use self::context::{Lift, TypeckTables, CtxtInterners}; pub use self::context::{ UserTypeAnnotationIndex, UserType, CanonicalUserType, - CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, + CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, ResolvedOpaqueTy, }; pub use self::instance::{Instance, InstanceDef}; diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index 00917be538f..c9dd83dcd5e 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -567,17 +567,23 @@ fn visit_opaque_types(&mut self, span: Span) { } } + let new = ty::ResolvedOpaqueTy { + concrete_type: definition_ty, + substs: self.tcx().lift_to_global(&opaque_defn.substs).unwrap(), + }; + let old = self.tables .concrete_existential_types - .insert(def_id, definition_ty); + .insert(def_id, new); if let Some(old) = old { - if old != definition_ty { + if old.concrete_type != definition_ty || old.substs != opaque_defn.substs { span_bug!( span, "visit_opaque_types tried to write \ - different types for the same existential type: {:?}, {:?}, {:?}", + different types for the same existential type: {:?}, {:?}, {:?}, {:?}", def_id, definition_ty, + opaque_defn, old, ); } diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index e4fc1925eb3..7cdcfec339e 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -23,9 +23,10 @@ use middle::weak_lang_items; use rustc::mir::mono::Linkage; use rustc::ty::query::Providers; -use rustc::ty::subst::Substs; +use rustc::ty::subst::{Subst, Substs}; use rustc::ty::util::Discr; use rustc::ty::util::IntTypeExt; +use rustc::ty::subst::UnpackedKind; use rustc::ty::{self, AdtKind, ToPolyTraitRef, Ty, TyCtxt}; use rustc::ty::{ReprOptions, ToPredicate}; use rustc::util::captures::Captures; @@ -1193,7 +1194,7 @@ fn type_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Ty<'tcx> { tcx.typeck_tables_of(owner) .concrete_existential_types .get(&def_id) - .cloned() + .map(|opaque| opaque.concrete_type) .unwrap_or_else(|| { // This can occur if some error in the // owner fn prevented us from populating @@ -1325,7 +1326,13 @@ fn find_existential_constraints<'a, 'tcx>( struct ConstraintLocator<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, - found: Option<(Span, ty::Ty<'tcx>)>, + // First found type span, actual type, mapping from the existential type's generic + // parameters to the concrete type's generic parameters + // + // The mapping is an index for each use site of a generic parameter in the concrete type + // + // The indices index into the generic parameters on the existential type. + found: Option<(Span, ty::Ty<'tcx>, Vec)>, } impl<'a, 'tcx> ConstraintLocator<'a, 'tcx> { @@ -1340,13 +1347,50 @@ fn check(&mut self, def_id: DefId) { .tcx .typeck_tables_of(def_id) .concrete_existential_types - .get(&self.def_id) - .cloned(); - if let Some(ty) = ty { + .get(&self.def_id); + if let Some(ty::ResolvedOpaqueTy { concrete_type, substs }) = ty { // FIXME(oli-obk): trace the actual span from inference to improve errors let span = self.tcx.def_span(def_id); - if let Some((prev_span, prev_ty)) = self.found { - let mut ty = ty.walk().fuse(); + // used to quickly look up the position of a generic parameter + let mut index_map: FxHashMap = FxHashMap::default(); + // skip binder is ok, since we only use this to find generic parameters and their + // positions. + for subst in substs.iter() { + if let UnpackedKind::Type(ty) = subst.unpack() { + if let ty::Param(p) = ty.sty { + let idx = index_map.len(); + if index_map.insert(p, idx).is_some() { + // there was already an entry for `p`, meaning a generic parameter + // was used twice + self.tcx.sess.span_err( + span, + &format!("defining existential type use restricts existential \ + type by using the generic parameter `{}` twice", p.name), + ); + return; + } + } else { + self.tcx.sess.delay_span_bug( + span, + &format!( + "non-defining exist ty use in defining scope: {:?}, {:?}", + concrete_type, substs, + ), + ); + } + } + } + // compute the index within the existential type for each generic parameter used in + // the concrete type + let indices = concrete_type + .subst(self.tcx, substs) + .walk() + .filter_map(|t| match &t.sty { + ty::Param(p) => Some(*index_map.get(p).unwrap()), + _ => None, + }).collect(); + if let Some((prev_span, prev_ty, ref prev_indices)) = self.found { + let mut ty = concrete_type.walk().fuse(); let mut prev_ty = prev_ty.walk().fuse(); let iter_eq = (&mut ty).zip(&mut prev_ty).all(|(t, p)| match (&t.sty, &p.sty) { // type parameters are equal to any other type parameter for the purpose of @@ -1359,13 +1403,21 @@ fn check(&mut self, def_id: DefId) { // found different concrete types for the existential type let mut err = self.tcx.sess.struct_span_err( span, - "defining existential type use differs from previous", + "concrete type differs from previous defining existential type use", + ); + err.span_note(prev_span, "previous use here"); + err.emit(); + } else if indices != *prev_indices { + // found "same" concrete types, but the generic parameter order differs + let mut err = self.tcx.sess.struct_span_err( + span, + "concrete type's generic parameters differ from previous defining use", ); err.span_note(prev_span, "previous use here"); err.emit(); } } else { - self.found = Some((span, ty)); + self.found = Some((span, concrete_type, indices)); } } } @@ -1424,7 +1476,7 @@ fn visit_trait_item(&mut self, it: &'tcx TraitItem) { } match locator.found { - Some((_, ty)) => ty, + Some((_, ty, _)) => ty, None => { let span = tcx.def_span(def_id); tcx.sess.span_err(span, "could not find defining uses"); diff --git a/src/test/ui/existential_types/different_defining_uses.rs b/src/test/ui/existential_types/different_defining_uses.rs index c51fca75a24..a8670cc07f2 100644 --- a/src/test/ui/existential_types/different_defining_uses.rs +++ b/src/test/ui/existential_types/different_defining_uses.rs @@ -9,6 +9,6 @@ fn foo() -> Foo { "" } -fn bar() -> Foo { //~ ERROR defining existential type use differs from previous +fn bar() -> Foo { //~ ERROR concrete type differs from previous 42i32 } diff --git a/src/test/ui/existential_types/different_defining_uses.stderr b/src/test/ui/existential_types/different_defining_uses.stderr index f782a002297..3b3449bbf11 100644 --- a/src/test/ui/existential_types/different_defining_uses.stderr +++ b/src/test/ui/existential_types/different_defining_uses.stderr @@ -1,7 +1,7 @@ -error: defining existential type use differs from previous +error: concrete type differs from previous defining existential type use --> $DIR/different_defining_uses.rs:12:1 | -LL | / fn bar() -> Foo { //~ ERROR defining existential type use differs from previous +LL | / fn bar() -> Foo { //~ ERROR concrete type differs from previous LL | | 42i32 LL | | } | |_^ diff --git a/src/test/ui/existential_types/different_defining_uses_never_type.rs b/src/test/ui/existential_types/different_defining_uses_never_type.rs index c6c6ae8d2dc..13ada63e4bc 100644 --- a/src/test/ui/existential_types/different_defining_uses_never_type.rs +++ b/src/test/ui/existential_types/different_defining_uses_never_type.rs @@ -9,10 +9,10 @@ fn foo() -> Foo { "" } -fn bar() -> Foo { //~ ERROR defining existential type use differs from previous +fn bar() -> Foo { //~ ERROR concrete type differs from previous panic!() } -fn boo() -> Foo { //~ ERROR defining existential type use differs from previous +fn boo() -> Foo { //~ ERROR concrete type differs from previous loop {} } diff --git a/src/test/ui/existential_types/different_defining_uses_never_type.stderr b/src/test/ui/existential_types/different_defining_uses_never_type.stderr index 04b0cf27784..161111e3379 100644 --- a/src/test/ui/existential_types/different_defining_uses_never_type.stderr +++ b/src/test/ui/existential_types/different_defining_uses_never_type.stderr @@ -1,7 +1,7 @@ -error: defining existential type use differs from previous +error: concrete type differs from previous defining existential type use --> $DIR/different_defining_uses_never_type.rs:12:1 | -LL | / fn bar() -> Foo { //~ ERROR defining existential type use differs from previous +LL | / fn bar() -> Foo { //~ ERROR concrete type differs from previous LL | | panic!() LL | | } | |_^ @@ -14,10 +14,10 @@ LL | | "" LL | | } | |_^ -error: defining existential type use differs from previous +error: concrete type differs from previous defining existential type use --> $DIR/different_defining_uses_never_type.rs:16:1 | -LL | / fn boo() -> Foo { //~ ERROR defining existential type use differs from previous +LL | / fn boo() -> Foo { //~ ERROR concrete type differs from previous LL | | loop {} LL | | } | |_^ diff --git a/src/test/ui/existential_types/generic_different_defining_uses.rs b/src/test/ui/existential_types/generic_different_defining_uses.rs index 3bd104251fb..ce3ab88a1c0 100644 --- a/src/test/ui/existential_types/generic_different_defining_uses.rs +++ b/src/test/ui/existential_types/generic_different_defining_uses.rs @@ -8,6 +8,6 @@ fn my_iter(t: T) -> MyIter { std::iter::once(t) } -fn my_iter2(t: T) -> MyIter { //~ ERROR defining existential type use differs from previous +fn my_iter2(t: T) -> MyIter { //~ ERROR concrete type differs from previous Some(t).into_iter() } diff --git a/src/test/ui/existential_types/generic_different_defining_uses.stderr b/src/test/ui/existential_types/generic_different_defining_uses.stderr index 234bcf232ae..89f70a873d9 100644 --- a/src/test/ui/existential_types/generic_different_defining_uses.stderr +++ b/src/test/ui/existential_types/generic_different_defining_uses.stderr @@ -1,7 +1,7 @@ -error: defining existential type use differs from previous +error: concrete type differs from previous defining existential type use --> $DIR/generic_different_defining_uses.rs:11:1 | -LL | / fn my_iter2(t: T) -> MyIter { //~ ERROR defining existential type use differs from previous +LL | / fn my_iter2(t: T) -> MyIter { //~ ERROR concrete type differs from previous LL | | Some(t).into_iter() LL | | } | |_^ diff --git a/src/test/ui/existential_types/generic_duplicate_param_use.rs b/src/test/ui/existential_types/generic_duplicate_param_use.rs index d08cd88c600..3f8753333aa 100644 --- a/src/test/ui/existential_types/generic_duplicate_param_use.rs +++ b/src/test/ui/existential_types/generic_duplicate_param_use.rs @@ -1,4 +1,3 @@ -// compile-pass #![feature(existential_type)] use std::fmt::Debug; @@ -7,7 +6,9 @@ fn main() {} // test that unused generic parameters are ok existential type Two: Debug; +//~^ could not find defining uses fn one(t: T) -> Two { +//~^ ERROR defining existential type use restricts existential type t } diff --git a/src/test/ui/existential_types/generic_duplicate_param_use.stderr b/src/test/ui/existential_types/generic_duplicate_param_use.stderr new file mode 100644 index 00000000000..d4deda999da --- /dev/null +++ b/src/test/ui/existential_types/generic_duplicate_param_use.stderr @@ -0,0 +1,17 @@ +error: defining existential type use restricts existential type by using the generic parameter `T` twice + --> $DIR/generic_duplicate_param_use.rs:11:1 + | +LL | / fn one(t: T) -> Two { +LL | | //~^ ERROR defining existential type use restricts existential type +LL | | t +LL | | } + | |_^ + +error: could not find defining uses + --> $DIR/generic_duplicate_param_use.rs:8:1 + | +LL | existential type Two: Debug; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/existential_types/generic_duplicate_param_use2.rs b/src/test/ui/existential_types/generic_duplicate_param_use2.rs index c27fbb74cf1..3842292decd 100644 --- a/src/test/ui/existential_types/generic_duplicate_param_use2.rs +++ b/src/test/ui/existential_types/generic_duplicate_param_use2.rs @@ -1,4 +1,3 @@ -// compile-pass #![feature(existential_type)] use std::fmt::Debug; @@ -9,6 +8,7 @@ fn main() {} existential type Two: Debug; fn one(t: T) -> Two { +//~^ defining existential type use restricts existential type t } diff --git a/src/test/ui/existential_types/generic_duplicate_param_use2.stderr b/src/test/ui/existential_types/generic_duplicate_param_use2.stderr new file mode 100644 index 00000000000..0a8be3218c7 --- /dev/null +++ b/src/test/ui/existential_types/generic_duplicate_param_use2.stderr @@ -0,0 +1,11 @@ +error: defining existential type use restricts existential type by using the generic parameter `T` twice + --> $DIR/generic_duplicate_param_use2.rs:10:1 + | +LL | / fn one(t: T) -> Two { +LL | | //~^ defining existential type use restricts existential type +LL | | t +LL | | } + | |_^ + +error: aborting due to previous error + diff --git a/src/test/ui/existential_types/generic_duplicate_param_use3.rs b/src/test/ui/existential_types/generic_duplicate_param_use3.rs index b4d1b26dbab..05c77c89473 100644 --- a/src/test/ui/existential_types/generic_duplicate_param_use3.rs +++ b/src/test/ui/existential_types/generic_duplicate_param_use3.rs @@ -1,4 +1,3 @@ -// compile-pass #![feature(existential_type)] use std::fmt::Debug; @@ -9,6 +8,7 @@ fn main() {} existential type Two: Debug; fn one(t: T) -> Two { +//~^ defining existential type use restricts existential type t } @@ -17,5 +17,6 @@ fn two(t: T, _: U) -> Two { } fn three(_: T, u: U) -> Two { +//~^ concrete type's generic parameters differ from previous defining use u } diff --git a/src/test/ui/existential_types/generic_duplicate_param_use3.stderr b/src/test/ui/existential_types/generic_duplicate_param_use3.stderr new file mode 100644 index 00000000000..8f860e7ee0a --- /dev/null +++ b/src/test/ui/existential_types/generic_duplicate_param_use3.stderr @@ -0,0 +1,28 @@ +error: defining existential type use restricts existential type by using the generic parameter `T` twice + --> $DIR/generic_duplicate_param_use3.rs:10:1 + | +LL | / fn one(t: T) -> Two { +LL | | //~^ defining existential type use restricts existential type +LL | | t +LL | | } + | |_^ + +error: concrete type's generic parameters differ from previous defining use + --> $DIR/generic_duplicate_param_use3.rs:19:1 + | +LL | / fn three(_: T, u: U) -> Two { +LL | | //~^ concrete type's generic parameters differ from previous defining use +LL | | u +LL | | } + | |_^ + | +note: previous use here + --> $DIR/generic_duplicate_param_use3.rs:15:1 + | +LL | / fn two(t: T, _: U) -> Two { +LL | | t +LL | | } + | |_^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/existential_types/generic_duplicate_param_use4.rs b/src/test/ui/existential_types/generic_duplicate_param_use4.rs index afab86c3ff0..609dbe06cd7 100644 --- a/src/test/ui/existential_types/generic_duplicate_param_use4.rs +++ b/src/test/ui/existential_types/generic_duplicate_param_use4.rs @@ -1,4 +1,3 @@ -// compile-pass #![feature(existential_type)] use std::fmt::Debug; @@ -9,6 +8,7 @@ fn main() {} existential type Two: Debug; fn one(t: T) -> Two { +//~^ ERROR defining existential type use restricts existential type t } diff --git a/src/test/ui/existential_types/generic_duplicate_param_use4.stderr b/src/test/ui/existential_types/generic_duplicate_param_use4.stderr new file mode 100644 index 00000000000..24b1caf7c1b --- /dev/null +++ b/src/test/ui/existential_types/generic_duplicate_param_use4.stderr @@ -0,0 +1,11 @@ +error: defining existential type use restricts existential type by using the generic parameter `T` twice + --> $DIR/generic_duplicate_param_use4.rs:10:1 + | +LL | / fn one(t: T) -> Two { +LL | | //~^ ERROR defining existential type use restricts existential type +LL | | t +LL | | } + | |_^ + +error: aborting due to previous error + diff --git a/src/test/ui/existential_types/generic_duplicate_param_use5.rs b/src/test/ui/existential_types/generic_duplicate_param_use5.rs new file mode 100644 index 00000000000..3f4a23b8b41 --- /dev/null +++ b/src/test/ui/existential_types/generic_duplicate_param_use5.rs @@ -0,0 +1,17 @@ +#![feature(existential_type)] + +use std::fmt::Debug; + +fn main() {} + +// test that unused generic parameters are ok +existential type Two: Debug; + +fn two(t: T, u: U) -> Two { + (t, u) +} + +fn three(t: T, u: U) -> Two { +//~^ concrete type differs from previous + (u, t) +} diff --git a/src/test/ui/existential_types/generic_duplicate_param_use5.stderr b/src/test/ui/existential_types/generic_duplicate_param_use5.stderr new file mode 100644 index 00000000000..52befb9c2e1 --- /dev/null +++ b/src/test/ui/existential_types/generic_duplicate_param_use5.stderr @@ -0,0 +1,19 @@ +error: concrete type differs from previous defining existential type use + --> $DIR/generic_duplicate_param_use5.rs:14:1 + | +LL | / fn three(t: T, u: U) -> Two { +LL | | //~^ concrete type differs from previous +LL | | (u, t) +LL | | } + | |_^ + | +note: previous use here + --> $DIR/generic_duplicate_param_use5.rs:10:1 + | +LL | / fn two(t: T, u: U) -> Two { +LL | | (t, u) +LL | | } + | |_^ + +error: aborting due to previous error + diff --git a/src/test/ui/existential_types/generic_duplicate_param_use6.rs b/src/test/ui/existential_types/generic_duplicate_param_use6.rs new file mode 100644 index 00000000000..3b8c56352bd --- /dev/null +++ b/src/test/ui/existential_types/generic_duplicate_param_use6.rs @@ -0,0 +1,17 @@ +#![feature(existential_type)] + +use std::fmt::Debug; + +fn main() {} + +// test that unused generic parameters are ok +existential type Two: Debug; + +fn two(t: T, u: U) -> Two { + (t, t) +} + +fn three(t: T, u: U) -> Two { +//~^ concrete type differs from previous + (u, t) +} diff --git a/src/test/ui/existential_types/generic_duplicate_param_use6.stderr b/src/test/ui/existential_types/generic_duplicate_param_use6.stderr new file mode 100644 index 00000000000..2bf1d0c05e6 --- /dev/null +++ b/src/test/ui/existential_types/generic_duplicate_param_use6.stderr @@ -0,0 +1,19 @@ +error: concrete type differs from previous defining existential type use + --> $DIR/generic_duplicate_param_use6.rs:14:1 + | +LL | / fn three(t: T, u: U) -> Two { +LL | | //~^ concrete type differs from previous +LL | | (u, t) +LL | | } + | |_^ + | +note: previous use here + --> $DIR/generic_duplicate_param_use6.rs:10:1 + | +LL | / fn two(t: T, u: U) -> Two { +LL | | (t, t) +LL | | } + | |_^ + +error: aborting due to previous error + diff --git a/src/test/ui/existential_types/generic_duplicate_param_use7.rs b/src/test/ui/existential_types/generic_duplicate_param_use7.rs new file mode 100644 index 00000000000..3906b85aadb --- /dev/null +++ b/src/test/ui/existential_types/generic_duplicate_param_use7.rs @@ -0,0 +1,16 @@ +// compile-pass +#![feature(existential_type)] + +use std::fmt::Debug; + +fn main() {} + +existential type Two: Debug; + +fn two(t: T, u: U) -> Two { + (t, t) +} + +fn three(t: T, t2: T, u: U) -> Two { + (t, t2) +}