about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2019-10-02 10:03:40 -0400
committerNiko Matsakis <niko@alum.mit.edu>2019-10-02 10:03:40 -0400
commitf7ed53c9dad467b5b9e33398e9e33f00a6724d96 (patch)
treecf32ecf52a5b5eff49b9809e9f0a3bd833285b19 /src
parentc8e58512d4ad327cdc974b6bfbde62e2778f0cc3 (diff)
downloadrust-f7ed53c9dad467b5b9e33398e9e33f00a6724d96.tar.gz
rust-f7ed53c9dad467b5b9e33398e9e33f00a6724d96.zip
extract expected return type from `-> impl Future` obligation
Diffstat (limited to 'src')
-rw-r--r--src/librustc_typeck/check/closure.rs102
-rw-r--r--src/test/ui/async-await/return-ty-raw-ptr-coercion.rs25
-rw-r--r--src/test/ui/async-await/return-ty-unsize-coercion.rs34
3 files changed, 158 insertions, 3 deletions
diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs
index 27f1a4aac61..e53f90ea719 100644
--- a/src/librustc_typeck/check/closure.rs
+++ b/src/librustc_typeck/check/closure.rs
@@ -611,9 +611,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // function.
                 Some(hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Fn)) => {
                     debug!("supplied_sig_of_closure: closure is async fn body");
-
-                    // FIXME
-                    astconv.ty_infer(None, decl.output.span())
+                    self.deduce_future_output_from_obligations(expr_def_id)
                 }
 
                 _ => astconv.ty_infer(None, decl.output.span()),
@@ -639,6 +637,104 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         result
     }
 
+    /// Invoked when we are translating the generator that results
+    /// from desugaring an `async fn`. Returns the "sugared" return
+    /// type of the `async fn` -- that is, the return type that the
+    /// user specified. The "desugared" return type is a `impl
+    /// Future<Output = T>`, so we do this by searching through the
+    /// obligations to extract the `T`.
+    fn deduce_future_output_from_obligations(
+        &self,
+        expr_def_id: DefId,
+    ) -> Ty<'tcx> {
+        debug!("deduce_future_output_from_obligations(expr_def_id={:?})", expr_def_id);
+
+        let ret_coercion =
+            self.ret_coercion
+            .as_ref()
+            .unwrap_or_else(|| span_bug!(
+                self.tcx.def_span(expr_def_id),
+                "async fn generator outside of a fn"
+            ));
+
+        // In practice, the return type of the surrounding function is
+        // always a (not yet resolved) inference variable, because it
+        // is the hidden type for an `impl Trait` that we are going to
+        // be inferring.
+        let ret_ty = ret_coercion.borrow().expected_ty();
+        let ret_ty = self.inh.infcx.shallow_resolve(ret_ty);
+        let ret_vid = match ret_ty.sty {
+            ty::Infer(ty::TyVar(ret_vid)) => ret_vid,
+            _ => {
+                span_bug!(
+                    self.tcx.def_span(expr_def_id),
+                    "async fn generator return type not an inference variable"
+                )
+            }
+        };
+
+        // Search for a pending obligation like
+        //
+        // `<R as Future>::Output = T`
+        //
+        // where R is the return type we are expecting. This type `T`
+        // will be our output.
+        let output_ty = self.obligations_for_self_ty(ret_vid)
+            .find_map(|(_, obligation)| {
+                if let ty::Predicate::Projection(ref proj_predicate) = obligation.predicate {
+                    self.deduce_future_output_from_projection(
+                        obligation.cause.span,
+                        proj_predicate
+                    )
+                } else {
+                    None
+                }
+            })
+            .unwrap();
+
+        debug!("deduce_future_output_from_obligations: output_ty={:?}", output_ty);
+        output_ty
+    }
+
+    /// Given a projection like
+    ///
+    /// `<_ as Future>::Output = T`
+    ///
+    /// returns `Some(T)`. If the projection is for some other trait,
+    /// returns `None`.
+    fn deduce_future_output_from_projection(
+        &self,
+        cause_span: Span,
+        projection: &ty::PolyProjectionPredicate<'tcx>,
+    ) -> Option<Ty<'tcx>> {
+        debug!("deduce_future_output_from_projection(projection={:?})", projection);
+
+        let trait_ref = projection.to_poly_trait_ref(self.tcx);
+        let future_trait = self.tcx.lang_items().future_trait().unwrap();
+        if trait_ref.def_id() != future_trait {
+            debug!("deduce_future_output_from_projection: not a future");
+            return None;
+        }
+
+        // The `Future` trait has only one associted item, `Output`,
+        // so check that this is what we see.
+        let output_assoc_item = self.tcx.associated_items(future_trait).nth(0).unwrap().def_id;
+        if output_assoc_item != projection.projection_def_id() {
+            span_bug!(
+                cause_span,
+                "projecting associated item `{:?}` from future, which is not Output `{:?}`",
+                projection.projection_def_id(),
+                output_assoc_item,
+            );
+        }
+
+        // Extract the type from the projection.
+        let output_ty = projection.skip_binder().ty;
+        let output_ty = self.resolve_vars_if_possible(&output_ty);
+        debug!("deduce_future_output_from_projection: output_ty={:?}", output_ty);
+        Some(output_ty)
+    }
+
     /// Converts the types that the user supplied, in case that doing
     /// so should yield an error, but returns back a signature where
     /// all parameters are of type `TyErr`.
diff --git a/src/test/ui/async-await/return-ty-raw-ptr-coercion.rs b/src/test/ui/async-await/return-ty-raw-ptr-coercion.rs
new file mode 100644
index 00000000000..570e745f8c7
--- /dev/null
+++ b/src/test/ui/async-await/return-ty-raw-ptr-coercion.rs
@@ -0,0 +1,25 @@
+// Check that we apply unsizing coercions based on the return type.
+//
+// Also serves as a regression test for #60424.
+//
+// edition:2018
+// check-pass
+
+#![allow(warnings)]
+
+use std::fmt::Debug;
+
+const TMP: u32 = 22;
+
+// Coerce from `Box<"asdf">` to `Box<dyn Debug>`.
+fn raw_pointer_coercion() {
+    fn sync_example() -> *const u32 {
+        &TMP
+    }
+
+    async fn async_example() -> *const u32 {
+        &TMP
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/async-await/return-ty-unsize-coercion.rs b/src/test/ui/async-await/return-ty-unsize-coercion.rs
new file mode 100644
index 00000000000..4d1b87677ed
--- /dev/null
+++ b/src/test/ui/async-await/return-ty-unsize-coercion.rs
@@ -0,0 +1,34 @@
+// Check that we apply unsizing coercions based on the return type.
+//
+// Also serves as a regression test for #60424.
+//
+// edition:2018
+// check-pass
+
+#![allow(warnings)]
+
+use std::fmt::Debug;
+
+// Coerce from `Box<"asdf">` to `Box<dyn Debug>`.
+fn unsize_trait_coercion() {
+    fn sync_example() -> Box<dyn Debug> {
+        Box::new("asdf")
+    }
+
+    async fn async_example() -> Box<dyn Debug> {
+        Box::new("asdf")
+    }
+}
+
+// Coerce from `Box<[u32; N]>` to `Box<[32]>`.
+fn unsize_slice_coercion() {
+    fn sync_example() -> Box<[u32]> {
+        Box::new([0])
+    }
+
+    async fn async_example() -> Box<[u32]> {
+        Box::new([0])
+    }
+}
+
+fn main() {}