# Chapter 15: Ownership verbs

Every parameter in Loup carries a *verb* that says what the function will do with the argument: read it, change it in place, take it over, or keep a reference to it. The verb is part of the signature, and for three of the four verbs the caller repeats it at the call site, so you can see at a glance which calls can change your data. The compiler checks both ends.

This chapter covers the four parameter verbs (`reads`, `edits`, `takes`, `shares`), the `modifier` keyword for methods that change `this`, `safe { }` blocks and `safe func`, `requires` clauses, and `synchronized` classes. All of them are compile-time checks that work the same in every mode, so a program checked with `loup run` gets exactly the same diagnostics as one built with `loup build --target native`. The normative text is in the Language Reference §3-2.4.9 through §3-2.4.13.

## The four verbs

| Verb | The function may | The caller writes | After the call the caller has |
|---|---|---|---|
| `reads` (the default) | read the value | nothing, or `reads:` | the value, unchanged |
| `edits` | read and change the value in place | `edits:` | the value, with the changes |
| `takes` | own the value | `takes:` | nothing — the binding is consumed |
| `shares` | keep a reference after it returns | `shares:` | the value, now also held elsewhere |

The verb goes in front of the parameter name: `func grow(edits xs: list[int])`. The acknowledgement at the call site looks like a named argument with the verb as its label: `grow(edits: xs)`.

## `reads` — the default

A parameter with no verb is a `reads` parameter. The function can look at the value but not change it, and the caller keeps it.

```loup
@mode(host.native)

func requestMethod(line: string): string {
    let space = line.indexOf(" ");
    if (space < 0) {
        return line;
    }
    return line.substring(0, space);
}

func main(): void {
    let line = "GET /api/users HTTP/1.1";
    println(requestMethod(line));          # implicit reads
    println(requestMethod(reads: line));   # the same call, spelled out
    println(line);                         # still ours
}
```

*Listing 15-1: A `reads` parameter needs no annotation at either end*

```text
GET
GET
GET /api/users HTTP/1.1
```

Writing `reads` explicitly is allowed on both sides and changes nothing; it is useful in a signature where the other parameters carry verbs. A `reads` parameter of a class type costs no ARC traffic: the callee borrows the caller's reference instead of retaining it.

"Read-only" is enforced. Here is a helper that tries to add a tag to the list it was given:

```loup,does_not_compile
@mode(host.native)

func addDefaultTag(tags: list[string]): void {
    if (not tags.contains("triaged")) {
        tags.push("triaged");
    }
}

func main(): void {
    var tags = ["billing"];
    addDefaultTag(tags);
    println(tags);
}
```

```text
main.lphc:5:9: error[E_READS_MUTATION]: E_READS_MUTATION: parameter 'tags' is a `reads` borrow (a parameter without a verb is read-only, §3-2.4.9) and cannot be mutated (`push` called on it); declare it `edits` and pass `edits: …` to change the caller's value, or copy it into a local first
```

The message names the parameter, says *why* it is read-only (it has no verb), names the offending operation, and offers both fixes. The same error fires for assigning to a field of a `reads` struct or class parameter, and for calling a `modifier` method on a `reads` class parameter. Assigning to the parameter itself (`tags = [...]`) is a different error — parameters are immutable bindings — reported as `cannot assign to immutable binding 'tags'`.

## `edits` — changing the caller's value in place

If the function's job is to change the caller's value, say so with `edits`:

```loup,does_not_compile
@mode(host.native)

func addDefaultTag(edits tags: list[string]): void {
    if (not tags.contains("triaged")) {
        tags.push("triaged");
    }
}

func main(): void {
    var tags = ["billing"];
    addDefaultTag(tags);
    println(tags);
}
```

```text
main.lphc:11:19: error[E_VERB_REQUIRED]: E_VERB_REQUIRED: parameter 'tags' requires 'edits' verb acknowledgment at call site
```

Changing the signature is not enough: every caller must now acknowledge that the call changes its list. This is the point of the verb system — a reader of `main` sees which calls can modify `tags` without opening the callee. Add the acknowledgement:

```loup
@mode(host.native)

func addDefaultTag(edits tags: list[string]): void {
    if (not tags.contains("triaged")) {
        tags.push("triaged");
    }
}

func main(): void {
    var tags = ["billing"];
    addDefaultTag(edits: tags);
    addDefaultTag(edits: tags);    # already tagged: no change
    println(tags);
}
```

*Listing 15-2: An `edits` parameter and its call-site acknowledgement*

```text
["billing", "triaged"]
```

The acknowledgement has to name the right verb. Writing `takes:` where the parameter is `edits` is a mismatch:

```loup,does_not_compile
@mode(host.native)

func addDefaultTag(edits tags: list[string]): void {
    tags.push("triaged");
}

func main(): void {
    var tags = ["billing"];
    addDefaultTag(takes: tags);
}
```

```text
main.lphc:9:26: error[E0001]: E_VERB_MISMATCH: call-site verb 'takes' does not match parameter 'tags' declared as 'edits'
```

### `edits` on structs

Structs are values: assigning one copies it (chapter 7). `edits` is how a function works on the caller's struct without copying it. A small tokenizer keeps its position in a `Cursor` and lets helper functions move it forward:

```loup
#~ @mode(host.native)
#~
struct Cursor {
    text: string;
    var pos: int;
}

func skipSpaces(edits c: Cursor): void {
    while (c.pos < c.text.length() and c.text[c.pos] == " ") {
        c.pos = c.pos + 1;
    }
}

func readWord(edits c: Cursor): string {
    let start = c.pos;
    while (c.pos < c.text.length() and c.text[c.pos] != " ") {
        c.pos = c.pos + 1;
    }
    return c.text.substring(start, c.pos);
}

func main(): void {
    var c = Cursor(text = "  GET   /index.html", pos = 0);
    skipSpaces(edits: c);
    let method = readWord(edits: c);
    skipSpaces(edits: c);
    let path = readWord(edits: c);
    println(i"{$ method $} {$ path $} (stopped at {$ c.pos $})");
}
```

*Listing 15-3: Helpers that advance a struct cursor through `edits`*

```text
GET /index.html (stopped at 19)
```

Without `edits`, `skipSpaces` could only return a new `Cursor` for the caller to store back. With it, the helpers read like methods, and the call sites still show that `c` changes.

### `edits` on class instances

A class instance is shared by reference, so `edits` on a class parameter means "this function writes to that object". In 1.25.1 writing a field of a class instance also needs a *safe context* (covered below), and a function with an `edits` class parameter does not get one automatically:

```loup,does_not_compile
@mode(host.native)

class Account {
    pub var balance: int = 0;
}

func deposit(edits a: Account, amount: int): void {
    a.balance = a.balance + amount;
}

func main(): void {
    let acct = Account();
    deposit(edits: acct, 25);
    println(acct.balance);
}
```

```text
main.lphc:8:5: error[E_SAFE_CONTEXT_REQUIRED]: E_SAFE_CONTEXT_REQUIRED: `modifier` method 'balance' called on ARC receiver of type 'Account' outside `safe {}` or `safe func` context — wrap the call in `safe { ... }` or mark the enclosing function as `safe func`
```

The compiler treats a write to a `pub var` field from outside the class like a call to a `modifier` method, which is why the message talks about "`modifier` method 'balance'". Declare the function `safe func`:

```loup
#~ @mode(host.native)
#~
class Account {
    pub var balance: int = 0;
}

safe func deposit(edits a: Account, amount: int): void {
    a.balance = a.balance + amount;
}

func main(): void {
    let acct = Account();
    deposit(edits: acct, 25);
    deposit(edits: acct, 17);
    println(acct.balance);
}
```

*Listing 15-4: Writing a class field through an `edits` parameter*

```text
42
```

> **Spec and toolchain differ.** The Language Reference (§3-2.4.12) says a function with an `edits` parameter is implicitly a `safe func`, so Listing 15-4 should not need the word `safe`. Stage0 1.25.1 still requires it. Writing `safe func` is correct under both readings.

### What `edits` does not check yet

The Language Reference also specifies alias checks for `edits`: passing the same value as two `edits` arguments (`E_EDITS_ALIAS`), or as an `edits` and a `reads` argument of one call (`E_EDITS_READS_ALIAS`), is an error. The 1.25.1 compilers do not report either, and they also accept an `edits:` acknowledgement on a plain `reads` parameter. Do not rely on the compiler to catch these: a call like `appendAll(edits: xs, xs)` that iterates one list while growing the other compiles, and loops for ever.

## `takes` — handing a value over

A `takes` parameter receives ownership. The caller's binding is consumed at the call, and the compiler rejects any later use of it. A log writer that flushes a batch and is done with it:

```loup
#~ @mode(host.native)
#~
func flush(takes batch: list[string]): int {
    for (line in batch) {
        println(i"write: {$ line $}");
    }
    return batch.length();
}

func main(): void {
    var pending = ["boot", "ready"];
    let written = flush(takes: pending);
    println(i"{$ written $} lines flushed");
    pending = ["idle"];    # a var may be given a fresh value
    println(pending);
}
```

*Listing 15-5: Passing a batch to a `takes` parameter*

```text
write: boot
write: ready
2 lines flushed
["idle"]
```

Forget the acknowledgement and the compiler asks for it, just as it does for `edits`:

```loup,does_not_compile
@mode(host.native)

func flush(takes batch: list[string]): int {
    return batch.length();
}

func main(): void {
    var pending = ["boot", "ready"];
    println(flush(pending));
}
```

```text
main.lphc:9:19: error[E_VERB_REQUIRED]: E_VERB_REQUIRED: parameter 'batch' requires 'takes' verb acknowledgment at call site
```

The more interesting mistake is using the binding after handing it over:

```loup,does_not_compile
@mode(host.native)

func flush(takes batch: list[string]): int {
    return batch.length();
}

func main(): void {
    var pending = ["boot", "ready"];
    let written = flush(takes: pending);
    println(i"flushed {$ written $} of {$ pending.length() $}");
}
```

```text
main.lphc:10:43: error[E_USE_AFTER_TAKE]: E_USE_AFTER_TAKE: binding 'pending' was consumed by a `takes` parameter and cannot be used again
```

After `takes:`, `pending` holds nothing the compiler will let you read. Either read what you need *before* the call (`let total = pending.length();`), or, if the binding is a `var`, give it a new value first, as Listing 15-5 does. Since 1.24 only `takes` consumes a binding; `reads`, `edits` and `shares` never do.

For ordinary class instances, `takes` hands over the caller's reference count instead of adding one. For `unique` values (chapter 17) it is a true move with no reference counting at all.

> **Known gap.** In 1.25.1 the use-after-take check covers calls to functions, but not calls to methods: after `pool.giveBack(takes: conn)`, a later read of `conn` compiles. Keep `takes` arguments to methods as the last use of the binding.

## `shares` — keeping a reference

`shares` says the function will keep a reference to the argument after it returns — store it in a field, a collection, or a closure. Both sides then hold the value. An event bus that records every event it publishes:

```loup
#~ @mode(host.native)
#~
class Event {
    pub let name: string;
    new(name: string) { this.name = name; }
}

class Bus {
    pub var history: list[Event] = [];
}

safe func publish(edits bus: Bus, shares event: Event): void {
    bus.history.push(event);
}

func main(): void {
    let bus = Bus();
    let deploy = Event("deploy");
    publish(edits: bus, shares: deploy);
    publish(edits: bus, shares: Event("rollback"));
    println(deploy.name);                 # the caller still has it
    println(bus.history.length());
}
```

*Listing 15-6: A `shares` parameter that is stored past the call*

```text
deploy
2
```

`shares` must be acknowledged at the call site like `edits` and `takes`; omitting it is `E_VERB_REQUIRED`. On a `unique` value, `shares` gives up uniqueness for good (chapter 17).

> **Known gap.** The Language Reference says that storing or returning a plain `reads` parameter beyond the call is `E_BORROW_ESCAPE`. That check is not implemented in 1.25.1: `bus.history.push(event)` also compiles when `event` has no verb. Write `shares` whenever a function keeps what it is given; it documents the contract and keeps your code correct once the check lands.

## `modifier` — methods that change `this`

The verbs describe parameters. For the receiver of a method, Loup uses one keyword: a class method that writes any `var` field of `this` must be declared `modifier`. A small inventory:

```loup
#~ @mode(host.native)
#~
class Inventory {
    var counts: dict[string, int] = {};

    pub modifier func add(item: string, n: int): void {
        let have = this.counts.get(item).unwrap_or(0);
        this.counts.set(item, have + n);
    }

    pub func count(item: string): int {
        return this.counts.get(item).unwrap_or(0);
    }
}

func main(): void {
    let stock = Inventory();
    safe {
        stock.add("bolt", 40);
        stock.add("nut", 25);
        stock.add("bolt", 10);
    }
    println(i"bolts: {$ stock.count("bolt") $}, washers: {$ stock.count("washer") $}");
}
```

*Listing 15-7: `modifier` marks the methods that change the object*

```text
bolts: 50, washers: 0
```

Scanning the class, you can see which methods change state without reading their bodies. The compiler holds you to it — forget `modifier` and the method no longer compiles:

```loup,does_not_compile
@mode(host.native)

class Meter {
    var reading: int = 0;

    pub func tick(): void {
        this.reading = this.reading + 1;
    }
}

func main(): void {
    let m = Meter();
}
```

```text
main.lphc:7:9: error[E_MODIFIER_REQUIRED]: E_MODIFIER_REQUIRED: writing to `var` field 'reading' of `self` in method 'tick' of class 'Meter' requires the method to be declared as `modifier`
```

`modifier` applies to class methods only. A struct is a value, so a struct method may assign the fields of its own copy freely, and marking it `modifier` is an error:

```loup,does_not_compile
@mode(host.native)

struct Meter {
    var reading: int;

    modifier func tick(): void {
        this.reading = this.reading + 1;
    }
}

func main(): void {
    var m = Meter(reading = 0);
}
```

```text
main.lphc:6:5: error[E_MODIFIER_ON_STRUCT]: E_MODIFIER_ON_STRUCT: `modifier` keyword on method 'tick' of struct 'Meter' is not allowed — `modifier` applies only to class methods
```

Drop the keyword and the struct method works on any `var` binding (`var m = Meter(reading = 0); m.tick();`).

## `safe` contexts and `safe func`

A class instance can be reachable through several references at once. To keep "who may change this object, and when" under the compiler's control, a call to a `modifier` method on an ordinary (reference-counted) instance must happen inside a *safe context*. Listing 15-7 put its `add` calls in a `safe { }` block; without it:

```loup,does_not_compile
@mode(host.native)

class Meter {
    var reading: int = 0;
    pub modifier func tick(): void { this.reading = this.reading + 1; }
    pub func value(): int { return this.reading; }
}

func main(): void {
    let m = Meter();
    m.tick();
    println(m.value());
}
```

```text
main.lphc:11:5: error[E_SAFE_CONTEXT_REQUIRED]: E_SAFE_CONTEXT_REQUIRED: `modifier` method 'tick' called on ARC receiver of type 'Meter' outside `safe {}` or `safe func` context — wrap the call in `safe { ... }` or mark the enclosing function as `safe func`
```

There are two fixes, and the message names both. A `safe { }` block anchors a safe context for a few statements; a `safe func` is a safe context for its whole body, so it can call `modifier` methods freely:

```loup
#~ @mode(host.native)
#~
class Meter {
    var reading: int = 0;
    pub modifier func tick(): void { this.reading = this.reading + 1; }
    pub func value(): int { return this.reading; }
}

safe func tickN(edits m: Meter, n: int): void {
    for (i in 0..n) {
        m.tick();
    }
}

func main(): void {
    let m = Meter();
    safe { m.tick(); }        # anchored inline
    tickN(edits: m, 4);       # the safe func provides its own context
    println(m.value());
}
```

*Listing 15-8: `safe { }` and `safe func`*

```text
5
```

The same rule applies inside a class: a non-`modifier` method that calls a `modifier` method on `this` gets `E_SAFE_CONTEXT_REQUIRED` too. Make the calling method a `modifier` as well — a `modifier` body is already a safe context for `this`.

Two kinds of receiver need no safe context: a `unique class` instance (chapter 17), whose single owner is proven at compile time, and a `synchronized` class instance (below), whose lock is taken at run time.

> **Spec and toolchain differ.** The Language Reference (§3-2.4.12) specifies more than 1.25.1 checks. A `safe func` is supposed to be callable only from a safe context, so that the requirement propagates up the call stack like `async`; Stage0 lets any function call a `safe func`, as `main` does in Listing 15-8. Inside a safe context the compiler is also supposed to reject a `modifier` call while another alias of the receiver is live (`E_SAFE_ALIAS_MUTABLE`); that check is not implemented. A setter (`pub set timeout(...)`) is specified as an implicit `modifier`, but a setter call outside `safe { }` compiles today.

## `requires` clauses and `distinct()`

A `requires` clause after the parameter list states contracts that span several parameters. The verb form gives a parameter its verb, exactly as writing it inline would:

```loup
#~ @mode(host.native)
#~
func moveAll(dst: list[int], src: list[int]) requires edits(dst): void {
    for (x in src) {
        dst.push(x);
    }
}

func main(): void {
    var queue = [1, 2];
    let incoming = [3, 4];
    moveAll(edits: queue, incoming);
    println(queue);
}
```

*Listing 15-9: `requires edits(dst)` is the same as `edits dst`*

```text
[1, 2, 3, 4]
```

> ⚠ **Planned.** The rest of `requires` is specified (Language Reference §3-2.4.10) but not yet checked: `distinct(a, b)`, which promises that two buffers do not overlap and lets the LLVM backend mark them `noalias`; its errors `E_DISTINCT_ALIAS` and `E_DISTINCT_NOT_READS_EDITS`; the call-site acknowledgements `edits(dst)` and `distinct(dst, src)` written among the arguments; and `requires` on methods. Both compilers parse a `distinct(...)` contract and then ignore it, so a call that passes the same list twice is accepted. Write the clause if it documents your intent, but do not count on it.

## `synchronized` classes

`safe` contexts are a compile-time proof, and the proof stops at a thread boundary. An object that several threads change needs a run-time lock, and a `synchronized class` provides one: every `modifier` method takes the instance's lock on entry and releases it on return (or when an exception unwinds). Callers need no `safe { }` block.

Sharing an ordinary class with a thread is rejected outright:

```loup,does_not_compile
@mode(host.native)

class HitCounter {
    var hits: int = 0;
    pub modifier func record(): void { this.hits = this.hits + 1; }
}

func main(): void {
    let counter = HitCounter();
    let worker = Thread.spawn(func() {
        safe { counter.record(); }
    });
    worker.join();
}
```

```text
main.lphc:10:18: error[E_NON_SENDABLE_CAPTURE]: E_NON_SENDABLE_CAPTURE: captured variable 'counter' of type 'HitCounter' does not implement Sendable — every value captured by a closure passed to Thread.spawn must be Sendable
  note: v1.20 §2-1.8.1: classes are never implicitly Sendable. Declare `class HitCounter implements Sendable` (every `var` field must then be Mutex[T], RwLock[T] or Atomic[T]), or pass the data as a Sendable value.
```

The note offers `Mutex`/`RwLock`/`Atomic` fields (chapter 20). The other way is to make the class `synchronized` and declare it `Sendable`; the lock is what makes its `var` fields safe to share:

```loup
@mode(host.native)

synchronized class HitCounter implements Sendable {
    var hits: int = 0;

    pub modifier func record(): void {
        this.hits = this.hits + 1;
    }

    pub modifier func total(): int {
        return this.hits;
    }
}

func main(): void {
    let counter = HitCounter();
    let a = Thread.spawn(func() {
        for (i in 0..1000) { counter.record(); }
    });
    let b = Thread.spawn(func() {
        for (i in 0..1000) { counter.record(); }
    });
    a.join();
    b.join();
    println(counter.total());
}
```

*Listing 15-10: A `synchronized` counter shared by two threads*

```text
2000
```

Notice that `total` is a `modifier` although it only reads. In a `synchronized` class the lock is held only inside `modifier` methods, so an ordinary method that reads a `var` field could see a half-finished write. The compiler rejects it:

```loup,does_not_compile
@mode(host.native)

synchronized class HitCounter {
    var hits: int = 0;
    pub modifier func record(): void { this.hits = this.hits + 1; }
    pub func total(): int { return this.hits; }
}

func main(): void {
    let counter = HitCounter();
    counter.record();
    println(counter.total());
}
```

```text
main.lphc:6:36: error[E_MODIFIER_REQUIRED]: E_MODIFIER_REQUIRED: reading `var` field 'hits' of `self` in non-`modifier` method 'total' of `synchronized` class 'HitCounter' is unsynchronized; declare the method `modifier` so the read occurs under the lock
```

Fields declared `let` never change, so ordinary methods may read them. A `pub var` field on a `synchronized` class draws the warning `E_PUB_VAR_ON_SYNCHRONIZED`, because code outside the class could write it without the lock.

A few facts about running Listing 15-10:

- Under `loup run` the interpreter runs threads under a global lock, so the program is correct but not parallel. A native binary uses real OS threads and the per-instance lock.
- The lock is not reentrant: a `modifier` that calls another `modifier` on the same `this` would deadlock. The specified diagnostic for this, `E_SYNCHRONIZED_REENTRANT`, is not reported by 1.25.1, so avoid the pattern yourself.
- Each `modifier` call takes and releases a lock. For a counter on a hot path, an `Atomic[int]` field is cheaper; chapter 20 compares the options.

## Summary

- A parameter's verb says what the function does with it: `reads` (default) reads, `edits` changes in place, `takes` consumes, `shares` keeps a reference.
- `edits`, `takes` and `shares` must be acknowledged at the call site (`grow(edits: xs)`); a missing acknowledgement is `E_VERB_REQUIRED`, a wrong one `E_VERB_MISMATCH`.
- Changing a `reads` parameter is `E_READS_MUTATION`; using a binding after `takes:` is `E_USE_AFTER_TAKE`.
- A class method that writes a `var` field is a `modifier` (`E_MODIFIER_REQUIRED` otherwise); struct methods never are (`E_MODIFIER_ON_STRUCT`).
- Calling a `modifier` method, or writing a field, on an ordinary class instance needs `safe { }` or a `safe func` (`E_SAFE_CONTEXT_REQUIRED`); `unique` and `synchronized` receivers do not.
- `requires edits(x)` works; `distinct()` and the other `requires` contracts are planned.
- A `synchronized class implements Sendable` is the simplest object that several threads can change; its reads of `var` fields must be `modifier` methods too.
- Several specified checks (`E_EDITS_ALIAS`, `E_BORROW_ESCAPE`, `E_SAFE_ALIAS_MUTABLE`, `E_SYNCHRONIZED_REENTRANT`, use-after-take through methods) are not implemented in 1.25.1; the text above says where.

Chapter 16 looks at `borrow`, `excl borrow` and `noescape fn`, the lower-level keywords underneath the verbs.
