Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spell out Self in async function return #72260

Merged
merged 6 commits into from
Jun 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/librustc_error_codes/error_codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,7 @@ E0751: include_str!("./error_codes/E0751.md"),
E0752: include_str!("./error_codes/E0752.md"),
E0753: include_str!("./error_codes/E0753.md"),
E0754: include_str!("./error_codes/E0754.md"),
E0760: include_str!("./error_codes/E0760.md"),
;
// E0006, // merged with E0005
// E0008, // cannot bind by-move into a pattern guard
Expand Down
32 changes: 32 additions & 0 deletions src/librustc_error_codes/error_codes/E0760.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
`async fn`/`impl trait` return type cannot contain a projection
or `Self` that references lifetimes from a parent scope.

Erroneous code example:

```compile_fail,E0760,edition2018
struct S<'a>(&'a i32);

impl<'a> S<'a> {
async fn new(i: &'a i32) -> Self {
S(&22)
}
}
```

To fix this error we need to spell out `Self` to `S<'a>`:

```edition2018
struct S<'a>(&'a i32);

impl<'a> S<'a> {
async fn new(i: &'a i32) -> S<'a> {
S(&22)
}
}
```

This will be allowed at some point in the future,
but the implementation is not yet complete.
See the [issue-61949] for this limitation.

[issue-61949]: https://github.com/rust-lang/rust/issues/61949
96 changes: 58 additions & 38 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1632,12 +1632,17 @@ fn check_opaque_for_inheriting_lifetimes(tcx: TyCtxt<'tcx>, def_id: LocalDefId,
struct ProhibitOpaqueVisitor<'tcx> {
opaque_identity_ty: Ty<'tcx>,
generics: &'tcx ty::Generics,
ty: Option<Ty<'tcx>>,
};

impl<'tcx> ty::fold::TypeVisitor<'tcx> for ProhibitOpaqueVisitor<'tcx> {
fn visit_ty(&mut self, t: Ty<'tcx>) -> bool {
debug!("check_opaque_for_inheriting_lifetimes: (visit_ty) t={:?}", t);
if t == self.opaque_identity_ty { false } else { t.super_visit_with(self) }
if t != self.opaque_identity_ty && t.super_visit_with(self) {
self.ty = Some(t);
return true;
}
false
}

fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool {
Expand All @@ -1650,46 +1655,61 @@ fn check_opaque_for_inheriting_lifetimes(tcx: TyCtxt<'tcx>, def_id: LocalDefId,
}
}

let prohibit_opaque = match item.kind {
ItemKind::OpaqueTy(hir::OpaqueTy {
origin: hir::OpaqueTyOrigin::AsyncFn | hir::OpaqueTyOrigin::FnReturn,
..
}) => {
let mut visitor = ProhibitOpaqueVisitor {
opaque_identity_ty: tcx.mk_opaque(
def_id.to_def_id(),
InternalSubsts::identity_for_item(tcx, def_id.to_def_id()),
),
generics: tcx.generics_of(def_id),
};
debug!("check_opaque_for_inheriting_lifetimes: visitor={:?}", visitor);

tcx.predicates_of(def_id)
.predicates
.iter()
.any(|(predicate, _)| predicate.visit_with(&mut visitor))
}
_ => false,
};

debug!("check_opaque_for_inheriting_lifetimes: prohibit_opaque={:?}", prohibit_opaque);
if prohibit_opaque {
let is_async = match item.kind {
ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => match origin {
hir::OpaqueTyOrigin::AsyncFn => true,
_ => false,
},
_ => unreachable!(),
if let ItemKind::OpaqueTy(hir::OpaqueTy {
origin: hir::OpaqueTyOrigin::AsyncFn | hir::OpaqueTyOrigin::FnReturn,
..
}) = item.kind
{
let mut visitor = ProhibitOpaqueVisitor {
opaque_identity_ty: tcx.mk_opaque(
def_id.to_def_id(),
InternalSubsts::identity_for_item(tcx, def_id.to_def_id()),
),
generics: tcx.generics_of(def_id),
ty: None,
};
let prohibit_opaque = tcx
.predicates_of(def_id)
.predicates
.iter()
.any(|(predicate, _)| predicate.visit_with(&mut visitor));
debug!(
"check_opaque_for_inheriting_lifetimes: prohibit_opaque={:?}, visitor={:?}",
prohibit_opaque, visitor
);

tcx.sess.span_err(
span,
&format!(
"`{}` return type cannot contain a projection or `Self` that references lifetimes from \
if prohibit_opaque {
let is_async = match item.kind {
ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => match origin {
hir::OpaqueTyOrigin::AsyncFn => true,
_ => false,
},
_ => unreachable!(),
};

let mut err = struct_span_err!(
tcx.sess,
span,
E0760,
"`{}` return type cannot contain a projection or `Self` that references lifetimes from \
a parent scope",
if is_async { "async fn" } else { "impl Trait" },
),
);
if is_async { "async fn" } else { "impl Trait" },
);

if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(span) {
if snippet == "Self" {
if let Some(ty) = visitor.ty {
err.span_suggestion(
span,
"consider spelling out the type instead",
format!("{:?}", ty),
Applicability::MaybeIncorrect,
);
}
}
}
err.emit();
}
}
}

Expand Down
5 changes: 3 additions & 2 deletions src/test/ui/async-await/issue-61949-self-return-type.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
error: `async fn` return type cannot contain a projection or `Self` that references lifetimes from a parent scope
error[E0760]: `async fn` return type cannot contain a projection or `Self` that references lifetimes from a parent scope
--> $DIR/issue-61949-self-return-type.rs:11:40
|
LL | pub async fn new(_bar: &'a i32) -> Self {
| ^^^^
| ^^^^ help: consider spelling out the type instead: `Foo<'a>`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0760`.
5 changes: 3 additions & 2 deletions src/test/ui/impl-trait/bound-normalization-fail.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ help: consider constraining the associated type `<T as impl_trait::Trait>::Assoc
LL | fn foo_fail<T: Trait<Assoc = ()>>() -> impl FooLike<Output=T::Assoc> {
| ^^^^^^^^^^^^

error: `impl Trait` return type cannot contain a projection or `Self` that references lifetimes from a parent scope
error[E0760]: `impl Trait` return type cannot contain a projection or `Self` that references lifetimes from a parent scope
--> $DIR/bound-normalization-fail.rs:43:41
|
LL | fn foo2_fail<'a, T: Trait<'a>>() -> impl FooLike<Output=T::Assoc> {
Expand All @@ -43,4 +43,5 @@ LL | fn foo2_fail<'a, T: Trait<'a, Assoc = ()>>() -> impl FooLike<Output=T::

error: aborting due to 3 previous errors; 1 warning emitted

For more information about this error, try `rustc --explain E0271`.
Some errors have detailed explanations: E0271, E0760.
For more information about an error, try `rustc --explain E0271`.