Sakura Lang

V6002 scoped_value_escape

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:

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):

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

compiler/tests/ui/V6002_scoped_value_escape.sakura, which the compiler's ui suite runs through sakura check:

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):

{
  "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, compiler/tests/ui/V6002_spawn_nested_lambda.sakura.

Fixed program

compiler/tests/ui/V6002_scoped_value_escape.fixed.sakura, which sakura check accepts with no diagnostics:

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")) }
}
Edit on GitHub

On this page