E4011

E4011 — A view of a tensor (a row q[i] or a field h.t) stored where a tensor of its own is held: a variable that already exists (t = q[i]) or a struct field (h.t = q[i], Holder { t : q[i] }). A view shares its owner's memory and is not a copy. Bind it to a new variable with let instead.

What the compiler reports

Error[E4011] at 83:11: this stores a view of `q` in `keep`, which holds a tensor of its own
Error[E4011] at 91:10: this stores a view of `q` in the field `t`

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
//
//
// A row `q[1]` or a tensor field `h.t` is a view of its owner's memory, not a copy, so it
// borrows the owner as `&q` would. Each function below breaks one rule of that borrow.

struct Holder {
  t : Tensor<f32, [4]>,
}

fn take(t : Tensor<f32, [2, 4]>) -> f32 {
  return t[0][0];
}

fn take_holder(h : Holder) -> f32 {
  return h.t[0];
}

// The tensor is moved while its row is still used.
fn owner_moved() -> f32 {
  let q = Tensor<f32, [2, 4]>::new();
  let r = q[1];
  let s = take(q);
  return r[0] + s;
}

// The struct is moved while a view of its field is still used.
fn struct_moved() -> f32 {
  let h = Holder {
    t : Tensor<f32, [4]>::new()
  };
  let t = h.t;
  let s = take_holder(h);
  return t[0] + s;
}

// The tensor is written while its row is still used.
fn owner_written() -> f32 {
  let mut q = Tensor<f32, [2, 4]>::new();
  let r = q[1];
  q[1][0] = 5.0;
  return r[0];
}

// The tensor is read while a row that can write it is still used.
fn owner_read() -> f32 {
  let mut q = Tensor<f32, [2, 4]>::new();
  let mut r = q[1];
  let x = q[1][0];
  r[0] = 5.0;
  return x + r[0];
}

// A row that can write is taken from a tensor that is not `mut`.
fn row_of_read_only() -> f32 {
  let q = Tensor<f32, [2, 4]>::new();
  let mut r = q[1];
  r[0] = 5.0;
  return r[0];
}

// A row of a tensor the function owns is returned.
fn row_of_a_local() -> Tensor<f32, [4]> {
  let q = Tensor<f32, [2, 4]>::new();
  return q[1];
}

// A parameter taken by value is owned by the function too.
fn row_of_a_parameter(q : Tensor<f32, [2, 4]>) -> Tensor<f32, [4]> {
  return q[1];
}

// A row is stored in a variable that already holds a tensor.
fn stored_in_a_variable() -> f32 {
  let q = Tensor<f32, [2, 4]>::new();
  let mut keep = Tensor<f32, [4]>::new();
  keep = q[1];
  return keep[0];
}

// A row is stored in a struct field.
fn stored_in_a_field() -> f32 {
  let q = Tensor<f32, [2, 4]>::new();
  let h = Holder {
    t : q[1]
  };
  return h.t[0];
}

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

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

Related