about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorManish Goregaokar <manishsmail@gmail.com>2020-06-25 18:00:10 -0700
committerGitHub <noreply@github.com>2020-06-25 18:00:10 -0700
commit4a245aeec5e7ef71002f91ef9e00eb24646c6ea4 (patch)
tree841b3c1ce244c56121be9e51fc23fd286d3f450b /src
parent3f5b8c800e546809b374aecad992689712ab00d6 (diff)
parent520461f1fb2730f8edb17922f3bcc74fccdc52d3 (diff)
downloadrust-4a245aeec5e7ef71002f91ef9e00eb24646c6ea4.tar.gz
rust-4a245aeec5e7ef71002f91ef9e00eb24646c6ea4.zip
Rollup merge of #73534 - estebank:borrowck-suggestions, r=matthewjasper
Provide suggestions for some moved value errors

When encountering an used moved value where the previous move happened
in a `match` or `if let` pattern, suggest using `ref`. Fix #63988.

When encountering a `&mut` value that is used in multiple iterations of
a loop, suggest reborrowing it with `&mut *`. Fix #62112.
Diffstat (limited to 'src')
-rw-r--r--src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs39
-rw-r--r--src/librustc_mir/borrow_check/diagnostics/explain_borrow.rs2
-rw-r--r--src/librustc_mir/borrow_check/diagnostics/mod.rs20
-rw-r--r--src/test/ui/borrowck/issue-41962.stderr4
-rw-r--r--src/test/ui/borrowck/move-in-pattern-mut.rs23
-rw-r--r--src/test/ui/borrowck/move-in-pattern-mut.stderr33
-rw-r--r--src/test/ui/borrowck/move-in-pattern.fixed24
-rw-r--r--src/test/ui/borrowck/move-in-pattern.rs24
-rw-r--r--src/test/ui/borrowck/move-in-pattern.stderr33
-rw-r--r--src/test/ui/borrowck/mut-borrow-in-loop-2.fixed35
-rw-r--r--src/test/ui/borrowck/mut-borrow-in-loop-2.rs35
-rw-r--r--src/test/ui/borrowck/mut-borrow-in-loop-2.stderr17
-rw-r--r--src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.stderr4
-rw-r--r--src/test/ui/nll/issue-53807.stderr4
-rw-r--r--src/test/ui/pattern/bindings-after-at/bind-by-move-neither-can-live-while-the-other-survives-1.stderr8
-rw-r--r--src/test/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse.stderr16
-rw-r--r--src/test/ui/ref-suggestion.stderr4
17 files changed, 315 insertions, 10 deletions
diff --git a/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs b/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs
index 8d7944004c7..60a1fe0b198 100644
--- a/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs
+++ b/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs
@@ -156,6 +156,20 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         format!("variable moved due to use{}", move_spans.describe()),
                     );
                 }
+                if let UseSpans::PatUse(span) = move_spans {
+                    err.span_suggestion_verbose(
+                        span.shrink_to_lo(),
+                        &format!(
+                            "borrow this field in the pattern to avoid moving {}",
+                            self.describe_place(moved_place.as_ref())
+                                .map(|n| format!("`{}`", n))
+                                .unwrap_or_else(|| "the value".to_string())
+                        ),
+                        "ref ".to_string(),
+                        Applicability::MachineApplicable,
+                    );
+                }
+
                 if Some(DesugaringKind::ForLoop) == move_span.desugaring_kind() {
                     let sess = self.infcx.tcx.sess;
                     if let Ok(snippet) = sess.source_map().span_to_snippet(move_span) {
@@ -198,11 +212,28 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 _ => true,
             };
 
-            if needs_note {
-                let mpi = self.move_data.moves[move_out_indices[0]].path;
-                let place = &self.move_data.move_paths[mpi].place;
+            let mpi = self.move_data.moves[move_out_indices[0]].path;
+            let place = &self.move_data.move_paths[mpi].place;
+            let ty = place.ty(self.body, self.infcx.tcx).ty;
+
+            if is_loop_move {
+                if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind {
+                    // We have a `&mut` ref, we need to reborrow on each iteration (#62112).
+                    err.span_suggestion_verbose(
+                        span.shrink_to_lo(),
+                        &format!(
+                            "consider creating a fresh reborrow of {} here",
+                            self.describe_place(moved_place)
+                                .map(|n| format!("`{}`", n))
+                                .unwrap_or_else(|| "the mutable reference".to_string()),
+                        ),
+                        "&mut *".to_string(),
+                        Applicability::MachineApplicable,
+                    );
+                }
+            }
 
-                let ty = place.ty(self.body, self.infcx.tcx).ty;
+            if needs_note {
                 let opt_name =
                     self.describe_place_with_options(place.as_ref(), IncludingDowncast(true));
                 let note_msg = match opt_name {
diff --git a/src/librustc_mir/borrow_check/diagnostics/explain_borrow.rs b/src/librustc_mir/borrow_check/diagnostics/explain_borrow.rs
index 5253acbba7f..849fd63998d 100644
--- a/src/librustc_mir/borrow_check/diagnostics/explain_borrow.rs
+++ b/src/librustc_mir/borrow_check/diagnostics/explain_borrow.rs
@@ -509,7 +509,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 // Used in a closure.
                 (LaterUseKind::ClosureCapture, var_span)
             }
-            UseSpans::OtherUse(span) => {
+            UseSpans::PatUse(span) | UseSpans::OtherUse(span) => {
                 let block = &self.body.basic_blocks()[location.block];
 
                 let kind = if let Some(&Statement {
diff --git a/src/librustc_mir/borrow_check/diagnostics/mod.rs b/src/librustc_mir/borrow_check/diagnostics/mod.rs
index ca8e54ea286..388076a9d60 100644
--- a/src/librustc_mir/borrow_check/diagnostics/mod.rs
+++ b/src/librustc_mir/borrow_check/diagnostics/mod.rs
@@ -542,20 +542,26 @@ pub(super) enum UseSpans {
         // The span of the first use of the captured variable inside the closure.
         var_span: Span,
     },
-    // This access has a single span associated to it: common case.
+    /// This access is caused by a `match` or `if let` pattern.
+    PatUse(Span),
+    /// This access has a single span associated to it: common case.
     OtherUse(Span),
 }
 
 impl UseSpans {
     pub(super) fn args_or_use(self) -> Span {
         match self {
-            UseSpans::ClosureUse { args_span: span, .. } | UseSpans::OtherUse(span) => span,
+            UseSpans::ClosureUse { args_span: span, .. }
+            | UseSpans::PatUse(span)
+            | UseSpans::OtherUse(span) => span,
         }
     }
 
     pub(super) fn var_or_use(self) -> Span {
         match self {
-            UseSpans::ClosureUse { var_span: span, .. } | UseSpans::OtherUse(span) => span,
+            UseSpans::ClosureUse { var_span: span, .. }
+            | UseSpans::PatUse(span)
+            | UseSpans::OtherUse(span) => span,
         }
     }
 
@@ -624,7 +630,7 @@ impl UseSpans {
     {
         match self {
             closure @ UseSpans::ClosureUse { .. } => closure,
-            UseSpans::OtherUse(_) => if_other(),
+            UseSpans::PatUse(_) | UseSpans::OtherUse(_) => if_other(),
         }
     }
 }
@@ -741,7 +747,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             }
         }
 
-        OtherUse(stmt.source_info.span)
+        if moved_place.projection.iter().any(|p| matches!(p, ProjectionElem::Downcast(..))) {
+            PatUse(stmt.source_info.span)
+        } else {
+            OtherUse(stmt.source_info.span)
+        }
     }
 
     /// Finds the span of arguments of a closure (within `maybe_closure_span`)
diff --git a/src/test/ui/borrowck/issue-41962.stderr b/src/test/ui/borrowck/issue-41962.stderr
index 422d1605aa4..604143b4e7e 100644
--- a/src/test/ui/borrowck/issue-41962.stderr
+++ b/src/test/ui/borrowck/issue-41962.stderr
@@ -5,6 +5,10 @@ LL |         if let Some(thing) = maybe {
    |                     ^^^^^ value moved here, in previous iteration of loop
    |
    = note: move occurs because value has type `std::vec::Vec<bool>`, which does not implement the `Copy` trait
+help: borrow this field in the pattern to avoid moving `maybe.0`
+   |
+LL |         if let Some(ref thing) = maybe {
+   |                     ^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/borrowck/move-in-pattern-mut.rs b/src/test/ui/borrowck/move-in-pattern-mut.rs
new file mode 100644
index 00000000000..175eb3b7a04
--- /dev/null
+++ b/src/test/ui/borrowck/move-in-pattern-mut.rs
@@ -0,0 +1,23 @@
+// Issue #63988
+#[derive(Debug)]
+struct S;
+fn foo(_: Option<S>) {}
+
+enum E {
+    V {
+        s: S,
+    }
+}
+fn bar(_: E) {}
+
+fn main() {
+    let s = Some(S);
+    if let Some(mut x) = s {
+        x = S;
+    }
+    foo(s); //~ ERROR use of moved value: `s`
+    let mut e = E::V { s: S };
+    let E::V { s: mut x } = e;
+    x = S;
+    bar(e); //~ ERROR use of moved value: `e`
+}
diff --git a/src/test/ui/borrowck/move-in-pattern-mut.stderr b/src/test/ui/borrowck/move-in-pattern-mut.stderr
new file mode 100644
index 00000000000..391638444c3
--- /dev/null
+++ b/src/test/ui/borrowck/move-in-pattern-mut.stderr
@@ -0,0 +1,33 @@
+error[E0382]: use of moved value: `s`
+  --> $DIR/move-in-pattern-mut.rs:18:9
+   |
+LL |     if let Some(mut x) = s {
+   |                 ----- value moved here
+...
+LL |     foo(s);
+   |         ^ value used here after partial move
+   |
+   = note: move occurs because value has type `S`, which does not implement the `Copy` trait
+help: borrow this field in the pattern to avoid moving `s.0`
+   |
+LL |     if let Some(ref mut x) = s {
+   |                 ^^^
+
+error[E0382]: use of moved value: `e`
+  --> $DIR/move-in-pattern-mut.rs:22:9
+   |
+LL |     let E::V { s: mut x } = e;
+   |                   ----- value moved here
+LL |     x = S;
+LL |     bar(e);
+   |         ^ value used here after partial move
+   |
+   = note: move occurs because value has type `S`, which does not implement the `Copy` trait
+help: borrow this field in the pattern to avoid moving `e.s`
+   |
+LL |     let E::V { s: ref mut x } = e;
+   |                   ^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/borrowck/move-in-pattern.fixed b/src/test/ui/borrowck/move-in-pattern.fixed
new file mode 100644
index 00000000000..f55fdcc5f90
--- /dev/null
+++ b/src/test/ui/borrowck/move-in-pattern.fixed
@@ -0,0 +1,24 @@
+// run-rustfix
+// Issue #63988
+#[derive(Debug)]
+struct S;
+fn foo(_: Option<S>) {}
+
+enum E {
+    V {
+        s: S,
+    }
+}
+fn bar(_: E) {}
+
+fn main() {
+    let s = Some(S);
+    if let Some(ref x) = s {
+        let _ = x;
+    }
+    foo(s); //~ ERROR use of moved value: `s`
+    let e = E::V { s: S };
+    let E::V { s: ref x } = e;
+    let _ = x;
+    bar(e); //~ ERROR use of moved value: `e`
+}
diff --git a/src/test/ui/borrowck/move-in-pattern.rs b/src/test/ui/borrowck/move-in-pattern.rs
new file mode 100644
index 00000000000..7ad04b9490c
--- /dev/null
+++ b/src/test/ui/borrowck/move-in-pattern.rs
@@ -0,0 +1,24 @@
+// run-rustfix
+// Issue #63988
+#[derive(Debug)]
+struct S;
+fn foo(_: Option<S>) {}
+
+enum E {
+    V {
+        s: S,
+    }
+}
+fn bar(_: E) {}
+
+fn main() {
+    let s = Some(S);
+    if let Some(x) = s {
+        let _ = x;
+    }
+    foo(s); //~ ERROR use of moved value: `s`
+    let e = E::V { s: S };
+    let E::V { s: x } = e;
+    let _ = x;
+    bar(e); //~ ERROR use of moved value: `e`
+}
diff --git a/src/test/ui/borrowck/move-in-pattern.stderr b/src/test/ui/borrowck/move-in-pattern.stderr
new file mode 100644
index 00000000000..c5cb24455eb
--- /dev/null
+++ b/src/test/ui/borrowck/move-in-pattern.stderr
@@ -0,0 +1,33 @@
+error[E0382]: use of moved value: `s`
+  --> $DIR/move-in-pattern.rs:19:9
+   |
+LL |     if let Some(x) = s {
+   |                 - value moved here
+...
+LL |     foo(s);
+   |         ^ value used here after partial move
+   |
+   = note: move occurs because value has type `S`, which does not implement the `Copy` trait
+help: borrow this field in the pattern to avoid moving `s.0`
+   |
+LL |     if let Some(ref x) = s {
+   |                 ^^^
+
+error[E0382]: use of moved value: `e`
+  --> $DIR/move-in-pattern.rs:23:9
+   |
+LL |     let E::V { s: x } = e;
+   |                   - value moved here
+LL |     let _ = x;
+LL |     bar(e);
+   |         ^ value used here after partial move
+   |
+   = note: move occurs because value has type `S`, which does not implement the `Copy` trait
+help: borrow this field in the pattern to avoid moving `e.s`
+   |
+LL |     let E::V { s: ref x } = e;
+   |                   ^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/borrowck/mut-borrow-in-loop-2.fixed b/src/test/ui/borrowck/mut-borrow-in-loop-2.fixed
new file mode 100644
index 00000000000..ceeba30a90f
--- /dev/null
+++ b/src/test/ui/borrowck/mut-borrow-in-loop-2.fixed
@@ -0,0 +1,35 @@
+// run-rustfix
+#![allow(dead_code)]
+
+struct Events<R>(R);
+
+struct Other;
+
+pub trait Trait<T> {
+    fn handle(value: T) -> Self;
+}
+
+// Blanket impl. (If you comment this out, compiler figures out that it
+// is passing an `&mut` to a method that must be expecting an `&mut`,
+// and injects an auto-reborrow.)
+impl<T, U> Trait<U> for T where T: From<U> {
+    fn handle(_: U) -> Self { unimplemented!() }
+}
+
+impl<'a, R> Trait<&'a mut Events<R>> for Other {
+    fn handle(_: &'a mut Events<R>) -> Self { unimplemented!() }
+}
+
+fn this_compiles<'a, R>(value: &'a mut Events<R>) {
+    for _ in 0..3 {
+        Other::handle(&mut *value);
+    }
+}
+
+fn this_does_not<'a, R>(value: &'a mut Events<R>) {
+    for _ in 0..3 {
+        Other::handle(&mut *value); //~ ERROR use of moved value: `value`
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/borrowck/mut-borrow-in-loop-2.rs b/src/test/ui/borrowck/mut-borrow-in-loop-2.rs
new file mode 100644
index 00000000000..d13fb7e5679
--- /dev/null
+++ b/src/test/ui/borrowck/mut-borrow-in-loop-2.rs
@@ -0,0 +1,35 @@
+// run-rustfix
+#![allow(dead_code)]
+
+struct Events<R>(R);
+
+struct Other;
+
+pub trait Trait<T> {
+    fn handle(value: T) -> Self;
+}
+
+// Blanket impl. (If you comment this out, compiler figures out that it
+// is passing an `&mut` to a method that must be expecting an `&mut`,
+// and injects an auto-reborrow.)
+impl<T, U> Trait<U> for T where T: From<U> {
+    fn handle(_: U) -> Self { unimplemented!() }
+}
+
+impl<'a, R> Trait<&'a mut Events<R>> for Other {
+    fn handle(_: &'a mut Events<R>) -> Self { unimplemented!() }
+}
+
+fn this_compiles<'a, R>(value: &'a mut Events<R>) {
+    for _ in 0..3 {
+        Other::handle(&mut *value);
+    }
+}
+
+fn this_does_not<'a, R>(value: &'a mut Events<R>) {
+    for _ in 0..3 {
+        Other::handle(value); //~ ERROR use of moved value: `value`
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/borrowck/mut-borrow-in-loop-2.stderr b/src/test/ui/borrowck/mut-borrow-in-loop-2.stderr
new file mode 100644
index 00000000000..fa1b741394a
--- /dev/null
+++ b/src/test/ui/borrowck/mut-borrow-in-loop-2.stderr
@@ -0,0 +1,17 @@
+error[E0382]: use of moved value: `value`
+  --> $DIR/mut-borrow-in-loop-2.rs:31:23
+   |
+LL | fn this_does_not<'a, R>(value: &'a mut Events<R>) {
+   |                         ----- move occurs because `value` has type `&mut Events<R>`, which does not implement the `Copy` trait
+LL |     for _ in 0..3 {
+LL |         Other::handle(value);
+   |                       ^^^^^ value moved here, in previous iteration of loop
+   |
+help: consider creating a fresh reborrow of `value` here
+   |
+LL |         Other::handle(&mut *value);
+   |                       ^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.stderr b/src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.stderr
index fb8562d00ea..952985fcdde 100644
--- a/src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.stderr
+++ b/src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.stderr
@@ -8,6 +8,10 @@ LL |     consume(node) + r
    |             ^^^^ value used here after partial move
    |
    = note: move occurs because value has type `std::boxed::Box<List>`, which does not implement the `Copy` trait
+help: borrow this field in the pattern to avoid moving `node.next.0`
+   |
+LL |         Some(ref right) => consume(right),
+   |              ^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/nll/issue-53807.stderr b/src/test/ui/nll/issue-53807.stderr
index 2b15da3710e..4f36a4ccab2 100644
--- a/src/test/ui/nll/issue-53807.stderr
+++ b/src/test/ui/nll/issue-53807.stderr
@@ -5,6 +5,10 @@ LL |         if let Some(thing) = maybe {
    |                     ^^^^^ value moved here, in previous iteration of loop
    |
    = note: move occurs because value has type `std::vec::Vec<bool>`, which does not implement the `Copy` trait
+help: borrow this field in the pattern to avoid moving `maybe.0`
+   |
+LL |         if let Some(ref thing) = maybe {
+   |                     ^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/pattern/bindings-after-at/bind-by-move-neither-can-live-while-the-other-survives-1.stderr b/src/test/ui/pattern/bindings-after-at/bind-by-move-neither-can-live-while-the-other-survives-1.stderr
index f2186b9298e..8a6ea8e91a2 100644
--- a/src/test/ui/pattern/bindings-after-at/bind-by-move-neither-can-live-while-the-other-survives-1.stderr
+++ b/src/test/ui/pattern/bindings-after-at/bind-by-move-neither-can-live-while-the-other-survives-1.stderr
@@ -46,6 +46,10 @@ LL |         Some(_z @ ref _y) => {}
    |              value moved here
    |
    = note: move occurs because value has type `X`, which does not implement the `Copy` trait
+help: borrow this field in the pattern to avoid moving `x.0`
+   |
+LL |         Some(ref _z @ ref _y) => {}
+   |              ^^^
 
 error[E0382]: borrow of moved value
   --> $DIR/bind-by-move-neither-can-live-while-the-other-survives-1.rs:35:19
@@ -57,6 +61,10 @@ LL |         Some(_z @ ref mut _y) => {}
    |              value moved here
    |
    = note: move occurs because value has type `X`, which does not implement the `Copy` trait
+help: borrow this field in the pattern to avoid moving `x.0`
+   |
+LL |         Some(ref _z @ ref mut _y) => {}
+   |              ^^^
 
 error: aborting due to 6 previous errors
 
diff --git a/src/test/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse.stderr b/src/test/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse.stderr
index f819e671436..5058998f2a7 100644
--- a/src/test/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse.stderr
+++ b/src/test/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse.stderr
@@ -357,6 +357,10 @@ LL |         a @ Some((mut b @ ref mut c, d @ ref e)) => {}
    |                   value moved here
    |
    = note: move occurs because value has type `main::U`, which does not implement the `Copy` trait
+help: borrow this field in the pattern to avoid moving the value
+   |
+LL |         a @ Some((ref mut b @ ref mut c, d @ ref e)) => {}
+   |                   ^^^
 
 error[E0382]: use of moved value
   --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:61:38
@@ -379,6 +383,10 @@ LL |         a @ Some((mut b @ ref mut c, d @ ref e)) => {}
    |                                      value moved here
    |
    = note: move occurs because value has type `main::U`, which does not implement the `Copy` trait
+help: borrow this field in the pattern to avoid moving the value
+   |
+LL |         a @ Some((mut b @ ref mut c, ref d @ ref e)) => {}
+   |                                      ^^^
 
 error[E0382]: borrow of moved value
   --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:71:30
@@ -412,6 +420,10 @@ LL |         a @ Some((mut b @ ref mut c, d @ ref e)) => {}
    |                   value moved here
    |
    = note: move occurs because value has type `main::U`, which does not implement the `Copy` trait
+help: borrow this field in the pattern to avoid moving the value
+   |
+LL |         a @ Some((ref mut b @ ref mut c, d @ ref e)) => {}
+   |                   ^^^
 
 error[E0382]: use of moved value
   --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:83:38
@@ -434,6 +446,10 @@ LL |         a @ Some((mut b @ ref mut c, d @ ref e)) => {}
    |                                      value moved here
    |
    = note: move occurs because value has type `main::U`, which does not implement the `Copy` trait
+help: borrow this field in the pattern to avoid moving the value
+   |
+LL |         a @ Some((mut b @ ref mut c, ref d @ ref e)) => {}
+   |                                      ^^^
 
 error[E0382]: borrow of moved value
   --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:93:30
diff --git a/src/test/ui/ref-suggestion.stderr b/src/test/ui/ref-suggestion.stderr
index 9ff8e21bb58..97d2c174d9a 100644
--- a/src/test/ui/ref-suggestion.stderr
+++ b/src/test/ui/ref-suggestion.stderr
@@ -28,6 +28,10 @@ LL |     x;
    |     ^ value used here after partial move
    |
    = note: move occurs because value has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait
+help: borrow this field in the pattern to avoid moving `x.0.0`
+   |
+LL |         (Some(ref y), ()) => {},
+   |               ^^^
 
 error: aborting due to 3 previous errors