E4007

E4007 — A value moved while a borrow of it is still used afterwards: let r = &a; let b = a; followed by a use of r. The borrow would point at a value that has moved away.

What the compiler reports

Error[E4007] at 96:11: `a` is moved while it is borrowed, and the borrow is used after the move
Error[E4008] at 105:11: this moves a value of type `S` out from behind a reference
7 error(s) emitted

The fragments the test suite holds the compiler to. An ellipsis marks text the test does not constrain; the emitted message also carries a source location.

A program that triggers it

//
// Part of the Vx Project, under the Apache License v2.0 with LLVM Exceptions.
// See LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//
// `S` is not `Copy`. A value moved inside a loop is used again on the next pass unless it is
// given a new value first (E4001, at the loop); a `continue` goes round again too, and a
// `break` carries the move out of the loop. A move in one `match` arm counts after the
// `match`. A value cannot move while a borrow of it is still used (E4007), and a reference
// cannot give away what it points at (E4008).

struct S {
  v : i64,
}

fn take(s : S) -> i64 {
  return s.v;
}

fn moved_in_a_while(n : i32) -> i32 {
  let a = S {
    v : 1i64
  };
  let mut i = 0;
  while i < n {
    take(a);
    i = i + 1;
  }
  return 0;
}

fn moved_then_continue(n : i32) -> i32 {
  let a = S {
    v : 1i64
  };
  let mut i = 0;
  while i < n {
    i = i + 1;
    if i == 2 {
      take(a);
      continue;
    }
  }
  return 0;
}

fn moved_in_a_for(n : i32) -> i32 {
  let a = S {
    v : 1i64
  };
  for i in 0..n {
    take(a);
  }
  return 0;
}

fn used_after_a_loop_that_moved_it(n : i32) -> i64 {
  let a = S {
    v : 1i64
  };
  let mut i = 0;
  while i < n {
    if i == 2 {
      take(a);
      break;
    }
    i = i + 1;
  }
  return take(a);
}

fn used_after_a_match_arm_moved_it(n : i32) -> i64 {
  let a = S {
    v : 1i64
  };
  match n {
    0 => {
      take(a);
    }
    _ => {
      print(1);
    }
  }
  return take(a);
}

fn moved_while_borrowed() -> i64 {
  let a = S {
    v : 1i64
  };
  let r = &a;
  let b = a;
  return r.v + take(b);
}

fn moved_out_of_a_reference() -> i64 {
  let a = S {
    v : 1i64
  };
  let r = &a;
  let b = *r;
  return take(b);
}

fn main() -> i32 {
  return 0;
}

From tests/frontend/fail/moves_in_loops_and_under_borrows.vx, which asserts this diagnostic on every commit.

Related