E3058

E3058 — A return, break or continue that leaves a task, or a task that joins another task. The last expression of a task body is its result. A task cannot join another task yet. Join both tasks in the tasks block.

What the compiler reports

Error[E3058] at [[@LINE+4]]:7: a `return` cannot leave a task
Error[E3058] at [[@LINE+5]]:9: a `break` cannot leave a task
Error[E3058] at [[@LINE+7]]:9: a task cannot join another task yet
Error[E4001] at [[@LINE+7]]:5: Use of moved or consumed linear variable: h
{{^}}Semantic check failed on {{.*}}: 11 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
//
//
// The end of a `tasks` block waits for each task that it started. So a `spawn` must be in a
// `tasks` block, and a task handle must not leave it: its only use is `h.join()`. A task body
// ends with its result, so a `return` or `break` must not leave it.

fn no_tasks_block() -> i32 {
  let h = spawn {
    1
  };
  return h.join();
}

fn spawn_in_a_closure() -> void {
  tasks {
    let f = |x : i32| spawn {
      x
    };
  }
}

fn spawn_directly_in_a_task() -> void {
  tasks {
    spawn {
      spawn {
        1
      };
    };
  }
}

fn handle_is_the_value_of_the_block() -> i32 {
  return tasks {
    let h = spawn {
      1
    };
    h
  };
}

fn handle_given_to_a_variable() -> void {
  tasks {
    let h = spawn {
      1
    };
    let g = h;
  }
}

fn spawn_as_an_argument() -> void {
  tasks {
    print(spawn {
      1
    });
  }
}

fn handle_with_a_wrong_type() -> void {
  tasks {
    let h : i32 = spawn {
      1
    };
    h.join();
  }
}

fn return_from_a_task() -> i32 {
  tasks {
    spawn {
      return 1;
    };
  }
  return 0;
}

fn break_out_of_a_task() -> void {
  tasks {
    for i in 0..3 {
      spawn {
        break;
      };
    }
  }
}

fn task_joins_a_task() -> void {
  tasks {
    let a = spawn {
      1
    };
    let b = spawn {
      a.join() + 1
    };
    b.join();
  }
}

fn joined_twice() -> void {
  tasks {
    let h = spawn {
      1
    };
    h.join();
    h.join();
  }
}

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

// The errors above are all the errors.

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

Related