E3055
E3055 — A task takes a value that is notSend, or reads a variable whose type is notSync. A task that moves a value, copies it or writes it needsSend. A task that reads a variable in place shares it with the code around the task, so it needsSync. The error names the field that makes the type fail.
What the compiler reports
Error[E3055] at [[@LINE+4]]:5: the task reads 'c', which the code around the task can read at the same time, so its type 'SharedCell<i32>' must be `Sync`: 'SharedCell<i32>' is never `Sync`
Error[E3055] at [[@LINE+5]]:5: the task copies 'p' into itself, so its type '*mut i32' must be `Send`: '*mut i32' is a raw pointer, which is not `Send`
{{^}}Semantic check failed on {{.*}}: 10 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
//
//
// A task runs at the same time as the code after its `spawn`, until its `join()` or the end of
// its `tasks` block. It holds each variable that it writes, and each one that it reads and does
// not copy, for that time. A task in a loop also runs at the same time as the next pass.
//
// A task that writes a value, or moves it into itself, needs the value to be `Send`. A task that
// reads a variable in place needs it to be `Sync`.
import core::cell;
import std::vec;
fn read_while_a_task_writes() -> i32 {
let mut x = 0;
tasks {
spawn {
x += 1;
};
let y = x;
}
return x;
}
fn two_tasks_write() -> i32 {
let mut x = 0;
tasks {
spawn {
x += 1;
};
spawn {
x += 2;
};
}
return x;
}
fn change_what_a_task_reads() -> i32 {
let mut v = Vec<i32>::new();
tasks {
let h = spawn {
v.len()
};
v = Vec<i32>::new();
h.join();
}
return 0;
}
fn task_in_a_loop_writes() -> i32 {
let mut x = 0;
tasks {
for i in 0..4 {
spawn {
x += i;
};
}
}
return x;
}
fn loop_changes_what_a_task_reads() -> void {
let mut v = Vec<i32>::new();
tasks {
for i in 0..3 {
v.push(i);
spawn {
v.len();
};
}
}
}
// A task moves `v`, because `v` ends before the task's join. So the pass cannot use `v` again.
fn use_a_local_that_a_task_moved() -> void {
tasks {
for i in 0..3 {
let mut v = Vec<i32>::new();
v.push(i);
spawn {
v.len();
};
let _w = v;
}
}
}
fn task_outlives_what_a_reference_points_at() -> void {
tasks {
for i in 0..3 {
let n = i;
let r = &n;
spawn {
*r;
};
}
}
}
fn use_after_moving_into_a_task() -> i32 {
let v = Vec<i32>::new();
tasks {
spawn {
let w = v;
w.len();
};
}
let again = v;
return 0;
}
fn share_a_shared_cell() -> void {
let c = SharedCell<i32>::new(1);
tasks {
spawn {
c.get();
};
}
}
fn copy_a_raw_pointer() -> i32 {
let mut x = 0;
let p = (&mut x) as *mut i32;
tasks {
spawn {
unsafe { *p = 1; }
};
}
return x;
}
fn main() -> i32 {
return 0;
}
// The errors above are all the errors.
From tests/frontend/fail/tasks_that_use_a_variable_at_the_same_time.vx, which asserts this diagnostic on every commit.
Related
A tour of VxThe chapter covering the rule this code enforces.
All diagnosticsEvery code the compiler can emit, grouped by stage.
E3054
unsafe impl of a trait other than Send or Sync, or of no trait at all. Only those two traits make a claim that the compiler cannot check. Remove unsafe.
E3056spawn { .. } outside a tasks { .. } block. The end of the tasks block waits for each task, so a task cannot outlive the variables it uses. A spawn in a closure, or directly in another task, is outside the block too. Put the spawn in a tasks block of its own.