From 8742fd9c8592a846d487e8a1cdab8235859c4bc8 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 8 Jan 2023 00:29:30 +0000 Subject: [PATCH] Label closure captures/generator locals that make opaque types recursive --- .../rustc_hir_analysis/src/check/check.rs | 59 +++++++++++++++++-- tests/ui/impl-trait/recursive-generator.rs | 23 ++++++++ .../ui/impl-trait/recursive-generator.stderr | 19 ++++++ .../recursive-impl-trait-type-indirect.stderr | 4 ++ 4 files changed, 99 insertions(+), 6 deletions(-) create mode 100644 tests/ui/impl-trait/recursive-generator.rs create mode 100644 tests/ui/impl-trait/recursive-generator.stderr diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index aa75132444a5a..abc1c2d7b8d17 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1391,11 +1391,15 @@ fn async_opaque_type_cycle_error(tcx: TyCtxt<'_>, span: Span) -> ErrorGuaranteed /// /// If all the return expressions evaluate to `!`, then we explain that the error will go away /// after changing it. This can happen when a user uses `panic!()` or similar as a placeholder. -fn opaque_type_cycle_error(tcx: TyCtxt<'_>, def_id: LocalDefId, span: Span) -> ErrorGuaranteed { +fn opaque_type_cycle_error( + tcx: TyCtxt<'_>, + opaque_def_id: LocalDefId, + span: Span, +) -> ErrorGuaranteed { let mut err = struct_span_err!(tcx.sess, span, E0720, "cannot resolve opaque type"); let mut label = false; - if let Some((def_id, visitor)) = get_owner_return_paths(tcx, def_id) { + if let Some((def_id, visitor)) = get_owner_return_paths(tcx, opaque_def_id) { let typeck_results = tcx.typeck(def_id); if visitor .returns @@ -1431,21 +1435,30 @@ fn opaque_type_cycle_error(tcx: TyCtxt<'_>, def_id: LocalDefId, span: Span) -> E .filter_map(|e| typeck_results.node_type_opt(e.hir_id).map(|t| (e.span, t))) .filter(|(_, ty)| !matches!(ty.kind(), ty::Never)) { - struct OpaqueTypeCollector(Vec); + #[derive(Default)] + struct OpaqueTypeCollector { + opaques: Vec, + closures: Vec, + } impl<'tcx> ty::visit::TypeVisitor<'tcx> for OpaqueTypeCollector { fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { match *t.kind() { ty::Alias(ty::Opaque, ty::AliasTy { def_id: def, .. }) => { - self.0.push(def); + self.opaques.push(def); ControlFlow::Continue(()) } + ty::Closure(def_id, ..) | ty::Generator(def_id, ..) => { + self.closures.push(def_id); + t.super_visit_with(self) + } _ => t.super_visit_with(self), } } } - let mut visitor = OpaqueTypeCollector(vec![]); + + let mut visitor = OpaqueTypeCollector::default(); ty.visit_with(&mut visitor); - for def_id in visitor.0 { + for def_id in visitor.opaques { let ty_span = tcx.def_span(def_id); if !seen.contains(&ty_span) { err.span_label(ty_span, &format!("returning this opaque type `{ty}`")); @@ -1453,6 +1466,40 @@ fn opaque_type_cycle_error(tcx: TyCtxt<'_>, def_id: LocalDefId, span: Span) -> E } err.span_label(sp, &format!("returning here with type `{ty}`")); } + + for closure_def_id in visitor.closures { + let Some(closure_local_did) = closure_def_id.as_local() else { continue; }; + let typeck_results = tcx.typeck(closure_local_did); + + let mut label_match = |ty: Ty<'_>, span| { + for arg in ty.walk() { + if let ty::GenericArgKind::Type(ty) = arg.unpack() + && let ty::Alias(ty::Opaque, ty::AliasTy { def_id: captured_def_id, .. }) = *ty.kind() + && captured_def_id == opaque_def_id.to_def_id() + { + err.span_label( + span, + format!( + "{} captures itself here", + tcx.def_kind(closure_def_id).descr(closure_def_id) + ), + ); + } + } + }; + + // Label any closure upvars that capture the opaque + for capture in typeck_results.closure_min_captures_flattened(closure_local_did) + { + label_match(capture.place.ty(), capture.get_path_span(tcx)); + } + // Label any generator locals that capture the opaque + for interior_ty in + typeck_results.generator_interior_types.as_ref().skip_binder() + { + label_match(interior_ty.ty, interior_ty.span); + } + } } } } diff --git a/tests/ui/impl-trait/recursive-generator.rs b/tests/ui/impl-trait/recursive-generator.rs new file mode 100644 index 0000000000000..e876f0fb43f65 --- /dev/null +++ b/tests/ui/impl-trait/recursive-generator.rs @@ -0,0 +1,23 @@ +#![feature(generators, generator_trait)] + +use std::ops::{Generator, GeneratorState}; + +fn foo() -> impl Generator { + //~^ ERROR cannot resolve opaque type + //~| NOTE recursive opaque type + //~| NOTE in this expansion of desugaring of + || { + //~^ NOTE returning here + let mut gen = Box::pin(foo()); + //~^ NOTE generator captures itself here + let mut r = gen.as_mut().resume(()); + while let GeneratorState::Yielded(v) = r { + yield v; + r = gen.as_mut().resume(()); + } + } +} + +fn main() { + foo(); +} diff --git a/tests/ui/impl-trait/recursive-generator.stderr b/tests/ui/impl-trait/recursive-generator.stderr new file mode 100644 index 0000000000000..e23fd4b4a85e5 --- /dev/null +++ b/tests/ui/impl-trait/recursive-generator.stderr @@ -0,0 +1,19 @@ +error[E0720]: cannot resolve opaque type + --> $DIR/recursive-generator.rs:5:13 + | +LL | fn foo() -> impl Generator { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive opaque type +... +LL | / || { +LL | | +LL | | let mut gen = Box::pin(foo()); + | | ------- generator captures itself here +LL | | +... | +LL | | } +LL | | } + | |_____- returning here with type `[generator@$DIR/recursive-generator.rs:9:5: 9:7]` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0720`. diff --git a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr index 2e34d3d4275ad..ebb231ae14f0d 100644 --- a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr +++ b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr @@ -53,6 +53,7 @@ LL | fn closure_capture() -> impl Sized { ... LL | / move || { LL | | x; + | | - closure captures itself here LL | | } | |_____- returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:35:5: 35:12]` @@ -64,6 +65,7 @@ LL | fn closure_ref_capture() -> impl Sized { ... LL | / move || { LL | | &x; + | | - closure captures itself here LL | | } | |_____- returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:43:5: 43:12]` @@ -94,6 +96,7 @@ LL | fn generator_capture() -> impl Sized { LL | / move || { LL | | yield; LL | | x; + | | - generator captures itself here LL | | } | |_____- returning here with type `[generator@$DIR/recursive-impl-trait-type-indirect.rs:61:5: 61:12]` @@ -114,6 +117,7 @@ LL | fn generator_hold() -> impl Sized { LL | LL | / move || { LL | | let x = generator_hold(); + | | - generator captures itself here LL | | yield; LL | | x; LL | | }