Skip to content

Commit 3991285

Browse files
committed
Auto merge of #59111 - gilescope:generator-better-errors, r=nikomatsakis
Improved error message when type must be bound due to generator. Fixes #58930. Keen to get some feedback - is this as minimal as we can get it or is there an existing visitor I could repurpose?
2 parents c32171b + 66e41bc commit 3991285

File tree

9 files changed

+135
-35
lines changed

9 files changed

+135
-35
lines changed

src/librustc/error_codes.rs

+30
Original file line numberDiff line numberDiff line change
@@ -2043,6 +2043,36 @@ a (non-transparent) struct containing a single float, while `Grams` is a
20432043
transparent wrapper around a float. This can make a difference for the ABI.
20442044
"##,
20452045

2046+
E0698: r##"
2047+
When using generators (or async) all type variables must be bound so a
2048+
generator can be constructed.
2049+
2050+
Erroneous code example:
2051+
2052+
```edition2018,compile-fail,E0698
2053+
#![feature(futures_api, async_await, await_macro)]
2054+
async fn bar<T>() -> () {}
2055+
2056+
async fn foo() {
2057+
await!(bar()); // error: cannot infer type for `T`
2058+
}
2059+
```
2060+
2061+
In the above example `T` is unknowable by the compiler.
2062+
To fix this you must bind `T` to a concrete type such as `String`
2063+
so that a generator can then be constructed:
2064+
2065+
```edition2018
2066+
#![feature(futures_api, async_await, await_macro)]
2067+
async fn bar<T>() -> () {}
2068+
2069+
async fn foo() {
2070+
await!(bar::<String>());
2071+
// ^^^^^^^^ specify type explicitly
2072+
}
2073+
```
2074+
"##,
2075+
20462076
E0700: r##"
20472077
The `impl Trait` return type captures lifetime parameters that do not
20482078
appear within the `impl Trait` itself.

src/librustc/infer/error_reporting/need_type_info.rs

+31-13
Original file line numberDiff line numberDiff line change
@@ -88,23 +88,17 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
8888
s
8989
}
9090

91-
pub fn need_type_info_err(&self,
92-
body_id: Option<hir::BodyId>,
93-
span: Span,
94-
ty: Ty<'tcx>)
95-
-> DiagnosticBuilder<'gcx> {
91+
pub fn need_type_info_err(
92+
&self,
93+
body_id: Option<hir::BodyId>,
94+
span: Span,
95+
ty: Ty<'tcx>
96+
) -> DiagnosticBuilder<'gcx> {
9697
let ty = self.resolve_type_vars_if_possible(&ty);
9798
let name = self.extract_type_name(&ty, None);
9899

99100
let mut err_span = span;
100-
let mut labels = vec![(
101-
span,
102-
if &name == "_" {
103-
"cannot infer type".to_owned()
104-
} else {
105-
format!("cannot infer type for `{}`", name)
106-
},
107-
)];
101+
let mut labels = vec![(span, InferCtxt::missing_type_msg(&name))];
108102

109103
let mut local_visitor = FindLocalByTypeVisitor {
110104
infcx: &self,
@@ -166,4 +160,28 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
166160

167161
err
168162
}
163+
164+
pub fn need_type_info_err_in_generator(
165+
&self,
166+
span: Span,
167+
ty: Ty<'tcx>
168+
) -> DiagnosticBuilder<'gcx> {
169+
let ty = self.resolve_type_vars_if_possible(&ty);
170+
let name = self.extract_type_name(&ty, None);
171+
172+
let mut err = struct_span_err!(self.tcx.sess,
173+
span,
174+
E0698,
175+
"type inside generator must be known in this context");
176+
err.span_label(span, InferCtxt::missing_type_msg(&name));
177+
err
178+
}
179+
180+
fn missing_type_msg(type_name: &str) -> String {
181+
if type_name == "_" {
182+
"cannot infer type".to_owned()
183+
} else {
184+
format!("cannot infer type for `{}`", type_name)
185+
}
186+
}
169187
}

src/librustc/infer/mod.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -1309,17 +1309,18 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
13091309
value.fold_with(&mut r)
13101310
}
13111311

1312-
/// Returns `true` if `T` contains unresolved type variables. In the
1312+
/// Returns first unresolved variable contained in `T`. In the
13131313
/// process of visiting `T`, this will resolve (where possible)
13141314
/// type variables in `T`, but it never constructs the final,
13151315
/// resolved type, so it's more efficient than
13161316
/// `resolve_type_vars_if_possible()`.
1317-
pub fn any_unresolved_type_vars<T>(&self, value: &T) -> bool
1317+
pub fn unresolved_type_vars<T>(&self, value: &T) -> Option<(Ty<'tcx>, Option<Span>)>
13181318
where
13191319
T: TypeFoldable<'tcx>,
13201320
{
13211321
let mut r = resolve::UnresolvedTypeFinder::new(self);
1322-
value.visit_with(&mut r)
1322+
value.visit_with(&mut r);
1323+
r.first_unresolved
13231324
}
13241325

13251326
pub fn fully_resolve<T: TypeFoldable<'tcx>>(&self, value: &T) -> FixupResult<T> {

src/librustc/infer/resolve.rs

+27-9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::{InferCtxt, FixupError, FixupResult};
1+
use super::{InferCtxt, FixupError, FixupResult, Span, type_variable::TypeVariableOrigin};
22
use crate::ty::{self, Ty, TyCtxt, TypeFoldable};
33
use crate::ty::fold::{TypeFolder, TypeVisitor};
44

@@ -77,40 +77,58 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for OpportunisticTypeAndRegionResolv
7777
///////////////////////////////////////////////////////////////////////////
7878
// UNRESOLVED TYPE FINDER
7979

80-
/// The unresolved type **finder** walks your type and searches for
81-
/// type variables that don't yet have a value. They get pushed into a
82-
/// vector. It does not construct the fully resolved type (which might
80+
/// The unresolved type **finder** walks a type searching for
81+
/// type variables that don't yet have a value. The first unresolved type is stored.
82+
/// It does not construct the fully resolved type (which might
8383
/// involve some hashing and so forth).
8484
pub struct UnresolvedTypeFinder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
8585
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
86+
87+
/// Used to find the type parameter name and location for error reporting.
88+
pub first_unresolved: Option<(Ty<'tcx>,Option<Span>)>,
8689
}
8790

8891
impl<'a, 'gcx, 'tcx> UnresolvedTypeFinder<'a, 'gcx, 'tcx> {
8992
pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>) -> Self {
90-
UnresolvedTypeFinder { infcx }
93+
UnresolvedTypeFinder { infcx, first_unresolved: None }
9194
}
9295
}
9396

9497
impl<'a, 'gcx, 'tcx> TypeVisitor<'tcx> for UnresolvedTypeFinder<'a, 'gcx, 'tcx> {
9598
fn visit_ty(&mut self, t: Ty<'tcx>) -> bool {
9699
let t = self.infcx.shallow_resolve(t);
97100
if t.has_infer_types() {
98-
if let ty::Infer(_) = t.sty {
101+
if let ty::Infer(infer_ty) = t.sty {
99102
// Since we called `shallow_resolve` above, this must
100103
// be an (as yet...) unresolved inference variable.
101-
true
104+
let ty_var_span =
105+
if let ty::TyVar(ty_vid) = infer_ty {
106+
let ty_vars = self.infcx.type_variables.borrow();
107+
if let TypeVariableOrigin::TypeParameterDefinition(span, _name)
108+
= *ty_vars.var_origin(ty_vid)
109+
{
110+
Some(span)
111+
} else {
112+
None
113+
}
114+
} else {
115+
None
116+
};
117+
self.first_unresolved = Some((t, ty_var_span));
118+
true // Halt visiting.
102119
} else {
103120
// Otherwise, visit its contents.
104121
t.super_visit_with(self)
105122
}
106123
} else {
107-
// Micro-optimize: no inference types at all Can't have unresolved type
108-
// variables, no need to visit the contents.
124+
// All type variables in inference types must already be resolved,
125+
// - no need to visit the contents, continue visiting.
109126
false
110127
}
111128
}
112129
}
113130

131+
114132
///////////////////////////////////////////////////////////////////////////
115133
// FULL TYPE RESOLUTION
116134

src/librustc/traits/project.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
594594

595595
// Once we have inferred everything we need to know, we
596596
// can ignore the `obligations` from that point on.
597-
if !infcx.any_unresolved_type_vars(&ty.value) {
597+
if infcx.unresolved_type_vars(&ty.value).is_none() {
598598
infcx.projection_cache.borrow_mut().complete_normalized(cache_key, &ty);
599599
// No need to extend `obligations`.
600600
} else {
@@ -704,7 +704,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
704704
fn prune_cache_value_obligations<'a, 'gcx, 'tcx>(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
705705
result: &NormalizedTy<'tcx>)
706706
-> NormalizedTy<'tcx> {
707-
if !infcx.any_unresolved_type_vars(&result.value) {
707+
if infcx.unresolved_type_vars(&result.value).is_none() {
708708
return NormalizedTy { value: result.value, obligations: vec![] };
709709
}
710710

@@ -722,7 +722,7 @@ fn prune_cache_value_obligations<'a, 'gcx, 'tcx>(infcx: &'a InferCtxt<'a, 'gcx,
722722
// but we have `T: Foo<X = ?1>` and `?1: Bar<X =
723723
// ?0>`).
724724
ty::Predicate::Projection(ref data) =>
725-
infcx.any_unresolved_type_vars(&data.ty()),
725+
infcx.unresolved_type_vars(&data.ty()).is_some(),
726726

727727
// We are only interested in `T: Foo<X = U>` predicates, whre
728728
// `U` references one of `unresolved_type_vars`. =)

src/librustc_typeck/check/generator_interior.rs

+10-6
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,16 @@ impl<'a, 'gcx, 'tcx> InteriorVisitor<'a, 'gcx, 'tcx> {
5454
debug!("type in expr = {:?}, scope = {:?}, type = {:?}, count = {}, yield_span = {:?}",
5555
expr, scope, ty, self.expr_count, yield_span);
5656

57-
if self.fcx.any_unresolved_type_vars(&ty) {
58-
let mut err = struct_span_err!(self.fcx.tcx.sess, source_span, E0698,
59-
"type inside generator must be known in this context");
60-
err.span_note(yield_span,
61-
"the type is part of the generator because of this `yield`");
62-
err.emit();
57+
if let Some((unresolved_type, unresolved_type_span)) =
58+
self.fcx.unresolved_type_vars(&ty)
59+
{
60+
// If unresolved type isn't a ty_var then unresolved_type_span is None
61+
self.fcx.need_type_info_err_in_generator(
62+
unresolved_type_span.unwrap_or(yield_span),
63+
unresolved_type)
64+
.span_note(yield_span,
65+
"the type is part of the generator because of this `yield`")
66+
.emit();
6367
} else {
6468
// Map the type to the number of types added before it
6569
let entries = self.types.len();

src/librustc_typeck/error_codes.rs

-1
Original file line numberDiff line numberDiff line change
@@ -4728,7 +4728,6 @@ register_diagnostics! {
47284728
E0640, // infer outlives requirements
47294729
E0641, // cannot cast to/from a pointer with an unknown kind
47304730
E0645, // trait aliases not finished
4731-
E0698, // type inside generator must be known in this context
47324731
E0719, // duplicate values for associated type binding
47334732
E0722, // Malformed #[optimize] attribute
47344733
E0724, // `#[ffi_returns_twice]` is only allowed in foreign functions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Provoke an unresolved type error (T).
2+
// Error message should pinpoint the type parameter T as needing to be bound
3+
// (rather than give a general error message)
4+
// edition:2018
5+
#![feature(futures_api, async_await, await_macro)]
6+
async fn bar<T>() -> () {}
7+
8+
async fn foo() {
9+
await!(bar());
10+
//~^ ERROR type inside generator must be known in this context
11+
//~| NOTE cannot infer type for `T`
12+
//~| NOTE the type is part of the generator because of this `yield`
13+
}
14+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error[E0698]: type inside generator must be known in this context
2+
--> $DIR/unresolved_type_param.rs:9:16
3+
|
4+
LL | await!(bar());
5+
| ^^^ cannot infer type for `T`
6+
|
7+
note: the type is part of the generator because of this `yield`
8+
--> $DIR/unresolved_type_param.rs:9:9
9+
|
10+
LL | await!(bar());
11+
| ^^^^^^^^^^^^^^
12+
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
13+
14+
error: aborting due to previous error
15+
16+
For more information about this error, try `rustc --explain E0698`.

0 commit comments

Comments
 (0)