about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMazdak Farrokhzad <twingoow@gmail.com>2019-08-14 04:18:36 +0200
committerGitHub <noreply@github.com>2019-08-14 04:18:36 +0200
commit3f181120795de77b3d9489f37e805082daa571e7 (patch)
treecc5ec923e0aac2187c1147e3c5b29fe956f4fb2f
parentdda33cad3841a49e066f8eac56f89db8a0ce4e5c (diff)
parent861d1bb365419c4a9ae8eb14257323e9877e5d42 (diff)
downloadrust-3f181120795de77b3d9489f37e805082daa571e7.tar.gz
rust-3f181120795de77b3d9489f37e805082daa571e7.zip
Rollup merge of #62849 - davidtwco:prohibit-inheriting-lifetimes, r=nikomatsakis
typeck: Prohibit RPIT types that inherit lifetimes

Part of #61949.

This PR prohibits return position `impl Trait` types that "inherit
lifetimes" from the parent scope. The intent is to forbid cases that are
challenging until they can be addressed properly.

cc @nikomatsakis
-rw-r--r--src/librustc_typeck/check/mod.rs84
-rw-r--r--src/test/ui/async-await/issue-61949-self-return-type.rs28
-rw-r--r--src/test/ui/async-await/issue-61949-self-return-type.stderr8
-rw-r--r--src/test/ui/impl-trait/bound-normalization-fail.rs4
-rw-r--r--src/test/ui/impl-trait/bound-normalization-fail.stderr14
5 files changed, 132 insertions, 6 deletions
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 4fb28db6e94..14fc0d6347e 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -1325,12 +1325,94 @@ fn check_union(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) {
     check_packed(tcx, span, def_id);
 }
 
+/// Checks that an opaque type does not contain cycles and does not use `Self` or `T::Foo`
+/// projections that would result in "inheriting lifetimes".
 fn check_opaque<'tcx>(
     tcx: TyCtxt<'tcx>,
     def_id: DefId,
     substs: SubstsRef<'tcx>,
     span: Span,
-    origin: &hir::OpaqueTyOrigin
+    origin: &hir::OpaqueTyOrigin,
+) {
+    check_opaque_for_inheriting_lifetimes(tcx, def_id, span);
+    check_opaque_for_cycles(tcx, def_id, substs, span, origin);
+}
+
+/// Checks that an opaque type does not use `Self` or `T::Foo` projections that would result
+/// in "inheriting lifetimes".
+fn check_opaque_for_inheriting_lifetimes(
+    tcx: TyCtxt<'tcx>,
+    def_id: DefId,
+    span: Span,
+) {
+    let item = tcx.hir().expect_item(
+        tcx.hir().as_local_hir_id(def_id).expect("opaque type is not local"));
+    debug!("check_opaque_for_inheriting_lifetimes: def_id={:?} span={:?} item={:?}",
+           def_id, span, item);
+
+    #[derive(Debug)]
+    struct ProhibitOpaqueVisitor<'tcx> {
+        opaque_identity_ty: Ty<'tcx>,
+        generics: &'tcx ty::Generics,
+    };
+
+    impl<'tcx> ty::fold::TypeVisitor<'tcx> for ProhibitOpaqueVisitor<'tcx> {
+        fn visit_ty(&mut self, t: Ty<'tcx>) -> bool {
+            debug!("check_opaque_for_inheriting_lifetimes: (visit_ty) t={:?}", t);
+            if t == self.opaque_identity_ty { false } else { t.super_visit_with(self) }
+        }
+
+        fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool {
+            debug!("check_opaque_for_inheriting_lifetimes: (visit_region) r={:?}", r);
+            if let RegionKind::ReEarlyBound(ty::EarlyBoundRegion { index, .. }) = r {
+                return *index < self.generics.parent_count as u32;
+            }
+
+            r.super_visit_with(self)
+        }
+    }
+
+    let prohibit_opaque = match item.node {
+        ItemKind::OpaqueTy(hir::OpaqueTy { origin: hir::OpaqueTyOrigin::AsyncFn, .. }) |
+        ItemKind::OpaqueTy(hir::OpaqueTy { origin: hir::OpaqueTyOrigin::FnReturn, .. }) => {
+            let mut visitor = ProhibitOpaqueVisitor {
+                opaque_identity_ty: tcx.mk_opaque(
+                    def_id, InternalSubsts::identity_for_item(tcx, def_id)),
+                generics: tcx.generics_of(def_id),
+            };
+            debug!("check_opaque_for_inheriting_lifetimes: visitor={:?}", visitor);
+
+            tcx.predicates_of(def_id).predicates.iter().any(
+                |(predicate, _)| predicate.visit_with(&mut visitor))
+        },
+        _ => false,
+    };
+
+    debug!("check_opaque_for_inheriting_lifetimes: prohibit_opaque={:?}", prohibit_opaque);
+    if prohibit_opaque {
+        let is_async = match item.node {
+            ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => match origin {
+                hir::OpaqueTyOrigin::AsyncFn => true,
+                _ => false,
+            },
+            _ => unreachable!(),
+        };
+
+        tcx.sess.span_err(span, &format!(
+            "`{}` return type cannot contain a projection or `Self` that references lifetimes from \
+             a parent scope",
+            if is_async { "async fn" } else { "impl Trait" },
+        ));
+    }
+}
+
+/// Checks that an opaque type does not contain cycles.
+fn check_opaque_for_cycles<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    def_id: DefId,
+    substs: SubstsRef<'tcx>,
+    span: Span,
+    origin: &hir::OpaqueTyOrigin,
 ) {
     if let Err(partially_expanded_type) = tcx.try_expand_impl_trait_type(def_id, substs) {
         if let hir::OpaqueTyOrigin::AsyncFn = origin {
diff --git a/src/test/ui/async-await/issue-61949-self-return-type.rs b/src/test/ui/async-await/issue-61949-self-return-type.rs
new file mode 100644
index 00000000000..c5a66d5d4a3
--- /dev/null
+++ b/src/test/ui/async-await/issue-61949-self-return-type.rs
@@ -0,0 +1,28 @@
+// ignore-tidy-linelength
+// edition:2018
+#![feature(async_await)]
+
+// This test checks that `Self` is prohibited as a return type. See #61949 for context.
+
+pub struct Foo<'a> {
+    pub bar: &'a i32,
+}
+
+impl<'a> Foo<'a> {
+    pub async fn new(_bar: &'a i32) -> Self {
+    //~^ ERROR `async fn` return type cannot contain a projection or `Self` that references lifetimes from a parent scope
+        Foo {
+            bar: &22
+        }
+    }
+}
+
+async fn foo() {
+    let x = {
+        let bar = 22;
+        Foo::new(&bar).await
+    };
+    drop(x);
+}
+
+fn main() { }
diff --git a/src/test/ui/async-await/issue-61949-self-return-type.stderr b/src/test/ui/async-await/issue-61949-self-return-type.stderr
new file mode 100644
index 00000000000..a9ae544502d
--- /dev/null
+++ b/src/test/ui/async-await/issue-61949-self-return-type.stderr
@@ -0,0 +1,8 @@
+error: `async fn` return type cannot contain a projection or `Self` that references lifetimes from a parent scope
+  --> $DIR/issue-61949-self-return-type.rs:12:40
+   |
+LL |     pub async fn new(_bar: &'a i32) -> Self {
+   |                                        ^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/impl-trait/bound-normalization-fail.rs b/src/test/ui/impl-trait/bound-normalization-fail.rs
index c33261bfd09..9ba7c91fc72 100644
--- a/src/test/ui/impl-trait/bound-normalization-fail.rs
+++ b/src/test/ui/impl-trait/bound-normalization-fail.rs
@@ -1,4 +1,5 @@
 // compile-fail
+// ignore-tidy-linelength
 // edition:2018
 
 #![feature(async_await)]
@@ -44,7 +45,8 @@ mod lifetimes {
 
     /// Missing bound constraining `Assoc`, `T::Assoc` can't be normalized further.
     fn foo2_fail<'a, T: Trait<'a>>() -> impl FooLike<Output=T::Assoc> {
-        //~^ ERROR: type mismatch
+    //~^ ERROR: type mismatch
+    //~^^ ERROR `impl Trait` return type cannot contain a projection or `Self` that references lifetimes from a parent scope
         Foo(())
     }
 }
diff --git a/src/test/ui/impl-trait/bound-normalization-fail.stderr b/src/test/ui/impl-trait/bound-normalization-fail.stderr
index aa306a7e08a..b5c8e078f0f 100644
--- a/src/test/ui/impl-trait/bound-normalization-fail.stderr
+++ b/src/test/ui/impl-trait/bound-normalization-fail.stderr
@@ -1,5 +1,5 @@
 warning: the feature `impl_trait_in_bindings` is incomplete and may cause the compiler to crash
-  --> $DIR/bound-normalization-fail.rs:5:12
+  --> $DIR/bound-normalization-fail.rs:6:12
    |
 LL | #![feature(impl_trait_in_bindings)]
    |            ^^^^^^^^^^^^^^^^^^^^^^
@@ -7,7 +7,7 @@ LL | #![feature(impl_trait_in_bindings)]
    = note: `#[warn(incomplete_features)]` on by default
 
 error[E0271]: type mismatch resolving `<Foo<()> as FooLike>::Output == <T as impl_trait::Trait>::Assoc`
-  --> $DIR/bound-normalization-fail.rs:29:32
+  --> $DIR/bound-normalization-fail.rs:30:32
    |
 LL |     fn foo_fail<T: Trait>() -> impl FooLike<Output=T::Assoc> {
    |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), found associated type
@@ -16,8 +16,14 @@ LL |     fn foo_fail<T: Trait>() -> impl FooLike<Output=T::Assoc> {
               found type `<T as impl_trait::Trait>::Assoc`
    = note: the return type of a function must have a statically known size
 
+error: `impl Trait` return type cannot contain a projection or `Self` that references lifetimes from a parent scope
+  --> $DIR/bound-normalization-fail.rs:47:41
+   |
+LL |     fn foo2_fail<'a, T: Trait<'a>>() -> impl FooLike<Output=T::Assoc> {
+   |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
 error[E0271]: type mismatch resolving `<Foo<()> as FooLike>::Output == <T as lifetimes::Trait<'static>>::Assoc`
-  --> $DIR/bound-normalization-fail.rs:46:41
+  --> $DIR/bound-normalization-fail.rs:47:41
    |
 LL |     fn foo2_fail<'a, T: Trait<'a>>() -> impl FooLike<Output=T::Assoc> {
    |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), found associated type
@@ -26,6 +32,6 @@ LL |     fn foo2_fail<'a, T: Trait<'a>>() -> impl FooLike<Output=T::Assoc> {
               found type `<T as lifetimes::Trait<'static>>::Assoc`
    = note: the return type of a function must have a statically known size
 
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0271`.