Skip to content

Commit f7ed53c

Browse files
committed
extract expected return type from -> impl Future obligation
1 parent c8e5851 commit f7ed53c

File tree

3 files changed

+158
-3
lines changed

3 files changed

+158
-3
lines changed

src/librustc_typeck/check/closure.rs

+99-3
Original file line numberDiff line numberDiff line change
@@ -611,9 +611,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
611611
// function.
612612
Some(hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Fn)) => {
613613
debug!("supplied_sig_of_closure: closure is async fn body");
614-
615-
// FIXME
616-
astconv.ty_infer(None, decl.output.span())
614+
self.deduce_future_output_from_obligations(expr_def_id)
617615
}
618616

619617
_ => astconv.ty_infer(None, decl.output.span()),
@@ -639,6 +637,104 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
639637
result
640638
}
641639

640+
/// Invoked when we are translating the generator that results
641+
/// from desugaring an `async fn`. Returns the "sugared" return
642+
/// type of the `async fn` -- that is, the return type that the
643+
/// user specified. The "desugared" return type is a `impl
644+
/// Future<Output = T>`, so we do this by searching through the
645+
/// obligations to extract the `T`.
646+
fn deduce_future_output_from_obligations(
647+
&self,
648+
expr_def_id: DefId,
649+
) -> Ty<'tcx> {
650+
debug!("deduce_future_output_from_obligations(expr_def_id={:?})", expr_def_id);
651+
652+
let ret_coercion =
653+
self.ret_coercion
654+
.as_ref()
655+
.unwrap_or_else(|| span_bug!(
656+
self.tcx.def_span(expr_def_id),
657+
"async fn generator outside of a fn"
658+
));
659+
660+
// In practice, the return type of the surrounding function is
661+
// always a (not yet resolved) inference variable, because it
662+
// is the hidden type for an `impl Trait` that we are going to
663+
// be inferring.
664+
let ret_ty = ret_coercion.borrow().expected_ty();
665+
let ret_ty = self.inh.infcx.shallow_resolve(ret_ty);
666+
let ret_vid = match ret_ty.sty {
667+
ty::Infer(ty::TyVar(ret_vid)) => ret_vid,
668+
_ => {
669+
span_bug!(
670+
self.tcx.def_span(expr_def_id),
671+
"async fn generator return type not an inference variable"
672+
)
673+
}
674+
};
675+
676+
// Search for a pending obligation like
677+
//
678+
// `<R as Future>::Output = T`
679+
//
680+
// where R is the return type we are expecting. This type `T`
681+
// will be our output.
682+
let output_ty = self.obligations_for_self_ty(ret_vid)
683+
.find_map(|(_, obligation)| {
684+
if let ty::Predicate::Projection(ref proj_predicate) = obligation.predicate {
685+
self.deduce_future_output_from_projection(
686+
obligation.cause.span,
687+
proj_predicate
688+
)
689+
} else {
690+
None
691+
}
692+
})
693+
.unwrap();
694+
695+
debug!("deduce_future_output_from_obligations: output_ty={:?}", output_ty);
696+
output_ty
697+
}
698+
699+
/// Given a projection like
700+
///
701+
/// `<_ as Future>::Output = T`
702+
///
703+
/// returns `Some(T)`. If the projection is for some other trait,
704+
/// returns `None`.
705+
fn deduce_future_output_from_projection(
706+
&self,
707+
cause_span: Span,
708+
projection: &ty::PolyProjectionPredicate<'tcx>,
709+
) -> Option<Ty<'tcx>> {
710+
debug!("deduce_future_output_from_projection(projection={:?})", projection);
711+
712+
let trait_ref = projection.to_poly_trait_ref(self.tcx);
713+
let future_trait = self.tcx.lang_items().future_trait().unwrap();
714+
if trait_ref.def_id() != future_trait {
715+
debug!("deduce_future_output_from_projection: not a future");
716+
return None;
717+
}
718+
719+
// The `Future` trait has only one associted item, `Output`,
720+
// so check that this is what we see.
721+
let output_assoc_item = self.tcx.associated_items(future_trait).nth(0).unwrap().def_id;
722+
if output_assoc_item != projection.projection_def_id() {
723+
span_bug!(
724+
cause_span,
725+
"projecting associated item `{:?}` from future, which is not Output `{:?}`",
726+
projection.projection_def_id(),
727+
output_assoc_item,
728+
);
729+
}
730+
731+
// Extract the type from the projection.
732+
let output_ty = projection.skip_binder().ty;
733+
let output_ty = self.resolve_vars_if_possible(&output_ty);
734+
debug!("deduce_future_output_from_projection: output_ty={:?}", output_ty);
735+
Some(output_ty)
736+
}
737+
642738
/// Converts the types that the user supplied, in case that doing
643739
/// so should yield an error, but returns back a signature where
644740
/// all parameters are of type `TyErr`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Check that we apply unsizing coercions based on the return type.
2+
//
3+
// Also serves as a regression test for #60424.
4+
//
5+
// edition:2018
6+
// check-pass
7+
8+
#![allow(warnings)]
9+
10+
use std::fmt::Debug;
11+
12+
const TMP: u32 = 22;
13+
14+
// Coerce from `Box<"asdf">` to `Box<dyn Debug>`.
15+
fn raw_pointer_coercion() {
16+
fn sync_example() -> *const u32 {
17+
&TMP
18+
}
19+
20+
async fn async_example() -> *const u32 {
21+
&TMP
22+
}
23+
}
24+
25+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Check that we apply unsizing coercions based on the return type.
2+
//
3+
// Also serves as a regression test for #60424.
4+
//
5+
// edition:2018
6+
// check-pass
7+
8+
#![allow(warnings)]
9+
10+
use std::fmt::Debug;
11+
12+
// Coerce from `Box<"asdf">` to `Box<dyn Debug>`.
13+
fn unsize_trait_coercion() {
14+
fn sync_example() -> Box<dyn Debug> {
15+
Box::new("asdf")
16+
}
17+
18+
async fn async_example() -> Box<dyn Debug> {
19+
Box::new("asdf")
20+
}
21+
}
22+
23+
// Coerce from `Box<[u32; N]>` to `Box<[32]>`.
24+
fn unsize_slice_coercion() {
25+
fn sync_example() -> Box<[u32]> {
26+
Box::new([0])
27+
}
28+
29+
async fn async_example() -> Box<[u32]> {
30+
Box::new([0])
31+
}
32+
}
33+
34+
fn main() {}

0 commit comments

Comments
 (0)