about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSantiago Pastorino <spastorino@gmail.com>2024-12-17 12:27:02 -0300
committerSantiago Pastorino <spastorino@gmail.com>2025-03-06 17:58:32 -0300
commit57cb498989ac29fbe9938ed2f4a31a3eab6850dc (patch)
tree460d39732c8b2b82b49168df3c04dacb76c7bc2f
parent81a926cc2ae22bed26c57a7ef481099916fc1cc8 (diff)
downloadrust-57cb498989ac29fbe9938ed2f4a31a3eab6850dc.tar.gz
rust-57cb498989ac29fbe9938ed2f4a31a3eab6850dc.zip
Generate the right MIR for by use closures
-rw-r--r--compiler/rustc_hir_typeck/src/expr_use_visitor.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/upvar.rs38
-rw-r--r--compiler/rustc_middle/src/ty/closure.rs7
-rw-r--r--compiler/rustc_mir_build/src/builder/mod.rs4
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs13
-rw-r--r--compiler/rustc_mir_transform/src/coroutine/by_move_body.rs2
-rw-r--r--compiler/rustc_passes/src/liveness.rs4
-rw-r--r--tests/ui/ergonomic-clones/closure.rs13
-rw-r--r--tests/ui/feature-gates/feature-gate-ergonomic-clones.rs1
-rw-r--r--tests/ui/feature-gates/feature-gate-ergonomic-clones.stderr28
10 files changed, 65 insertions, 47 deletions
diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
index bd9173e6163..7e037d56ffe 100644
--- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
+++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
@@ -1086,7 +1086,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
                     );
 
                     match capture_info.capture_kind {
-                        ty::UpvarCapture::ByValue => {
+                        ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => {
                             self.consume_or_copy(&place_with_id, place_with_id.hir_id);
                         }
                         ty::UpvarCapture::ByRef(upvar_borrow) => {
diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs
index 4c2a0e1a26b..3c5fcdd005e 100644
--- a/compiler/rustc_hir_typeck/src/upvar.rs
+++ b/compiler/rustc_hir_typeck/src/upvar.rs
@@ -670,9 +670,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 origin = updated.1;
 
                 let (place, capture_kind) = match capture_clause {
-                    hir::CaptureBy::Value { .. } | hir::CaptureBy::Use { .. } => {
-                        adjust_for_move_closure(place, capture_kind)
-                    }
+                    hir::CaptureBy::Value { .. } => adjust_for_move_closure(place, capture_kind),
+                    hir::CaptureBy::Use { .. } => adjust_for_use_closure(place, capture_kind),
                     hir::CaptureBy::Ref => adjust_for_non_move_closure(place, capture_kind),
                 };
 
@@ -1307,7 +1306,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         for captured_place in root_var_min_capture_list.iter() {
             match captured_place.info.capture_kind {
                 // Only care about captures that are moved into the closure
-                ty::UpvarCapture::ByValue => {
+                ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => {
                     projections_list.push(captured_place.place.projections.as_slice());
                     diagnostics_info.insert(UpvarMigrationInfo::CapturingPrecise {
                         source_expr: captured_place.info.path_expr_id,
@@ -1931,7 +1930,7 @@ fn apply_capture_kind_on_capture_ty<'tcx>(
     region: ty::Region<'tcx>,
 ) -> Ty<'tcx> {
     match capture_kind {
-        ty::UpvarCapture::ByValue => ty,
+        ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => ty,
         ty::UpvarCapture::ByRef(kind) => Ty::new_ref(tcx, region, ty, kind.to_mutbl_lossy()),
     }
 }
@@ -2168,6 +2167,20 @@ fn adjust_for_move_closure(
     (place, ty::UpvarCapture::ByValue)
 }
 
+/// Truncate deref of any reference.
+fn adjust_for_use_closure(
+    mut place: Place<'_>,
+    mut kind: ty::UpvarCapture,
+) -> (Place<'_>, ty::UpvarCapture) {
+    let first_deref = place.projections.iter().position(|proj| proj.kind == ProjectionKind::Deref);
+
+    if let Some(idx) = first_deref {
+        truncate_place_to_len_and_update_capture_kind(&mut place, &mut kind, idx);
+    }
+
+    (place, ty::UpvarCapture::ByUse)
+}
+
 /// Adjust closure capture just that if taking ownership of data, only move data
 /// from enclosing stack frame.
 fn adjust_for_non_move_closure(
@@ -2178,7 +2191,7 @@ fn adjust_for_non_move_closure(
         place.projections.iter().position(|proj| proj.kind == ProjectionKind::Deref);
 
     match kind {
-        ty::UpvarCapture::ByValue => {
+        ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => {
             if let Some(idx) = contains_deref {
                 truncate_place_to_len_and_update_capture_kind(&mut place, &mut kind, idx);
             }
@@ -2223,6 +2236,7 @@ fn construct_capture_kind_reason_string<'tcx>(
 
     let capture_kind_str = match capture_info.capture_kind {
         ty::UpvarCapture::ByValue => "ByValue".into(),
+        ty::UpvarCapture::ByUse => "ByUse".into(),
         ty::UpvarCapture::ByRef(kind) => format!("{kind:?}"),
     };
 
@@ -2244,6 +2258,7 @@ fn construct_capture_info_string<'tcx>(
 
     let capture_kind_str = match capture_info.capture_kind {
         ty::UpvarCapture::ByValue => "ByValue".into(),
+        ty::UpvarCapture::ByUse => "ByUse".into(),
         ty::UpvarCapture::ByRef(kind) => format!("{kind:?}"),
     };
     format!("{place_str} -> {capture_kind_str}")
@@ -2339,8 +2354,11 @@ fn determine_capture_info(
     // expressions.
     let eq_capture_kind = match (capture_info_a.capture_kind, capture_info_b.capture_kind) {
         (ty::UpvarCapture::ByValue, ty::UpvarCapture::ByValue) => true,
+        (ty::UpvarCapture::ByUse, ty::UpvarCapture::ByUse) => true,
         (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => ref_a == ref_b,
-        (ty::UpvarCapture::ByValue, _) | (ty::UpvarCapture::ByRef(_), _) => false,
+        (ty::UpvarCapture::ByValue, _)
+        | (ty::UpvarCapture::ByUse, _)
+        | (ty::UpvarCapture::ByRef(_), _) => false,
     };
 
     if eq_capture_kind {
@@ -2350,8 +2368,10 @@ fn determine_capture_info(
         }
     } else {
         // We select the CaptureKind which ranks higher based the following priority order:
-        // ByValue > MutBorrow > UniqueImmBorrow > ImmBorrow
+        // (ByUse | ByValue) > MutBorrow > UniqueImmBorrow > ImmBorrow
         match (capture_info_a.capture_kind, capture_info_b.capture_kind) {
+            (ty::UpvarCapture::ByUse, _) => capture_info_a,
+            (_, ty::UpvarCapture::ByUse) => capture_info_b,
             (ty::UpvarCapture::ByValue, _) => capture_info_a,
             (_, ty::UpvarCapture::ByValue) => capture_info_b,
             (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => {
@@ -2405,7 +2425,7 @@ fn truncate_place_to_len_and_update_capture_kind<'tcx>(
         }
 
         ty::UpvarCapture::ByRef(..) => {}
-        ty::UpvarCapture::ByValue => {}
+        ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => {}
     }
 
     place.projections.truncate(len);
diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs
index 3605f2402e7..5d9b1ddfa38 100644
--- a/compiler/rustc_middle/src/ty/closure.rs
+++ b/compiler/rustc_middle/src/ty/closure.rs
@@ -51,6 +51,9 @@ pub enum UpvarCapture {
     /// depending on inference.
     ByValue,
 
+    /// Upvar is captured by use. This is true when the closure is labeled `use`.
+    ByUse,
+
     /// Upvar is captured by reference.
     ByRef(BorrowKind),
 }
@@ -178,7 +181,7 @@ impl<'tcx> CapturedPlace<'tcx> {
 
     pub fn is_by_ref(&self) -> bool {
         match self.info.capture_kind {
-            ty::UpvarCapture::ByValue => false,
+            ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => false,
             ty::UpvarCapture::ByRef(..) => true,
         }
     }
@@ -214,7 +217,7 @@ impl<'tcx> TyCtxt<'tcx> {
     pub fn closure_captures(self, def_id: LocalDefId) -> &'tcx [&'tcx ty::CapturedPlace<'tcx>] {
         if !self.is_closure_like(def_id.to_def_id()) {
             return &[];
-        };
+        }
         self.closure_typeinfo(def_id).captures
     }
 }
diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs
index 94955954934..ff2b59ee07d 100644
--- a/compiler/rustc_mir_build/src/builder/mod.rs
+++ b/compiler/rustc_mir_build/src/builder/mod.rs
@@ -37,7 +37,7 @@ pub(crate) fn closure_saved_names_of_captured_variables<'tcx>(
         .map(|captured_place| {
             let name = captured_place.to_symbol();
             match captured_place.info.capture_kind {
-                ty::UpvarCapture::ByValue => name,
+                ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => name,
                 ty::UpvarCapture::ByRef(..) => Symbol::intern(&format!("_ref__{name}")),
             }
         })
@@ -871,7 +871,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 let mut projs = closure_env_projs.clone();
                 projs.push(ProjectionElem::Field(FieldIdx::new(i), ty));
                 match capture {
-                    ty::UpvarCapture::ByValue => {}
+                    ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => {}
                     ty::UpvarCapture::ByRef(..) => {
                         projs.push(ProjectionElem::Deref);
                     }
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index 40e84be72b1..e46e8c9871a 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -652,7 +652,7 @@ impl<'tcx> ThirBuildCx<'tcx> {
                 }
             },
 
-            hir::ExprKind::Closure { .. } => {
+            hir::ExprKind::Closure(hir::Closure { .. }) => {
                 let closure_ty = self.typeck_results.expr_ty(expr);
                 let (def_id, args, movability) = match *closure_ty.kind() {
                     ty::Closure(def_id, args) => (def_id, UpvarArgs::Closure(args), None),
@@ -1252,6 +1252,17 @@ impl<'tcx> ThirBuildCx<'tcx> {
 
         match upvar_capture {
             ty::UpvarCapture::ByValue => captured_place_expr,
+            ty::UpvarCapture::ByUse => {
+                let span = captured_place_expr.span;
+                let expr_id = self.thir.exprs.push(captured_place_expr);
+
+                Expr {
+                    temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
+                    ty: upvar_ty,
+                    span: closure_expr.span,
+                    kind: ExprKind::ByUse { expr: expr_id, span },
+                }
+            }
             ty::UpvarCapture::ByRef(upvar_borrow) => {
                 let borrow_kind = match upvar_borrow {
                     ty::BorrowKind::Immutable => BorrowKind::Shared,
diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs
index 0f5fcb0d8eb..c79ea14d6f6 100644
--- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs
+++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs
@@ -170,7 +170,7 @@ pub(crate) fn coroutine_by_move_body_def_id<'tcx>(
             // this when building the field projection in the MIR body later on.
             let mut parent_capture_ty = parent_capture.place.ty();
             parent_capture_ty = match parent_capture.info.capture_kind {
-                ty::UpvarCapture::ByValue => parent_capture_ty,
+                ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => parent_capture_ty,
                 ty::UpvarCapture::ByRef(kind) => Ty::new_ref(
                     tcx,
                     tcx.lifetimes.re_erased,
diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs
index 84fb169c5f7..822804893fe 100644
--- a/compiler/rustc_passes/src/liveness.rs
+++ b/compiler/rustc_passes/src/liveness.rs
@@ -706,7 +706,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
                             );
                             self.acc(self.exit_ln, var, ACC_READ | ACC_USE);
                         }
-                        ty::UpvarCapture::ByValue => {}
+                        ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => {}
                     }
                 }
             }
@@ -1500,7 +1500,7 @@ impl<'tcx> Liveness<'_, 'tcx> {
         for (&var_hir_id, min_capture_list) in closure_min_captures {
             for captured_place in min_capture_list {
                 match captured_place.info.capture_kind {
-                    ty::UpvarCapture::ByValue => {}
+                    ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => {}
                     ty::UpvarCapture::ByRef(..) => continue,
                 };
                 let span = captured_place.get_capture_kind_span(self.ir.tcx);
diff --git a/tests/ui/ergonomic-clones/closure.rs b/tests/ui/ergonomic-clones/closure.rs
index ea5c06457f5..340b9ece50e 100644
--- a/tests/ui/ergonomic-clones/closure.rs
+++ b/tests/ui/ergonomic-clones/closure.rs
@@ -3,18 +3,27 @@
 
 #![feature(ergonomic_clones)]
 
-fn ergonomic_clone_closure() -> i32 {
+fn ergonomic_clone_closure_no_captures() -> i32 {
     let cl = use || {
         1
     };
     cl()
 }
 
+fn ergonomic_clone_closure_with_captures() -> String {
+    let s = String::from("hi");
+
+    let cl = use || {
+        s
+    };
+    cl()
+}
+
 fn ergonomic_clone_async_closures() -> String {
     let s = String::from("hi");
 
     async use {
-        22
+        s
     };
 
     s
diff --git a/tests/ui/feature-gates/feature-gate-ergonomic-clones.rs b/tests/ui/feature-gates/feature-gate-ergonomic-clones.rs
index f00f15174b2..0ffc3925005 100644
--- a/tests/ui/feature-gates/feature-gate-ergonomic-clones.rs
+++ b/tests/ui/feature-gates/feature-gate-ergonomic-clones.rs
@@ -13,7 +13,6 @@ fn ergonomic_closure_clone() {
 
     let s3 = use || {
         //~^ ERROR `.use` calls are experimental [E0658]
-        //~| ERROR use of moved value: `s1` [E0382]
         s1
     };
 }
diff --git a/tests/ui/feature-gates/feature-gate-ergonomic-clones.stderr b/tests/ui/feature-gates/feature-gate-ergonomic-clones.stderr
index c83b4400d3c..366151c91a6 100644
--- a/tests/ui/feature-gates/feature-gate-ergonomic-clones.stderr
+++ b/tests/ui/feature-gates/feature-gate-ergonomic-clones.stderr
@@ -28,30 +28,6 @@ LL |     let s3 = use || {
    = help: add `#![feature(ergonomic_clones)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error[E0382]: use of moved value: `s1`
-  --> $DIR/feature-gate-ergonomic-clones.rs:14:14
-   |
-LL |     let s1 = String::from("hi!");
-   |         -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait
-LL |
-LL |     let s2 = use || {
-   |              ------ value moved into closure here
-LL |
-LL |         s1
-   |         -- variable moved due to use in closure
-...
-LL |     let s3 = use || {
-   |              ^^^^^^ value used here after move
-...
-LL |         s1
-   |         -- use occurs due to use in closure
-   |
-help: consider cloning the value if the performance cost is acceptable
-   |
-LL |         s1.clone()
-   |           ++++++++
-
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
 
-Some errors have detailed explanations: E0382, E0658.
-For more information about an error, try `rustc --explain E0382`.
+For more information about this error, try `rustc --explain E0658`.