about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2024-04-17 21:30:41 -0400
committerMichael Goulet <michael@errs.io>2024-04-19 14:12:21 -0400
commit5daf58ffc1d46a63d33652cc953a3a801b8d1265 (patch)
treebc17d69d1944c4aa5b0e7904bb6a941391f4afd9
parentd1a0fa5ed3ffe52d72f761d3c95cbeb0a9cdfe66 (diff)
downloadrust-5daf58ffc1d46a63d33652cc953a3a801b8d1265.tar.gz
rust-5daf58ffc1d46a63d33652cc953a3a801b8d1265.zip
Fix capturing duplicated lifetimes via parent
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs37
-rw-r--r--tests/ui/impl-trait/precise-capturing/capture-parent-arg.rs38
-rw-r--r--tests/ui/impl-trait/precise-capturing/capture-parent-arg.stderr28
3 files changed, 96 insertions, 7 deletions
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 3881e240ced..36553591de8 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 <https://github.com/rust-lang/rust/pull/115659>.
+                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(&param.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 00000000000..f880bb038d5
--- /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<<W<'a> 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<<Self as Tr>::Assoc> {}
+}
+
+// The normal way of capturing `'a`... but not mentioned in the bounds.
+impl<'a> W<'a> {
+    fn bad1() -> impl use<> Into<<W<'a> 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<<Self as Tr>::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 00000000000..85790d57163
--- /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 <https://github.com/rust-lang/rust/issues/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<<W<'a> 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<<Self as Tr>::Assoc> {}
+   |                  ------------------------------------ lifetime captured due to being mentioned in the bounds of the `impl Trait`
+
+error: aborting due to 2 previous errors; 1 warning emitted
+