]> git.lizzy.rs Git - rust.git/commitdiff
Check lifetimes on existential types
authorOliver Schneider <github35764891676564198441@oli-obk.de>
Tue, 17 Jul 2018 09:21:54 +0000 (11:21 +0200)
committerOliver Schneider <github35764891676564198441@oli-obk.de>
Wed, 18 Jul 2018 08:53:10 +0000 (10:53 +0200)
src/librustc/infer/anon_types/mod.rs
src/librustc/ty/fold.rs
src/librustc_typeck/check/wfcheck.rs
src/librustc_typeck/check/writeback.rs
src/test/ui/existential_types/generic_duplicate_lifetime_param.rs [new file with mode: 0644]
src/test/ui/existential_types/generic_duplicate_lifetime_param.stderr [new file with mode: 0644]
src/test/ui/existential_types/generic_duplicate_param_use.rs [new file with mode: 0644]
src/test/ui/existential_types/generic_duplicate_param_use.stderr [new file with mode: 0644]
src/test/ui/existential_types/generic_lifetime_param.rs [new file with mode: 0644]
src/test/ui/existential_types/no_revealing_outside_defining_module.rs
src/test/ui/existential_types/no_revealing_outside_defining_module.stderr

index 2cb47c7beb53a852973835bfe497d562453ed099..ae4d88704a098e830c10bc3faafd19b5c1095f5d 100644 (file)
@@ -653,6 +653,7 @@ fn instantiate_anon_types_in_map<T: TypeFoldable<'tcx>>(&mut self, value: &T) ->
         let tcx = self.infcx.tcx;
         value.fold_with(&mut BottomUpFolder {
             tcx,
+            reg_op: |reg| reg,
             fldop: |ty| {
                 if let ty::TyAnon(def_id, substs) = ty.sty {
                     // Check that this is `impl Trait` type is
index 1380d10e493a8deaca0c5d2cb667406724181828..e4484041b065ce829306f816f8a7e04e1212482c 100644 (file)
@@ -200,15 +200,18 @@ fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool {
 ///////////////////////////////////////////////////////////////////////////
 // Some sample folders
 
-pub struct BottomUpFolder<'a, 'gcx: 'a+'tcx, 'tcx: 'a, F>
-    where F: FnMut(Ty<'tcx>) -> Ty<'tcx>
+pub struct BottomUpFolder<'a, 'gcx: 'a+'tcx, 'tcx: 'a, F, G>
+    where F: FnMut(Ty<'tcx>) -> Ty<'tcx>,
+          G: FnMut(ty::Region<'tcx>) -> ty::Region<'tcx>,
 {
     pub tcx: TyCtxt<'a, 'gcx, 'tcx>,
     pub fldop: F,
+    pub reg_op: G,
 }
 
-impl<'a, 'gcx, 'tcx, F> TypeFolder<'gcx, 'tcx> for BottomUpFolder<'a, 'gcx, 'tcx, F>
+impl<'a, 'gcx, 'tcx, F, G> TypeFolder<'gcx, 'tcx> for BottomUpFolder<'a, 'gcx, 'tcx, F, G>
     where F: FnMut(Ty<'tcx>) -> Ty<'tcx>,
+          G: FnMut(ty::Region<'tcx>) -> ty::Region<'tcx>,
 {
     fn tcx<'b>(&'b self) -> TyCtxt<'b, 'gcx, 'tcx> { self.tcx }
 
@@ -216,6 +219,11 @@ fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
         let t1 = ty.super_fold_with(self);
         (self.fldop)(t1)
     }
+
+    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
+        let r = r.super_fold_with(self);
+        (self.reg_op)(r)
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////
index cd6a1e3fdba22f7df7616a1020db974698cac4b3..03ecb945cbd3c9497f0f71182ba58ada0d49d37d 100644 (file)
@@ -575,9 +575,10 @@ fn check_existential_types<'a, 'fcx, 'gcx, 'tcx>(
                     let anon_node_id = tcx.hir.as_local_node_id(def_id).unwrap();
                     if may_define_existential_type(tcx, fn_def_id, anon_node_id) {
                         trace!("check_existential_types may define. Generics: {:#?}", generics);
+                        let mut seen: FxHashMap<_, Vec<_>> = FxHashMap();
                         for (subst, param) in substs.iter().zip(&generics.params) {
-                            if let ty::subst::UnpackedKind::Type(ty) = subst.unpack() {
-                                match ty.sty {
+                            match subst.unpack() {
+                                ty::subst::UnpackedKind::Type(ty) => match ty.sty {
                                     ty::TyParam(..) => {},
                                     // prevent `fn foo() -> Foo<u32>` from being defining
                                     _ => {
@@ -597,11 +598,47 @@ fn check_existential_types<'a, 'fcx, 'gcx, 'tcx>(
                                                 ),
                                             )
                                             .emit();
-                                        return tcx.types.err;
                                     },
-                                } // match ty
-                            } // if let Type = subst
+                                }, // match ty
+                                ty::subst::UnpackedKind::Lifetime(region) => {
+                                    let param_span = tcx.def_span(param.def_id);
+                                    if let ty::ReStatic = region {
+                                        tcx
+                                            .sess
+                                            .struct_span_err(
+                                                span,
+                                                "non-defining existential type use \
+                                                    in defining scope",
+                                            )
+                                            .span_label(
+                                                param_span,
+                                                "cannot use static lifetime, use a bound lifetime \
+                                                instead or remove the lifetime parameter from the \
+                                                existential type",
+                                            )
+                                            .emit();
+                                    } else {
+                                        seen.entry(region).or_default().push(param_span);
+                                    }
+                                },
+                            } // match subst
                         } // for (subst, param)
+                        for (_, spans) in seen {
+                            if spans.len() > 1 {
+                                tcx
+                                    .sess
+                                    .struct_span_err(
+                                        span,
+                                        "non-defining existential type use \
+                                            in defining scope",
+                                    ).
+                                    span_note(
+                                        spans,
+                                        "lifetime used multiple times",
+                                    )
+                                    .emit();
+                            }
+                        }
                     } // if may_define_existential_type
 
                     // now register the bounds on the parameters of the existential type
@@ -631,6 +668,7 @@ fn check_existential_types<'a, 'fcx, 'gcx, 'tcx>(
             } // if let TyAnon
             ty
         },
+        reg_op: |reg| reg,
     });
     substituted_predicates
 }
index d82ad0d180bf5d7c2f55498dd0091bef524efbf4..b37f489b2c721419002f3ad2d96fae2e51153ecd 100644 (file)
@@ -389,16 +389,17 @@ fn visit_anon_types(&mut self, span: Span) {
         for (&def_id, anon_defn) in self.fcx.anon_types.borrow().iter() {
             let node_id = self.tcx().hir.as_local_node_id(def_id).unwrap();
             let instantiated_ty = self.resolve(&anon_defn.concrete_ty, &node_id);
-            let mut definition_ty = self.fcx.infer_anon_definition_from_instantiation(
-                def_id,
-                anon_defn,
-                instantiated_ty,
-            );
 
             let generics = self.tcx().generics_of(def_id);
 
-            // named existential type, not an impl trait
-            if generics.parent.is_none() {
+            let definition_ty = if generics.parent.is_some() {
+                // impl trait
+                self.fcx.infer_anon_definition_from_instantiation(
+                    def_id,
+                    anon_defn,
+                    instantiated_ty,
+                )
+            } else {
                 // prevent
                 // * `fn foo<T>() -> Foo<T>`
                 // * `fn foo<T: Bound + Other>() -> Foo<T>`
@@ -411,9 +412,10 @@ fn visit_anon_types(&mut self, span: Span) {
                 // fn foo<U>() -> Foo<U> { .. }
                 // ```
                 // figures out the concrete type with `U`, but the stored type is with `T`
-                definition_ty = definition_ty.fold_with(&mut BottomUpFolder {
+                instantiated_ty.fold_with(&mut BottomUpFolder {
                     tcx: self.tcx().global_tcx(),
                     fldop: |ty| {
+                        trace!("checking type {:?}: {:#?}", ty, ty.sty);
                         // find a type parameter
                         if let ty::TyParam(..) = ty.sty {
                             // look it up in the substitution list
@@ -445,8 +447,50 @@ fn visit_anon_types(&mut self, span: Span) {
                         }
                         ty
                     },
-                });
-            }
+                    reg_op: |region| {
+                        match region {
+                            // ignore static regions
+                            ty::ReStatic => region,
+                            _ => {
+                                trace!("checking {:?}", region);
+                                for (subst, p) in anon_defn.substs.iter().zip(&generics.params) {
+                                    if let UnpackedKind::Lifetime(subst) = subst.unpack() {
+                                        if subst == region {
+                                            // found it in the substitution list, replace with the
+                                            // parameter from the existential type
+                                            let reg = ty::EarlyBoundRegion {
+                                                def_id: p.def_id,
+                                                index: p.index,
+                                                name: p.name,
+                                            };
+                                            trace!("replace {:?} with {:?}", region, reg);
+                                            return self.tcx().global_tcx()
+                                                .mk_region(ty::ReEarlyBound(reg));
+                                        }
+                                    }
+                                }
+                                trace!("anon_defn: {:#?}", anon_defn);
+                                trace!("generics: {:#?}", generics);
+                                self.tcx().sess
+                                    .struct_span_err(
+                                        span,
+                                        "non-defining existential type use in defining scope",
+                                    )
+                                    .span_label(
+                                        span,
+                                        format!(
+                                            "lifetime `{}` is part of concrete type but not used \
+                                            in parameter list of existential type",
+                                            region,
+                                        ),
+                                    )
+                                    .emit();
+                                self.tcx().global_tcx().mk_region(ty::ReStatic)
+                            }
+                        }
+                    }
+                })
+            };
 
             let old = self.tables.concrete_existential_types.insert(def_id, definition_ty);
             if let Some(old) = old {
diff --git a/src/test/ui/existential_types/generic_duplicate_lifetime_param.rs b/src/test/ui/existential_types/generic_duplicate_lifetime_param.rs
new file mode 100644 (file)
index 0000000..92b234a
--- /dev/null
@@ -0,0 +1,20 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+
+#![feature(existential_type)]
+
+fn main() {}
+
+existential type Two<'a, 'b>: std::fmt::Debug;
+
+fn one<'a>(t: &'a ()) -> Two<'a, 'a> { //~ ERROR non-defining existential type use
+    t
+}
diff --git a/src/test/ui/existential_types/generic_duplicate_lifetime_param.stderr b/src/test/ui/existential_types/generic_duplicate_lifetime_param.stderr
new file mode 100644 (file)
index 0000000..0316832
--- /dev/null
@@ -0,0 +1,16 @@
+error: non-defining existential type use in defining scope
+  --> $DIR/generic_duplicate_lifetime_param.rs:18:1
+   |
+LL | / fn one<'a>(t: &'a ()) -> Two<'a, 'a> { //~ ERROR non-defining existential type use
+LL | |     t
+LL | | }
+   | |_^
+   |
+note: lifetime used multiple times
+  --> $DIR/generic_duplicate_lifetime_param.rs:16:22
+   |
+LL | existential type Two<'a, 'b>: std::fmt::Debug;
+   |                      ^^  ^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/existential_types/generic_duplicate_param_use.rs b/src/test/ui/existential_types/generic_duplicate_param_use.rs
new file mode 100644 (file)
index 0000000..52e36fa
--- /dev/null
@@ -0,0 +1,20 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+
+#![feature(existential_type)]
+
+fn main() {}
+
+existential type Two<T, U>: 'static; //~ ERROR type parameter `U` is unused
+
+fn one<T: 'static>(t: T) -> Two<T, T> {
+    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 (file)
index 0000000..e4a92db
--- /dev/null
@@ -0,0 +1,9 @@
+error[E0091]: type parameter `U` is unused
+  --> $DIR/generic_duplicate_param_use.rs:16:25
+   |
+LL | existential type Two<T, U>: 'static; //~ ERROR type parameter `U` is unused
+   |                         ^ unused type parameter
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0091`.
diff --git a/src/test/ui/existential_types/generic_lifetime_param.rs b/src/test/ui/existential_types/generic_lifetime_param.rs
new file mode 100644 (file)
index 0000000..3bdb69e
--- /dev/null
@@ -0,0 +1,21 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-pass
+
+#![feature(existential_type)]
+
+fn main() {}
+
+existential type Region<'a>: std::fmt::Debug;
+
+fn region<'b>(a: &'b ()) -> Region<'b> {
+    a
+}
index 086df13ba7055995ecccf85bb709fc8992740f79..41c17c357bc807225f1219710ca7f825eb6e67bc 100644 (file)
 
 fn main() {}
 
-mod boo2 {
-    mod boo {
-        pub existential type Boo: ::std::fmt::Debug;
-        fn bomp() -> Boo {
-            ""
-        }
+mod boo {
+    pub existential type Boo: ::std::fmt::Debug;
+    fn bomp() -> Boo {
+        ""
     }
+}
 
-    // don't actually know the type here
+// don't actually know the type here
 
-    fn bomp2() {
-        let _: &str = bomp(); //~ ERROR mismatched types
-    }
+fn bomp2() {
+    let _: &str = bomp(); //~ ERROR mismatched types
+}
 
-    fn bomp() -> boo::Boo {
-        "" //~ ERROR mismatched types
-    }
+fn bomp() -> boo::Boo {
+    "" //~ ERROR mismatched types
 }
+
+fn bomp_loop() -> boo::Boo {
+    loop {}
+}
\ No newline at end of file
index eebd329548936ffddb208c647fa15c4c4a1ce404..a1c98c6d4b89ea9279f5ed6df05e8786ff6dfa1f 100644 (file)
@@ -1,19 +1,19 @@
 error[E0308]: mismatched types
-  --> $DIR/no_revealing_outside_defining_module.rs:27:23
+  --> $DIR/no_revealing_outside_defining_module.rs:26:19
    |
-LL |         let _: &str = bomp(); //~ ERROR mismatched types
-   |                       ^^^^^^ expected &str, found anonymized type
+LL |     let _: &str = bomp(); //~ ERROR mismatched types
+   |                   ^^^^^^ expected &str, found anonymized type
    |
    = note: expected type `&str`
               found type `Boo`
 
 error[E0308]: mismatched types
-  --> $DIR/no_revealing_outside_defining_module.rs:31:9
+  --> $DIR/no_revealing_outside_defining_module.rs:30:5
    |
-LL |     fn bomp() -> boo::Boo {
-   |                  -------- expected `Boo` because of return type
-LL |         "" //~ ERROR mismatched types
-   |         ^^ expected anonymized type, found reference
+LL | fn bomp() -> boo::Boo {
+   |              -------- expected `Boo` because of return type
+LL |     "" //~ ERROR mismatched types
+   |     ^^ expected anonymized type, found reference
    |
    = note: expected type `Boo`
               found type `&'static str`