From 05bac9374131a523ee5c9a582f266848b0418c8d 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 | 32 ++++++++++++++++ .../capture-parent-arg.stderr | 28 ++++++++++++++ 3 files changed, 90 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 8c85d13650b40..5f629fe3c650d 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..8e4ad1a994908 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/capture-parent-arg.rs @@ -0,0 +1,32 @@ +#![feature(precise_capturing)] +//~^ WARN the feature `precise_capturing` is incomplete + +trait Tr { + type Assoc; +} + +struct W<'a>(&'a ()); + +impl Tr for W<'_> { + type Assoc = (); +} + +impl<'a> W<'a> { + fn good1() -> impl use<'a> Into< as Tr>::Assoc> {} +} + +impl<'a> W<'a> { + fn good2() -> impl use<'a> Into<::Assoc> {} +} + +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 +} + +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..ae314222b9479 --- /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:23: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:27: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 +