diff options
| author | Niko Matsakis <niko@alum.mit.edu> | 2019-10-02 10:03:40 -0400 |
|---|---|---|
| committer | Niko Matsakis <niko@alum.mit.edu> | 2019-10-02 10:03:40 -0400 |
| commit | f7ed53c9dad467b5b9e33398e9e33f00a6724d96 (patch) | |
| tree | cf32ecf52a5b5eff49b9809e9f0a3bd833285b19 /src | |
| parent | c8e58512d4ad327cdc974b6bfbde62e2778f0cc3 (diff) | |
| download | rust-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.rs | 102 | ||||
| -rw-r--r-- | src/test/ui/async-await/return-ty-raw-ptr-coercion.rs | 25 | ||||
| -rw-r--r-- | src/test/ui/async-await/return-ty-unsize-coercion.rs | 34 |
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() {} |
