about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2024-06-24 01:11:56 +0000
committerEsteban Küber <esteban@kuber.com.ar>2024-06-24 03:39:54 +0000
commita2298a6f190a020da69860433b4031775ea82cbe (patch)
tree110563a04a4e3472799f084773959e9fa4da6500
parent33422e72c8a66bdb5ee21246a948a1a02ca91674 (diff)
downloadrust-a2298a6f190a020da69860433b4031775ea82cbe.tar.gz
rust-a2298a6f190a020da69860433b4031775ea82cbe.zip
Do not ICE when suggesting dereferencing closure arg
Account for `for` lifetimes when constructing closure to see if dereferencing the return value would be valid.

Fix #125634, fix #124563.
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs6
-rw-r--r--tests/ui/regions/account-for-lifetimes-in-closure-suggestion.rs19
-rw-r--r--tests/ui/regions/account-for-lifetimes-in-closure-suggestion.stderr17
-rw-r--r--tests/ui/regions/lifetime-not-long-enough-suggestion-regression-test-124563.rs (renamed from tests/crashes/124563.rs)11
-rw-r--r--tests/ui/regions/lifetime-not-long-enough-suggestion-regression-test-124563.stderr49
-rw-r--r--tests/ui/regions/regions-escape-method.fixed17
-rw-r--r--tests/ui/regions/regions-escape-method.rs1
-rw-r--r--tests/ui/regions/regions-escape-method.stderr7
8 files changed, 119 insertions, 8 deletions
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index 67c11ff4a5b..d0cdf2baf99 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -1152,7 +1152,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         // Get the arguments for the found method, only specifying that `Self` is the receiver type.
         let Some(possible_rcvr_ty) = typeck_results.node_type_opt(rcvr.hir_id) else { return };
         let args = GenericArgs::for_item(tcx, method_def_id, |param, _| {
-            if param.index == 0 {
+            if let ty::GenericParamDefKind::Lifetime = param.kind {
+                tcx.lifetimes.re_erased.into()
+            } else if param.index == 0 && param.name == kw::SelfUpper {
                 possible_rcvr_ty.into()
             } else if param.index == closure_param.index {
                 closure_ty.into()
@@ -1169,7 +1171,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
             Obligation::misc(tcx, span, self.mir_def_id(), self.param_env, pred)
         }));
 
-        if ocx.select_all_or_error().is_empty() {
+        if ocx.select_all_or_error().is_empty() && count > 0 {
             diag.span_suggestion_verbose(
                 tcx.hir().body(*body).value.peel_blocks().span.shrink_to_lo(),
                 "dereference the return value",
diff --git a/tests/ui/regions/account-for-lifetimes-in-closure-suggestion.rs b/tests/ui/regions/account-for-lifetimes-in-closure-suggestion.rs
new file mode 100644
index 00000000000..2de92cf62da
--- /dev/null
+++ b/tests/ui/regions/account-for-lifetimes-in-closure-suggestion.rs
@@ -0,0 +1,19 @@
+// #125634
+struct Thing;
+
+// Invariant in 'a, Covariant in 'b
+struct TwoThings<'a, 'b>(*mut &'a (), &'b mut ());
+
+impl Thing {
+    fn enter_scope<'a>(self, _scope: impl for<'b> FnOnce(TwoThings<'a, 'b>)) {}
+}
+
+fn foo() {
+    Thing.enter_scope(|ctx| {
+        SameLifetime(ctx); //~ ERROR lifetime may not live long enough
+    });
+}
+
+struct SameLifetime<'a>(TwoThings<'a, 'a>);
+
+fn main() {}
diff --git a/tests/ui/regions/account-for-lifetimes-in-closure-suggestion.stderr b/tests/ui/regions/account-for-lifetimes-in-closure-suggestion.stderr
new file mode 100644
index 00000000000..5e158f59cdc
--- /dev/null
+++ b/tests/ui/regions/account-for-lifetimes-in-closure-suggestion.stderr
@@ -0,0 +1,17 @@
+error: lifetime may not live long enough
+  --> $DIR/account-for-lifetimes-in-closure-suggestion.rs:13:22
+   |
+LL |     Thing.enter_scope(|ctx| {
+   |                        ---
+   |                        |
+   |                        has type `TwoThings<'_, '1>`
+   |                        has type `TwoThings<'2, '_>`
+LL |         SameLifetime(ctx);
+   |                      ^^^ this usage requires that `'1` must outlive `'2`
+   |
+   = note: requirement occurs because of the type `TwoThings<'_, '_>`, which makes the generic argument `'_` invariant
+   = note: the struct `TwoThings<'a, 'b>` is invariant over the parameter `'a`
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
+
+error: aborting due to 1 previous error
+
diff --git a/tests/crashes/124563.rs b/tests/ui/regions/lifetime-not-long-enough-suggestion-regression-test-124563.rs
index b082739af53..23427838ceb 100644
--- a/tests/crashes/124563.rs
+++ b/tests/ui/regions/lifetime-not-long-enough-suggestion-regression-test-124563.rs
@@ -1,5 +1,4 @@
-//@ known-bug: rust-lang/rust#124563
-
+// #124563
 use std::marker::PhantomData;
 
 pub trait Trait {}
@@ -17,11 +16,11 @@ where
     T: Trait,
 {
     type Trait = T;
-    type Bar = BarImpl<'a, 'b, T>;
+    type Bar = BarImpl<'a, 'b, T>; //~ ERROR lifetime bound not satisfied
 
     fn foo(&mut self) {
-        self.enter_scope(|ctx| {
-            BarImpl(ctx);
+        self.enter_scope(|ctx| { //~ ERROR lifetime may not live long enough
+            BarImpl(ctx); //~ ERROR lifetime may not live long enough
         });
     }
 }
@@ -44,3 +43,5 @@ where
 {
     type Foo = FooImpl<'a, 'b, T>;
 }
+
+fn main() {}
diff --git a/tests/ui/regions/lifetime-not-long-enough-suggestion-regression-test-124563.stderr b/tests/ui/regions/lifetime-not-long-enough-suggestion-regression-test-124563.stderr
new file mode 100644
index 00000000000..fcd0a232a7b
--- /dev/null
+++ b/tests/ui/regions/lifetime-not-long-enough-suggestion-regression-test-124563.stderr
@@ -0,0 +1,49 @@
+error[E0478]: lifetime bound not satisfied
+  --> $DIR/lifetime-not-long-enough-suggestion-regression-test-124563.rs:19:16
+   |
+LL |     type Bar = BarImpl<'a, 'b, T>;
+   |                ^^^^^^^^^^^^^^^^^^
+   |
+note: lifetime parameter instantiated with the lifetime `'a` as defined here
+  --> $DIR/lifetime-not-long-enough-suggestion-regression-test-124563.rs:14:6
+   |
+LL | impl<'a, 'b, T> Foo for FooImpl<'a, 'b, T>
+   |      ^^
+note: but lifetime parameter must outlive the lifetime `'b` as defined here
+  --> $DIR/lifetime-not-long-enough-suggestion-regression-test-124563.rs:14:10
+   |
+LL | impl<'a, 'b, T> Foo for FooImpl<'a, 'b, T>
+   |          ^^
+
+error: lifetime may not live long enough
+  --> $DIR/lifetime-not-long-enough-suggestion-regression-test-124563.rs:23:21
+   |
+LL |         self.enter_scope(|ctx| {
+   |                           ---
+   |                           |
+   |                           has type `&'1 mut FooImpl<'_, '_, T>`
+   |                           has type `&mut FooImpl<'2, '_, T>`
+LL |             BarImpl(ctx);
+   |                     ^^^ this usage requires that `'1` must outlive `'2`
+
+error: lifetime may not live long enough
+  --> $DIR/lifetime-not-long-enough-suggestion-regression-test-124563.rs:22:9
+   |
+LL |   impl<'a, 'b, T> Foo for FooImpl<'a, 'b, T>
+   |        --  -- lifetime `'b` defined here
+   |        |
+   |        lifetime `'a` defined here
+...
+LL | /         self.enter_scope(|ctx| {
+LL | |             BarImpl(ctx);
+LL | |         });
+   | |__________^ argument requires that `'a` must outlive `'b`
+   |
+   = help: consider adding the following bound: `'a: 'b`
+   = note: requirement occurs because of a mutable reference to `FooImpl<'_, '_, T>`
+   = note: mutable references are invariant over their type parameter
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0478`.
diff --git a/tests/ui/regions/regions-escape-method.fixed b/tests/ui/regions/regions-escape-method.fixed
new file mode 100644
index 00000000000..f192dca1e25
--- /dev/null
+++ b/tests/ui/regions/regions-escape-method.fixed
@@ -0,0 +1,17 @@
+// Test a method call where the parameter `B` would (illegally) be
+// inferred to a region bound in the method argument. If this program
+// were accepted, then the closure passed to `s.f` could escape its
+// argument.
+//@ run-rustfix
+
+struct S;
+
+impl S {
+    fn f<B, F>(&self, _: F) where F: FnOnce(&i32) -> B {
+    }
+}
+
+fn main() {
+    let s = S;
+    s.f(|p| *p) //~ ERROR lifetime may not live long enough
+}
diff --git a/tests/ui/regions/regions-escape-method.rs b/tests/ui/regions/regions-escape-method.rs
index 69c01ae6906..82bf86c79b2 100644
--- a/tests/ui/regions/regions-escape-method.rs
+++ b/tests/ui/regions/regions-escape-method.rs
@@ -2,6 +2,7 @@
 // inferred to a region bound in the method argument. If this program
 // were accepted, then the closure passed to `s.f` could escape its
 // argument.
+//@ run-rustfix
 
 struct S;
 
diff --git a/tests/ui/regions/regions-escape-method.stderr b/tests/ui/regions/regions-escape-method.stderr
index aeda923b0ba..687b91bb7b4 100644
--- a/tests/ui/regions/regions-escape-method.stderr
+++ b/tests/ui/regions/regions-escape-method.stderr
@@ -1,11 +1,16 @@
 error: lifetime may not live long enough
-  --> $DIR/regions-escape-method.rs:15:13
+  --> $DIR/regions-escape-method.rs:16:13
    |
 LL |     s.f(|p| p)
    |          -- ^ returning this value requires that `'1` must outlive `'2`
    |          ||
    |          |return type of closure is &'2 i32
    |          has type `&'1 i32`
+   |
+help: dereference the return value
+   |
+LL |     s.f(|p| *p)
+   |             +
 
 error: aborting due to 1 previous error