1
Fork 0

Rollup merge of #76765 - guswynn:async_return, r=tmandry

Make it more clear what an about async fn's returns when referring to what it returns

see #76547

This is *likely* not the ONLY place that this happens to be unclear, but we can move this fn to rustc_middle or something like that and reuse it if need be, to apply it to more diagnostics

One outstanding question I have is, if the fn returns (), should I make the message more clear (what about `fn f()` vs `fn f() -> ()`, can you tell those apart in the hir?)

R? `@tmandry`

`@rustbot` modify labels +A-diagnostics +T-compiler
This commit is contained in:
Jonas Schievink 2020-11-10 14:45:09 +01:00 committed by GitHub
commit 9596e34ad4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 353 additions and 135 deletions

View file

@ -102,43 +102,89 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
None => String::new(),
};
let (span_1, span_2, main_label, span_label) = match (sup_is_ret_type, sub_is_ret_type) {
(None, None) => {
let (main_label_1, span_label_1) = if ty_sup.hir_id == ty_sub.hir_id {
(
"this type is declared with multiple lifetimes...".to_owned(),
"...but data with one lifetime flows into the other here".to_owned(),
)
} else {
(
"these two types are declared with different lifetimes...".to_owned(),
format!("...but data{} flows{} here", span_label_var1, span_label_var2),
)
};
(ty_sup.span, ty_sub.span, main_label_1, span_label_1)
}
let (span_1, span_2, main_label, span_label, future_return_type) =
match (sup_is_ret_type, sub_is_ret_type) {
(None, None) => {
let (main_label_1, span_label_1) = if ty_sup.hir_id == ty_sub.hir_id {
(
"this type is declared with multiple lifetimes...".to_owned(),
"...but data with one lifetime flows into the other here".to_owned(),
)
} else {
(
"these two types are declared with different lifetimes...".to_owned(),
format!("...but data{} flows{} here", span_label_var1, span_label_var2),
)
};
(ty_sup.span, ty_sub.span, main_label_1, span_label_1, None)
}
(Some(ret_span), _) => (
ty_sub.span,
ret_span,
"this parameter and the return type are declared with different lifetimes..."
.to_owned(),
format!("...but data{} is returned here", span_label_var1),
),
(_, Some(ret_span)) => (
ty_sup.span,
ret_span,
"this parameter and the return type are declared with different lifetimes..."
.to_owned(),
format!("...but data{} is returned here", span_label_var1),
),
};
(Some(ret_span), _) => {
let sup_future = self.future_return_type(scope_def_id_sup);
let (return_type, action) = if let Some(_) = sup_future {
("returned future", "held across an await point")
} else {
("return type", "returned")
};
struct_span_err!(self.tcx().sess, span, E0623, "lifetime mismatch")
.span_label(span_1, main_label)
.span_label(span_2, String::new())
.span_label(span, span_label)
.emit();
(
ty_sub.span,
ret_span,
format!(
"this parameter and the {} are declared with different lifetimes...",
return_type
),
format!("...but data{} is {} here", span_label_var1, action),
sup_future,
)
}
(_, Some(ret_span)) => {
let sub_future = self.future_return_type(scope_def_id_sub);
let (return_type, action) = if let Some(_) = sub_future {
("returned future", "held across an await point")
} else {
("return type", "returned")
};
(
ty_sup.span,
ret_span,
format!(
"this parameter and the {} are declared with different lifetimes...",
return_type
),
format!("...but data{} is {} here", span_label_var1, action),
sub_future,
)
}
};
let mut e = struct_span_err!(self.tcx().sess, span, E0623, "lifetime mismatch");
e.span_label(span_1, main_label);
e.span_label(span_2, String::new());
e.span_label(span, span_label);
if let Some(t) = future_return_type {
let snip = self
.tcx()
.sess
.source_map()
.span_to_snippet(t.span)
.ok()
.and_then(|s| match (&t.kind, s.as_str()) {
(rustc_hir::TyKind::Tup(&[]), "") => Some("()".to_string()),
(_, "") => None,
_ => Some(s),
})
.unwrap_or("{unnamed_type}".to_string());
e.span_label(
t.span,
&format!("this `async fn` implicitly returns an `impl Future<Output = {}>`", snip),
);
}
e.emit();
Some(ErrorReported)
}
}

View file

@ -85,6 +85,60 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
})
}
pub(super) fn future_return_type(
&self,
local_def_id: LocalDefId,
) -> Option<&rustc_hir::Ty<'_>> {
if let Some(hir::IsAsync::Async) = self.asyncness(local_def_id) {
if let rustc_middle::ty::Opaque(def_id, _) =
self.tcx().type_of(local_def_id).fn_sig(self.tcx()).output().skip_binder().kind()
{
match self.tcx().hir().get_if_local(*def_id) {
Some(hir::Node::Item(hir::Item {
kind:
hir::ItemKind::OpaqueTy(hir::OpaqueTy {
bounds,
origin: hir::OpaqueTyOrigin::AsyncFn,
..
}),
..
})) => {
for b in bounds.iter() {
if let hir::GenericBound::LangItemTrait(
hir::LangItem::Future,
_span,
_hir_id,
generic_args,
) = b
{
for type_binding in generic_args.bindings.iter() {
if type_binding.ident.name == rustc_span::sym::Output {
if let hir::TypeBindingKind::Equality { ty } =
type_binding.kind
{
return Some(ty);
}
}
}
}
}
}
_ => {}
}
}
}
None
}
pub(super) fn asyncness(&self, local_def_id: LocalDefId) -> Option<hir::IsAsync> {
// similar to the asyncness fn in rustc_ty::ty
let hir_id = self.tcx().hir().local_def_id_to_hir_id(local_def_id);
let node = self.tcx().hir().get(hir_id);
let fn_like = rustc_middle::hir::map::blocks::FnLikeNode::from_node(node)?;
Some(fn_like.asyncness())
}
// Here, we check for the case where the anonymous region
// is in the return type.
// FIXME(#42703) - Need to handle certain cases here.