From 5daf58ffc1d46a63d33652cc953a3a801b8d1265 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 17 Apr 2024 21:30:41 -0400 Subject: [PATCH] Fix capturing duplicated lifetimes via parent --- .../rustc_hir_analysis/src/check/check.rs | 37 ++++++++++++++---- .../precise-capturing/capture-parent-arg.rs | 38 +++++++++++++++++++ .../capture-parent-arg.stderr | 28 ++++++++++++++ 3 files changed, 96 insertions(+), 7 deletions(-) create mode 100644 tests/ui/impl-trait/precise-capturing/capture-parent-arg.rs create mode 100644 tests/ui/impl-trait/precise-capturing/capture-parent-arg.stderr diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 3881e240cedbc..36553591de8ae 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -492,6 +492,7 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe }; let mut expected_captures = UnordSet::default(); + let mut shadowed_captures = UnordSet::default(); let mut seen_params = UnordMap::default(); let mut prev_non_lifetime_param = None; for arg in precise_capturing_args { @@ -530,6 +531,21 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe match tcx.named_bound_var(hir_id) { Some(ResolvedArg::EarlyBound(def_id)) => { expected_captures.insert(def_id); + + // Make sure we allow capturing these lifetimes through `Self` and + // `T::Assoc` projection syntax, too. These will occur when we only + // see lifetimes are captured after hir-lowering -- this aligns with + // the cases that were stabilized with the `impl_trait_projection` + // feature -- see . + if let DefKind::LifetimeParam = tcx.def_kind(def_id) + && let ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. }) + | ty::ReLateParam(ty::LateParamRegion { + bound_region: ty::BoundRegionKind::BrNamed(def_id, _), + .. + }) = *tcx.map_opaque_lifetime_to_parent_lifetime(def_id.expect_local()) + { + shadowed_captures.insert(def_id); + } } _ => { tcx.dcx().span_delayed_bug( @@ -555,23 +571,30 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe ); continue; } + // If a param is shadowed by a early-bound (duplicated) lifetime, then + // it may or may not be captured as invariant, depending on if it shows + // up through `Self` or `T::Assoc` syntax. + if shadowed_captures.contains(¶m.def_id) { + continue; + } match param.kind { ty::GenericParamDefKind::Lifetime => { // Check if the lifetime param was captured but isn't named in the precise captures list. if variances[param.index as usize] == ty::Invariant { - let param_span = - if let ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. }) + let param_span = if let DefKind::OpaqueTy = + tcx.def_kind(tcx.parent(param.def_id)) + && let ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. }) | ty::ReLateParam(ty::LateParamRegion { bound_region: ty::BoundRegionKind::BrNamed(def_id, _), .. }) = *tcx .map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()) - { - Some(tcx.def_span(def_id)) - } else { - None - }; + { + Some(tcx.def_span(def_id)) + } else { + None + }; // FIXME(precise_capturing): Structured suggestion for this would be useful tcx.dcx().emit_err(errors::LifetimeNotCaptured { use_span: tcx.def_span(param.def_id), diff --git a/tests/ui/impl-trait/precise-capturing/capture-parent-arg.rs b/tests/ui/impl-trait/precise-capturing/capture-parent-arg.rs new file mode 100644 index 0000000000000..f880bb038d549 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/capture-parent-arg.rs @@ -0,0 +1,38 @@ +#![feature(precise_capturing)] +//~^ WARN the feature `precise_capturing` is incomplete + +trait Tr { + type Assoc; +} + +struct W<'a>(&'a ()); + +impl Tr for W<'_> { + type Assoc = (); +} + +// The normal way of capturing `'a`... +impl<'a> W<'a> { + fn good1() -> impl use<'a> Into< as Tr>::Assoc> {} +} + +// This ensures that we don't error when we capture the *parent* copy of `'a`, +// since the opaque captures that rather than the duplicated `'a` lifetime +// synthesized from mentioning `'a` directly in the bounds. +impl<'a> W<'a> { + fn good2() -> impl use<'a> Into<::Assoc> {} +} + +// The normal way of capturing `'a`... but not mentioned in the bounds. +impl<'a> W<'a> { + fn bad1() -> impl use<> Into< as Tr>::Assoc> {} + //~^ ERROR `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list +} + +// But also make sure that we error here... +impl<'a> W<'a> { +//~^ ERROR `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list + fn bad2() -> impl use<> Into<::Assoc> {} +} + +fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/capture-parent-arg.stderr b/tests/ui/impl-trait/precise-capturing/capture-parent-arg.stderr new file mode 100644 index 0000000000000..85790d5716341 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/capture-parent-arg.stderr @@ -0,0 +1,28 @@ +warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/capture-parent-arg.rs:1:12 + | +LL | #![feature(precise_capturing)] + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #123432 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list + --> $DIR/capture-parent-arg.rs:28:37 + | +LL | impl<'a> W<'a> { + | -- this lifetime parameter is captured +LL | fn bad1() -> impl use<> Into< as Tr>::Assoc> {} + | -------------------^^---------------- lifetime captured due to being mentioned in the bounds of the `impl Trait` + +error: `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list + --> $DIR/capture-parent-arg.rs:33:6 + | +LL | impl<'a> W<'a> { + | ^^ +LL | +LL | fn bad2() -> impl use<> Into<::Assoc> {} + | ------------------------------------ lifetime captured due to being mentioned in the bounds of the `impl Trait` + +error: aborting due to 2 previous errors; 1 warning emitted +