# V6002 scoped_value_escape

URL: /docs/errors/V6002
Markdown: /docs/errors/V6002.md

> A scoped value escapes the with that bounds it (design §6.3, §17.70).

An error from the `capability` phase. `sakura explain V6002` prints this explanation offline.

A scoped value escapes the `with` that bounds it (design §6.3, §17.70). Scoped types are
external types (`Dir`, `File`, `Scope`, …), `Handler<…>` and any type containing one; a lambda is
scoped when its type is, or when it captures a scoped local. This fires when the value of a
`with` expression is scoped, when a top-level `let` has a scoped type, and when the lambda given
to `Task.spawn` captures a scoped local bound inside a `with` that encloses the spawn but not the
binding of its `Scope`. Returning a scoped value from an ordinary function is allowed.

Wrong:

```sakura
import std/fs.{Fs}
fn leak() -> Handler<Fs> ! {Fs} {
  with Fs.rooted(Fs.root()) { Fs.rooted(Fs.root()) }
}
```

Fixed (use the handler inside the `with` and return data):

```sakura
import std/fs.{Fs, FsError}
import std/path.{Path}
fn read_config() -> Bytes ! {Fs, Fail<FsError>} {
  with Fs.rooted(Fs.root()) { Fs.read(Path.of("config.toml")) }
}
```

## Wrong program [#wrong-program]

[`compiler/tests/ui/V6002_scoped_value_escape.sakura`](https://github.com/icantcodefyi/sakuralang/blob/master/compiler/tests/ui/V6002_scoped_value_escape.sakura), which the compiler's ui suite runs through `sakura check`:

```sakura
import std/fs.{Fs}

fn leak() -> Handler<Fs> ! {Fs} { with Fs.rooted(Fs.root()) { Fs.rooted(Fs.root()) } }
```

`sakura check --json` reports this record (pretty-printed; `$DIR` is the fixture's directory):

```json
{
  "schema_version": "1.0",
  "kind": "diagnostic",
  "id": "d:1aa70ab5e952dc43",
  "code": "V6002",
  "severity": "error",
  "phase": "capability",
  "message": "the value of this `with` is a `Handler<Fs>`, a scoped value: capabilities, handlers and resources may not outlive the `with` that produced them (design §6.3)",
  "snapshot": "sha256:$SNAPSHOT",
  "symbol_id": "main::V6002_scoped_value_escape::leak",
  "spans": [
    {
      "uri": "file://$DIR/V6002_scoped_value_escape.sakura",
      "content_hash": "sha256:afc99366219bd0d4a54a4e2ac146eb68f3f379f6e940a7d3dba2b8d2f5d5411b",
      "start_byte": 140,
      "end_byte": 160,
      "role": "primary"
    }
  ],
  "related_ids": [],
  "expected": null,
  "actual": null,
  "obligation": null,
  "counterexample": null,
  "explanation_uri": "sakura://explain/V6002",
  "fixes": []
}
```

More fixtures for this code: [`compiler/tests/ui/V6002_spawn_captures_inner_with.sakura`](https://github.com/icantcodefyi/sakuralang/blob/master/compiler/tests/ui/V6002_spawn_captures_inner_with.sakura), [`compiler/tests/ui/V6002_spawn_nested_lambda.sakura`](https://github.com/icantcodefyi/sakuralang/blob/master/compiler/tests/ui/V6002_spawn_nested_lambda.sakura).

## Fixed program [#fixed-program]

[`compiler/tests/ui/V6002_scoped_value_escape.fixed.sakura`](https://github.com/icantcodefyi/sakuralang/blob/master/compiler/tests/ui/V6002_scoped_value_escape.fixed.sakura), which `sakura check` accepts with no diagnostics:

```sakura
import std/fs.{Fs, FsError}
import std/path.{Path}

fn read_config() -> Bytes ! {Fs, Fail<FsError>} {
  with Fs.rooted(Fs.root()) { Fs.read(Path.of("config.toml")) }
}
```
