Skip to main content

cove_runtime/
interp.rs

1//! The MVP tree-walking interpreter.
2//!
3//! The interpreter is an ordinary evaluator over [`cove_syntax::ast`] plus the
4//! five rules that make Cove Cove:
5//!
6//! - assignment and ordinary argument passing clone a [`Value`], and `Clone`
7//!   already encodes field-wise shallow copy, so there is no deep-copy path;
8//! - mutation resolves an lvalue down to a slot the caller owns. Which
9//!   lvalues source may write — `let` binds a read-only place and `var` a
10//!   mutable one — is checked before the run, by `cove-sema`, since ADR 0021;
11//! - `var self` and `var` parameters bind the caller's place instead of a copy;
12//! - Host API calls go through [`HostRegistry::call`], which enforces grants;
13//! - concurrent work belongs to a task scope, and leaving the scope waits for
14//!   or cancels the tasks spawned into it.
15//!
16//! Static checking (types, exhaustiveness, uniqueness) is future work; the
17//! interpreter enforces the same rules dynamically and says which rule it
18//! enforced. Two families of rule are no longer among them, because
19//! `cove check` decides them from the source and a program it refuses never
20//! reaches here: which places source may write, and whether a labelled
21//! argument stands in declaration order. ADR 0021 says why they went, and
22//! what is left in their place is a guard rather than a diagnostic —
23//! `var_self_needs_place` and `Interpreter::resolve_place`'s last arm are
24//! this evaluator saying it was handed something it cannot address, not the
25//! language saying a program is wrong.
26
27use std::cell::RefCell;
28use std::collections::BTreeSet;
29use std::rc::Rc;
30use std::sync::Arc;
31use std::time::Duration;
32
33use cove_diag::{SourceMap, Span};
34use cove_schema::builtins::{FreeBuiltinKind, MAP_ENTRY, NONE_CASE, OPTION, RESULT};
35use cove_sema::resolve::{Program, ResolvedModule};
36use cove_syntax::ast::{
37    Arg, BinaryOp, Block, EnumDecl, Expr, ExprKind, FnDecl, Ident, ItemKind, Param, Pattern,
38    PatternKind, Receiver, StmtKind, StrPart, StructDecl, Type, TypeKind, UnaryOp,
39};
40
41use crate::budget::{Budget, Cancellation, Meter, Stopped};
42use crate::builtins::{self, Callable};
43use crate::error::RuntimeError;
44use crate::heap::{Collection, Heap, HeapStats, SlotRoots};
45use crate::host::{HostRegistry, Reentry, ResourceHandle};
46use crate::runtime::{Runtime, ENTRY_TASK};
47use crate::schema::TypeSchema;
48use crate::task::{self, ChildFailure, Task, TaskOutcome, TaskScope, Tasking, Transfer};
49use crate::trace::{RunOutcome, Timing, TraceEvent};
50use crate::value::{
51    Closure, ClosureBody, DynValue, EnumValue, HostFnValue, RangeBounds, Repr, StructValue, Value,
52};
53use crate::wallclock::Instant;
54
55/// How deep Cove calls may nest before the runtime reports a limit instead of
56/// exhausting the host stack.
57///
58/// This is an unconditional safety net independent of [`crate::budget::Limits`]:
59/// a `Budget`'s `max_call_depth` is optional and `Limits::default()` imposes
60/// none, but the interpreter is a recursive Rust tree walker, so unbounded
61/// recursion must still be stopped before it exhausts the native stack. A host
62/// that configures a stricter `max_call_depth` is stopped by that limit first;
63/// this constant is the fallback when it does not.
64///
65/// The limit is calibrated against [`STACK_SIZE`], which is the stack the
66/// runtime gives every thread it runs Cove on. That is what makes the promise
67/// above a relationship rather than a coincidence: the number here bounds
68/// frames, the number there bounds bytes, and neither may be changed without
69/// reading the other. See [`STACK_SIZE`] for the measured per-frame cost the
70/// two are derived from.
71pub(crate) const MAX_CALL_DEPTH: usize = 256;
72
73/// The native stack one Cove frame costs, so that [`STACK_SIZE`] can be
74/// derived from [`MAX_CALL_DEPTH`] instead of chosen beside it.
75///
76/// Measured on macOS by lifting the depth limit, giving a task thread a known
77/// stack, and binary-searching the deepest recursion that runs cleanly; the
78/// figure is the slope between two stack sizes, so whatever the interpreter
79/// spends before the recursion starts cancels out. Four shapes were measured
80/// at 4 MiB and 16 MiB in a debug build and at 1 MiB and 4 MiB in a release
81/// build, and the figures here are the worst of the four rounded up to a
82/// whole number of kibibytes:
83///
84/// | recursion through          | debug   | release |
85/// |----------------------------|---------|---------|
86/// | a free function            | 123 KiB | 9.6 KiB |
87/// | a method on a struct       | 135 KiB | 9.6 KiB |
88/// | a `dyn` trait conformance  | 95 KiB  | 7.3 KiB |
89/// | a `match` with live locals | 101 KiB | 8.2 KiB |
90///
91/// A debug frame costs fourteen times a release one, which is why the two
92/// profiles cannot share a stack size: one number would be absurd in release
93/// or useless in debug.
94///
95/// This is a measurement of ordinary shapes and not a bound on every shape.
96/// The interpreter recurses once more for each level of expression nesting,
97/// so a program that writes its recursive call inside a long chain of nested
98/// expressions spends more per Cove frame than any of these, and no constant
99/// can be the worst case for a program the compiler has not seen. That is
100/// what the margin in [`STACK_SIZE`] is for.
101#[cfg(debug_assertions)]
102const STACK_PER_FRAME: usize = 136 * 1024;
103
104/// The native stack one Cove frame costs. See the debug-build definition
105/// above for how both figures were measured.
106#[cfg(not(debug_assertions))]
107const STACK_PER_FRAME: usize = 10 * 1024;
108
109/// The native stack one reentry level costs, measured the same way with
110/// [`MAX_REENTRY_DEPTH`] lifted and `clock.timeout` nested into itself: 163.8
111/// KiB in a debug build and 16.1 KiB in a release one, rounded up here as
112/// above. That confirms the figure [`MAX_REENTRY_DEPTH`] was calibrated
113/// against, which was thirteen levels in a 2 MiB task thread.
114///
115/// [`MAX_CALL_DEPTH`] already counts the Cove frames inside a reentry level,
116/// so what this adds to the budget is counted twice on purpose: a host's own
117/// native frames are a host's business and nothing measures them, and eight
118/// levels of the shipped hosts are cheap enough next to 256 Cove frames that
119/// paying for them twice costs less than reasoning about it.
120#[cfg(debug_assertions)]
121const STACK_PER_REENTRY: usize = 164 * 1024;
122
123/// The native stack one reentry level costs. See the debug-build definition
124/// above.
125#[cfg(not(debug_assertions))]
126const STACK_PER_REENTRY: usize = 17 * 1024;
127
128/// How much more stack a thread gets than the limits above can spend on it.
129///
130/// Three, because the per-frame figures are measured shapes rather than a
131/// worst case: a deeply nested expression costs more per frame than anything
132/// measured, another platform's calling convention or another compiler's
133/// inlining will not reproduce these numbers exactly, and a host that runs a
134/// callback spends stack nothing here counts. A margin is what stands in for
135/// all of that, and it is cheap: see [`STACK_SIZE`].
136const STACK_MARGIN: usize = 3;
137
138/// How much stack the runtime gives every thread it runs Cove on.
139///
140/// A tree-walking interpreter spends native stack per Cove frame, so
141/// `MAX_CALL_DEPTH` keeps its promise only on a stack big enough to hold
142/// that many frames. Nothing gave the runtime such a stack before: a spawned
143/// task took the platform default of 2 MiB, and the entry took whatever the
144/// process main thread happened to have, which is 8 MiB on macOS and Linux
145/// and 1 MiB on Windows. In a debug build 8 MiB held 65 frames of the
146/// cheapest recursion Cove can write, so the limit of 256 was reached only on
147/// a release build's main thread, and everywhere else an ordinary program
148/// with no capability granted at all could end the process by recursing.
149///
150/// So the size is derived from the limits rather than chosen beside them.
151/// Raising `MAX_CALL_DEPTH` raises this, which is the relationship the
152/// limit's promise rests on, and it is now arithmetic rather than a
153/// coincidence that held on one thread of one profile. It works out at about
154/// 106 MiB in a debug build and about 8 MiB in a release one.
155///
156/// A number that large in a debug build is affordable because a thread stack
157/// is reserved address space that commits a page at a time as it is touched.
158/// That was checked rather than assumed: a debug run holding a hundred tasks
159/// alive at once reached a maximum resident set of 14.9 to 15.1 MB under this
160/// size and 15.07 MB under the old platform default of 2 MiB — a difference
161/// smaller than the variation between runs — while reserving over 10 GiB of
162/// address space. What the size costs is address space per live task and not
163/// memory per live task, which is a trade a 64-bit host does not notice, and
164/// `max_tasks` is the control for how many live tasks a run may hold in any
165/// case.
166///
167/// An embedder that calls [`Interpreter::run_entry`] on a thread of its own
168/// is the one case the runtime cannot size, and the one case where the
169/// promise is the embedder's to keep; see that method for what to do about
170/// it.
171///
172/// `cove_syntax`'s `MAX_NESTING_DEPTH` answers the same question from the
173/// other side. The parser is handed a thread rather than making one, so it
174/// cannot size the stack and instead fixes the stack it is willing to promise
175/// — 2 MiB, what an unsized thread has — and derives its limit from what a
176/// level of nesting costs on it. Together the two bound both halves of a
177/// `.cove` file's route through the toolchain: reading it and running it.
178pub const STACK_SIZE: usize =
179    STACK_MARGIN * (MAX_CALL_DEPTH * STACK_PER_FRAME + MAX_REENTRY_DEPTH * STACK_PER_REENTRY);
180
181/// Runs `body` on a thread the runtime sized, and hands back what it
182/// produced.
183///
184/// This is how a host runs Cove on a stack `MAX_CALL_DEPTH` fits on. The
185/// process main thread is not one: its size is the platform's business, it is
186/// 1 MiB on Windows, and no `main` can change it after the fact. So every
187/// path the toolchain has into a Cove program — `cove run`, `cove test`,
188/// `cove generate`, `cove replay`, a `cove build` binary, and `cove-bench` —
189/// does its whole run inside one of these, and `Interpreter::spawn` gives a
190/// task thread the same size, so no thread this runtime evaluates Cove on has
191/// a stack it did not choose.
192///
193/// The thread is scoped, so `body` may borrow, and everything Cove-shaped can
194/// be built inside it: a [`Value`] is `Rc`-based and could not cross the
195/// boundary in either direction. Only `T` crosses, which is why it is `Send`.
196///
197/// A panic inside `body` is resumed on the calling thread rather than
198/// swallowed, so a bug reports itself exactly as it did when the same work
199/// ran inline. The `Err` is the machine refusing a thread, which is the one
200/// failure this adds.
201pub fn on_cove_stack<T: Send>(body: impl FnOnce() -> T + Send) -> std::io::Result<T> {
202    std::thread::scope(|scope| {
203        let thread = std::thread::Builder::new()
204            .name("cove entry".to_string())
205            .stack_size(STACK_SIZE)
206            .spawn_scoped(scope, body)?;
207        match thread.join() {
208            Ok(value) => Ok(value),
209            Err(panic) => std::panic::resume_unwind(panic),
210        }
211    })
212}
213
214/// How many host calls that are running a Cove callback may be stacked on one
215/// thread before the runtime refuses the next one.
216///
217/// [`MAX_CALL_DEPTH`] bounds Cove frames, and it is calibrated against the
218/// interpreter's own frames, which are the only ones it can see. A reentry
219/// level is not one of those: between the callback's frame and the frame that
220/// called the host sit `HostRegistry::dispatch` and then however much native
221/// stack the host itself uses, which is a host's business and nothing counts
222/// it. So the depth limit's promise — a limit reported instead of an
223/// exhausted native stack — holds for Cove calling Cove and stops holding
224/// exactly where a third party controls the multiplier.
225///
226/// This is the bound that puts it back. It is deliberately far below
227/// [`MAX_CALL_DEPTH`]: the deepest layering the shipped hosts reach is a
228/// route handler that bounds its work with `clock.timeout`, which is two, and
229/// nothing plausible needs eight. It is also measured rather than guessed: a
230/// thirteenth nested `clock.timeout` level exhausts the smallest stack this
231/// runtime runs Cove on, which is a spawned task's thread in a debug build.
232///
233/// It is a bound and not a proof. A host that puts a megabyte on the stack
234/// before it reenters can still overflow at the first level, and no counter
235/// here can know that; what this removes is the case where a *host* decides
236/// how many times the multiplier applies.
237pub(crate) const MAX_REENTRY_DEPTH: usize = 8;
238
239/// Fuel charged at every safepoint: a loop back edge, a function call, or an
240/// `await`.
241///
242/// ADR 0001 is explicit that fuel is a coarse runtime control, not a modeled
243/// instruction count — real safepoints vary enormously in the CPU work they
244/// guard, so no constant here would make fuel mean "instructions executed."
245/// A flat per-safepoint cost keeps that honest: fuel measures how many
246/// safepoints a run passed through, which is exactly what bounds a
247/// non-terminating loop or an unbounded recursion, and nothing more precise
248/// than that is claimed.
249pub const SAFEPOINT_FUEL: u64 = 10;
250
251/// Non-local control flow raised while evaluating an expression.
252enum Control {
253    Error(RuntimeError),
254    /// `return` unwinds to the enclosing function call.
255    Return(Value),
256    /// `break` / `break expr` unwinds to the nearest enclosing loop, which
257    /// evaluates to `()` however it leaves. An operand is evaluated where it
258    /// is written and its value discarded, so there is nothing to carry.
259    Break,
260    /// `continue` unwinds to the nearest enclosing loop's next iteration.
261    Continue,
262}
263
264impl From<RuntimeError> for Control {
265    fn from(error: RuntimeError) -> Self {
266        Control::Error(error)
267    }
268}
269
270type Eval = Result<Value, Control>;
271
272/// Converts a completed call back into an ordinary result.
273///
274/// `Break` and `Continue` reaching a function call boundary would mean
275/// `break` or `continue` was used outside a loop (or reached past a closure
276/// boundary), which resolve-time checking rejects before the interpreter ever
277/// runs; see `cove_sema::resolve`'s `break_outside_loop` / `continue_outside_loop`.
278fn finish(result: Eval) -> Result<Value, RuntimeError> {
279    match result {
280        Ok(value) => Ok(value),
281        Err(Control::Return(value)) => Ok(value),
282        Err(Control::Error(error)) => Err(error),
283        Err(Control::Break) => {
284            unreachable!("`break` outside a loop is rejected before execution")
285        }
286        Err(Control::Continue) => {
287            unreachable!("`continue` outside a loop is rejected before execution")
288        }
289    }
290}
291
292/// An assignable location: a binding slot plus the struct fields to navigate.
293///
294/// Every step is taken under a single borrow, so a place never holds a
295/// reference across the evaluation of another expression.
296///
297/// A place carried a `mutable` flag until ADR 0021, because `let` binds a
298/// read-only place and `var` a mutable one and this is where that used to be
299/// enforced. The rule has not changed; where it is enforced has.
300/// `Checker::place_mutability` in `cove-sema` is the one statement of it now,
301/// and a flag kept here would be a second — read by nothing, and free to
302/// drift from the one that decides.
303#[derive(Clone)]
304struct Place {
305    slot: Rc<RefCell<Value>>,
306    steps: Vec<Rc<str>>,
307}
308
309impl Place {
310    fn binding(value: Value) -> Place {
311        Place {
312            slot: Rc::new(RefCell::new(value)),
313            steps: Vec::new(),
314        }
315    }
316
317    fn field(&self, name: Rc<str>) -> Place {
318        let mut steps = self.steps.clone();
319        steps.push(name);
320        Place {
321            slot: self.slot.clone(),
322            steps,
323        }
324    }
325
326    fn with_ref<R>(&self, span: Span, f: impl FnOnce(&Value) -> R) -> Result<R, RuntimeError> {
327        let root = self.slot.borrow();
328        let mut current: &Value = &root;
329        for step in &self.steps {
330            match current {
331                Value(Repr::Struct(value)) => {
332                    current = value
333                        .get(step)
334                        .ok_or_else(|| no_field(&value.type_name, step, span))?;
335                }
336                other => return Err(not_a_struct(other, step, span)),
337            }
338        }
339        Ok(f(current))
340    }
341
342    fn with_mut<R>(&self, span: Span, f: impl FnOnce(&mut Value) -> R) -> Result<R, RuntimeError> {
343        let mut root = self.slot.borrow_mut();
344        let mut current: &mut Value = &mut root;
345        for step in &self.steps {
346            match current {
347                Value(Repr::Struct(value)) => {
348                    let type_name = value.type_name.clone();
349                    // The one place a struct's field is written, and so the
350                    // one place its shared storage becomes private again.
351                    // `make_mut` copies when another holder exists and does
352                    // nothing when none does, which is what makes sharing a
353                    // copied struct unobservable.
354                    current = Rc::make_mut(value)
355                        .get_mut(step)
356                        .ok_or_else(|| no_field(&type_name, step, span))?;
357                }
358                other => return Err(not_a_struct(other, step, span)),
359            }
360        }
361        Ok(f(current))
362    }
363
364    /// Reading a place clones: that is the value-semantics rule.
365    fn read(&self, span: Span) -> Result<Value, RuntimeError> {
366        self.with_ref(span, Value::clone)
367    }
368
369    fn write(&self, span: Span, value: Value) -> Result<(), RuntimeError> {
370        self.with_mut(span, |slot| *slot = value)
371    }
372}
373
374/// One lexical environment: the module a body resolves names in, the bindings
375/// the body received, and the bindings it has declared.
376///
377/// Every binding an environment declares is registered in its interpreter's
378/// [`SlotRoots`], and leaving a block — or dropping the whole environment when a
379/// call returns — truncates that list back to where the scope began. The
380/// collector's roots are therefore the environment chain itself, which is what
381/// ADR 0011 means by "the roots are the interpreter's own structures": there is
382/// no machine stack to map, because nothing a Cove binding names lives anywhere
383/// but here.
384///
385/// The list belongs to one interpreter, and ADR 0008 gives each task an
386/// interpreter of its own, so these are one task's roots and no others'.
387///
388/// # Why the bindings are one flat list and a block is a mark
389///
390/// A block scope used to be a vector of its own, allocated when the block was
391/// entered and dropped when it was left. It is now a mark into `frame`, and
392/// `frame` holds every binding this call has declared, in the order it
393/// declared them.
394///
395/// The two are the same thing to a lookup: scanning scopes in reverse and then
396/// each scope's bindings in reverse visits exactly the order scanning one flat
397/// list in reverse does, so a name finds the binding it always found. They are
398/// not the same thing to a call, which now enters and leaves a block without
399/// allocating — measured at 1.09× on `examples/cq`, and more than that on the
400/// call-heavy benchmarks, because a call enters a block for every one of them.
401///
402/// # Why captures are not in it
403///
404/// A closure receives its captures when it is created, and *how many* it
405/// receives is decided then too: only the names its body mentions and that
406/// were live get captured. So a capture's position is a run-time fact, and
407/// putting captures in `frame` would make every position after them one as
408/// well. They live in their own list, searched after `frame` and so found only
409/// when this call declared nothing of that name — which is the order they had
410/// when they were declared into the first scope ahead of the parameters.
411struct Env {
412    module: Rc<str>,
413    /// What a closure body was handed by the environment that created it, in
414    /// the order [`Env::captures`] produced them.
415    captures: Vec<(Rc<str>, Place)>,
416    /// Every binding this call has declared, in declaration order.
417    frame: Vec<(Rc<str>, Place)>,
418    /// One entry per open block scope: where it begins in `frame`, and where
419    /// it begins in `roots`.
420    marks: Vec<(usize, usize)>,
421    roots: Rc<RefCell<SlotRoots>>,
422    /// Where this environment's own bindings begin in `roots`.
423    base: usize,
424}
425
426impl Env {
427    fn new(module: Rc<str>, roots: Rc<RefCell<SlotRoots>>) -> Env {
428        let base = roots.borrow().len();
429        Env {
430            module,
431            captures: Vec::new(),
432            frame: Vec::new(),
433            marks: Vec::new(),
434            roots,
435            base,
436        }
437    }
438
439    fn push(&mut self) {
440        let roots_mark = self.roots.borrow().len();
441        self.marks.push((self.frame.len(), roots_mark));
442    }
443
444    fn pop(&mut self) {
445        if let Some((frame_mark, roots_mark)) = self.marks.pop() {
446            self.frame.truncate(frame_mark);
447            self.roots.borrow_mut().truncate(roots_mark);
448        }
449    }
450
451    /// Declares a binding this call made.
452    fn declare(&mut self, name: Rc<str>, place: Place) {
453        self.roots.borrow_mut().push(place.slot.clone());
454        self.frame.push((name, place));
455    }
456
457    /// Declares a binding this call was handed among a closure's captures.
458    ///
459    /// Separate from [`Env::declare`] only so that the two lists stay
460    /// separate; a capture is rooted exactly as anything else is.
461    fn declare_capture(&mut self, name: Rc<str>, place: Place) {
462        self.roots.borrow_mut().push(place.slot.clone());
463        self.captures.push((name, place));
464    }
465
466    fn lookup(&self, name: &str) -> Option<&Place> {
467        self.frame
468            .iter()
469            .rev()
470            .chain(self.captures.iter().rev())
471            .find(|(n, _)| &**n == name)
472            .map(|(_, place)| place)
473    }
474
475    /// The bindings a closure body can read, by value at creation time.
476    ///
477    /// Only names the body mentions are captured. What a closure holds is
478    /// therefore what actually has to cross a task boundary when the closure
479    /// is spawned, rather than every binding that happened to be in scope.
480    ///
481    /// The walk is outermost first, so a name declared twice leaves the
482    /// innermost value in the capture — and captures are visited before this
483    /// call's own bindings for the same reason they are searched after them.
484    fn captures(
485        &self,
486        mentioned: &BTreeSet<String>,
487        span: Span,
488    ) -> Result<Vec<(Rc<str>, Value)>, RuntimeError> {
489        let mut captured: Vec<(Rc<str>, Value)> = Vec::new();
490        for (name, place) in self.captures.iter().chain(self.frame.iter()) {
491            if !mentioned.contains(&**name) {
492                continue;
493            }
494            let value = place.read(span)?;
495            match captured.iter_mut().find(|(n, _)| n == name) {
496                Some(slot) => slot.1 = value,
497                None => captured.push((name.clone(), value)),
498            }
499        }
500        Ok(captured)
501    }
502}
503
504/// An environment's bindings leave the root set with the environment, so a
505/// call that returns — by any path, including an error — takes its own
506/// bindings out of the collector's reach at the same moment the program loses
507/// them.
508impl Drop for Env {
509    fn drop(&mut self) {
510        self.roots.borrow_mut().truncate(self.base);
511    }
512}
513
514/// An argument that has been evaluated, in call-site order.
515struct EvaluatedArg {
516    label: Option<Rc<str>>,
517    spread: bool,
518    slot: ArgSlot,
519    span: Span,
520}
521
522/// Ordinary arguments pass a value; `var` arguments pass the caller's place.
523enum ArgSlot {
524    Value(Value),
525    Alias(Place),
526}
527
528/// The body a call is about to enter.
529struct Target<'t> {
530    name: &'t str,
531    params: &'t [Param],
532    body: &'t Block,
533    module: Rc<str>,
534    receiver: Option<Receiver>,
535    is_async: bool,
536    captures: &'t [(Rc<str>, Value)],
537    /// The written return type, when there is one. A `dyn Trait` in it is
538    /// what tells the interpreter to wrap the result; a lambda writes no
539    /// return type, so it never converts.
540    return_type: Option<&'t Type>,
541}
542
543/// The interpreter's half of what a task needs, which is what makes `spawn`,
544/// `await`, and leaving a scope one implementation rather than two.
545///
546/// Each of the four is a field this evaluator already had for its own
547/// reasons, exposed rather than duplicated: ADR 0008 gives every task an
548/// evaluator of its own, so "the run this belongs to" and "the task this is
549/// running" are questions any evaluator can answer.
550impl Tasking for Interpreter<'_> {
551    fn runtime(&self) -> &Runtime {
552        self.runtime
553    }
554
555    fn hosts(&self) -> &HostRegistry {
556        self.hosts
557    }
558
559    fn charge_wait(&mut self, wait: Duration) {
560        Interpreter::charge_wait(self, wait);
561    }
562
563    fn running_task(&self) -> Option<u64> {
564        self.task_stack.last().copied()
565    }
566}
567
568/// Executes a resolved program.
569///
570/// One interpreter runs one body on one thread: the entry, or the body of a
571/// spawned task. Everything shared with the rest of the run is reached
572/// through the [`Runtime`] it borrows, which is what a `spawn` hands to the
573/// thread it starts.
574///
575/// # Ownership of the run's [`crate::budget::Budget`]
576///
577/// The `Budget` is owned by the [`HostRegistry`] this interpreter borrows,
578/// not by `Interpreter` itself: a host installs it once with
579/// `HostRegistry::set_budget`, and every task thread charges that one budget
580/// at its own safepoints, through a [`crate::budget::Meter`] taken from it
581/// where the run begins. ADR 0008 draws a task's fuel from the run's budget,
582/// so there is exactly one authoritative count of what the run spent,
583/// whichever thread spent it. Call depth is the exception and is counted
584/// here, because a task has a stack of its own.
585pub struct Interpreter<'a> {
586    pub program: &'a Program,
587    pub sources: &'a SourceMap,
588    pub hosts: &'a HostRegistry,
589    /// What every thread of this run shares, so a `spawn` can hand a task
590    /// thread everything it needs to run a body.
591    runtime: &'a Runtime,
592    depth: usize,
593    /// The call-site span of every live call, outermost first — pushed and
594    /// popped in `call_target` exactly where `depth` is, one entry per
595    /// level, so the two can never disagree about how deep the call is.
596    ///
597    /// This evaluator has no frame stack for a `RuntimeError` to read the way
598    /// the linear-memory backend's does — issue #258 is exactly that gap —
599    /// so it keeps this instead. Reading it happens at the one place an
600    /// error can still see the frame that is failing: inside `call_target`,
601    /// right where `depth` would otherwise be decremented, before the entry
602    /// this level pushed is popped. Everything but the outermost entry
603    /// becomes [`RuntimeError::chain`]; the outermost is the entry's own
604    /// invocation, which — like [`crate::vm::exec::Machine::calls`]'s
605    /// innermost frame — names no caller and is excluded the same way.
606    call_sites: Vec<Span>,
607    /// The run's budget, as this interpreter's safepoints charge it.
608    ///
609    /// `None` is a run with no budget installed, which is what an embedder
610    /// that installed none has, and what it has always meant here: no limit.
611    ///
612    /// Taken once, where the run begins, rather than reached at each
613    /// safepoint through [`HostRegistry::with_budget`]'s mutex — which every
614    /// call, every back edge and every `await` used to take, and which issue
615    /// #182 measured at 36% of `benches/call` on the other backend.
616    /// [`crate::budget::Meter`] is where the argument for the shape is, and
617    /// [`Interpreter::bind_budget`] is where this is filled in.
618    budget: Option<Meter>,
619    /// The host's `max_call_depth`, read off the budget when it was bound.
620    ///
621    /// It cannot change while a run lasts — a budget is installed before a
622    /// run begins and the shape of `invoke_within` is what stops it being
623    /// replaced during one — so every call asking for it was every call
624    /// taking the budget's lock for an answer that could not have moved.
625    call_depth_limit: Option<usize>,
626    /// This task's own cancellation flag, when this interpreter is running a
627    /// spawned task's body rather than the entry.
628    ///
629    /// Cancelling the *run* is the budget's flag, which every safepoint
630    /// already observes through the shared budget. This is the second flag a
631    /// safepoint checks: it stops one task without stopping the run, which is
632    /// what leaving a scope early asks for.
633    cancellation: Option<Cancellation>,
634    /// Flags raised by a host call that bounds the work it was given, one for
635    /// each such call this thread is inside.
636    ///
637    /// `clock.timeout` is the one that raises them. A safepoint checks these
638    /// beside the task's own flag, which is what makes a timeout stop the
639    /// body it bounds rather than measure it afterwards.
640    stops: Vec<Cancellation>,
641    /// How many host calls running a Cove callback this thread is currently
642    /// inside, which is what [`MAX_REENTRY_DEPTH`] bounds.
643    ///
644    /// Counted here rather than in the budget for the same reason `depth` is:
645    /// it measures one thread's native stack, and ADR 0008 gives each task a
646    /// stack of its own.
647    reentry_depth: usize,
648    /// Ids of the tasks whose bodies this thread is running, innermost last,
649    /// so a nested `spawn` can name its immediate parent.
650    task_stack: Vec<u64>,
651    /// Active timing contexts: one for the body this thread is running, and
652    /// one more for each nested context inside it. A host call's wait is
653    /// charged against every context on this stack. Each task thread has a
654    /// stack of its own, which is what makes one task's CPU work and
655    /// another's wait separately attributable.
656    timings: Vec<Timing>,
657    /// Every binding every live environment on this thread has declared, in
658    /// declaration order. This is the list a collection walks; see [`Env`] for
659    /// how it stays in step with the environment chain.
660    roots: Rc<RefCell<SlotRoots>>,
661    /// This task's heap.
662    ///
663    /// ADR 0011: a value belongs to one task or is immutable and shared, so a
664    /// task's objects are its own. ADR 0008 gives each task a thread, so this
665    /// heap is reached only from the thread that owns it: a collection needs
666    /// no safepoint from any other task and takes no lock.
667    heap: Heap,
668    /// The scratch key `find_method` compares against, kept so that looking a
669    /// method up does not allocate the pair it looks it up by.
670    ///
671    /// `Resolved::methods` is keyed by two owned strings, and a lookup needs
672    /// two to compare against. Building them was 3.4% of `examples/cq`'s run
673    /// (issue #104), all of it thrown away immediately; this pair is written
674    /// over and keeps its capacity instead. A `Cell` rather than a field,
675    /// because `find_method` takes `&self`.
676    method_key: std::cell::Cell<(String, String)>,
677    /// Where the most recent assertion failed, and the message it produced.
678    ///
679    /// A failed assertion is an ordinary `Err`, which carries a message and
680    /// no source position, and that is the right shape for the language: a
681    /// test propagates it with `?` like any other expected failure. The test
682    /// runner still wants to point at the assertion the way every other
683    /// error points at source, so the one party that saw the assertion —
684    /// this evaluator — records where it was. The message is kept alongside
685    /// so a caller can tell that the `Err` it is holding is that assertion's
686    /// and not some later, unrelated failure.
687    assertion_failure: Option<(Span, String)>,
688}
689
690impl<'a> Interpreter<'a> {
691    /// An interpreter for the entry of `runtime`'s run.
692    ///
693    /// The run's budget is bound here, which is the one lock this takes and
694    /// the last one a safepoint of this interpreter will be behind. It is
695    /// sound to bind it this early because a budget cannot be installed once
696    /// an interpreter exists: `HostRegistry::set_budget` needs
697    /// `&mut HostRegistry` and this borrows the registry shared for `'a`. The
698    /// one other way a budget is installed is `HostRegistry::begin_run`,
699    /// which is reached only through [`Interpreter::invoke_within`] and its
700    /// siblings, each of which rebinds.
701    pub fn new(runtime: &'a Runtime) -> Self {
702        let mut interpreter = Interpreter {
703            program: runtime.program(),
704            sources: runtime.sources(),
705            hosts: runtime.hosts(),
706            runtime,
707            depth: 0,
708            call_sites: Vec::new(),
709            budget: None,
710            call_depth_limit: None,
711            cancellation: None,
712            stops: Vec::new(),
713            reentry_depth: 0,
714            task_stack: Vec::new(),
715            timings: Vec::new(),
716            roots: Rc::new(RefCell::new(SlotRoots::new())),
717            heap: Heap::new(),
718            method_key: std::cell::Cell::new((String::new(), String::new())),
719            assertion_failure: None,
720        };
721        interpreter.bind_budget();
722        interpreter
723    }
724
725    /// Takes the run's budget, in the form every safepoint of this
726    /// interpreter will charge it, together with the call-depth limit that
727    /// comes off it.
728    ///
729    /// Called where a run begins and nowhere else, for the reason
730    /// [`crate::budget::Meter`] gives: a `Meter` names the accounting of the
731    /// run it was taken from, and `HostRegistry::begin_run` gives the budget
732    /// it installs fresh accounting. So [`Interpreter::new`] takes one, and
733    /// the two ways in that install a budget of their own take another
734    /// straight after installing it.
735    fn bind_budget(&mut self) {
736        self.budget = self.hosts.budget_meter();
737        self.call_depth_limit = self
738            .budget
739            .as_ref()
740            .and_then(|budget| budget.limits().max_call_depth);
741    }
742
743    /// What this run's heaps have done so far: allocation, collections, live
744    /// heap, peak live heap, and total pause.
745    ///
746    /// The counters come from every heap retired so far, folded into the
747    /// [`Runtime`] as each task's thread ended. The live figures come from
748    /// this interpreter's own heap, which at the end of a run is the only one
749    /// left: every task's heap went with its thread, and summing what those
750    /// last measured would report memory that no longer exists.
751    pub fn heap_stats(&self) -> HeapStats {
752        let mut stats = self.runtime.heap_stats();
753        let mine = self.heap.stats();
754        stats.live_bytes = mine.live_bytes;
755        stats.live_objects = mine.live_objects;
756        stats
757    }
758
759    /// Allocates growable vector storage in this task's heap.
760    ///
761    /// Every `Vector` a Cove program can reach is created here, which is what
762    /// makes the heap's table of objects complete.
763    pub fn allocate_vector(&mut self, elements: Vec<Value>) -> Value {
764        Value(Repr::Vector(self.heap.allocate(elements)))
765    }
766
767    /// The task this interpreter is running: the spawned task's id, or
768    /// [`ENTRY_TASK`] when it is running the entry.
769    ///
770    /// This is the one answer to "which task" that every event naming a task
771    /// is written from — the heap it collected, and the host call it made.
772    fn task_id(&self) -> u64 {
773        self.task_stack.last().copied().unwrap_or(ENTRY_TASK)
774    }
775
776    /// Marks and sweeps this task's heap, and records what it did.
777    ///
778    /// The interpreter calls this at safepoints; a host may call it directly
779    /// to observe the heap at a chosen moment.
780    pub fn collect(&mut self) -> Collection {
781        let roots = Rc::clone(&self.roots);
782        let collected = {
783            let roots = roots.borrow();
784            self.heap.collect(&*roots)
785        };
786        let task = self.task_id();
787        self.runtime.trace(TraceEvent::HeapCollected {
788            task,
789            allocated: collected.allocated,
790            freed: collected.freed_objects,
791            live_objects: collected.live_objects,
792            live_bytes: collected.live_bytes,
793            pause: collected.pause,
794        });
795        collected
796    }
797
798    /// Collects when enough has been allocated to be worth it.
799    fn collect_if_due(&mut self) {
800        if self.heap.should_collect() {
801            self.collect();
802        }
803    }
804
805    /// Ends this task's heap and folds what it did into the run's totals.
806    ///
807    /// One last collection runs first. A heap dies with the thread that owns
808    /// it, and a `Weak` table dropped without a sweep takes nothing with it —
809    /// so a task that ends while a cycle it built is still in scope would
810    /// leave that cycle behind, which is the one thing this collector exists
811    /// to prevent. By the time this runs, every environment on this thread
812    /// has dropped and the roots are empty, so the only thing left to survive
813    /// is what the value the task produced still holds; the reference counts
814    /// find that, as they find any other value the collector cannot read.
815    fn retire_heap(&mut self) {
816        if !self.heap.is_empty() {
817            self.collect();
818        }
819        let stats = self.heap.take_stats();
820        self.runtime.retire_heap(&stats);
821    }
822
823    /// Where the most recent failed assertion was written, together with the
824    /// message it produced, or `None` when no assertion has failed.
825    ///
826    /// A caller compares the message against the error it is reporting: an
827    /// assertion that failed and was then handled inside the program is not
828    /// the reason a later error was returned.
829    pub fn assertion_failure(&self) -> Option<(Span, &str)> {
830        self.assertion_failure
831            .as_ref()
832            .map(|(span, message)| (*span, message.as_str()))
833    }
834
835    /// The source text `span` covers, for a diagnostic that quotes the code
836    /// it is about.
837    fn source_text(&self, span: Span) -> &str {
838        source_text(self.sources, span)
839    }
840
841    /// An interpreter for the body of the spawned task `id`, which stops when
842    /// `cancellation` is raised.
843    fn for_task(runtime: &'a Runtime, id: u64, cancellation: Cancellation) -> Self {
844        let mut interpreter = Interpreter::new(runtime);
845        interpreter.cancellation = Some(cancellation);
846        interpreter.task_stack.push(id);
847        interpreter
848    }
849
850    /// Calls the host-selected entry function, and records how the run came
851    /// out.
852    ///
853    /// `args` are the process arguments; they are passed as an
854    /// `Array<String>` when the entry declares a parameter for them.
855    ///
856    /// Every path a *command* takes into a Cove program passes through here —
857    /// `cove run`, `cove test`, `cove generate`, `cove replay`, and a `cove
858    /// build` binary — because a command has strings to hand over and nothing
859    /// else. A host that has a value instead calls [`Interpreter::invoke`],
860    /// which is the same run with a different way in.
861    ///
862    /// It wraps `Interpreter::enter` rather than living inside it so that a
863    /// run that never reached its entry — one that named a function this
864    /// package does not declare, say — still ends with an event saying so.
865    ///
866    /// # Run this on a thread with at least [`STACK_SIZE`] bytes
867    ///
868    /// The interpreter is a recursive tree walker, so a Cove program spends
869    /// native stack as it nests calls, and `MAX_CALL_DEPTH` stops it before
870    /// that stack runs out. What "before" means depends on how much stack
871    /// there is. The runtime sizes every thread it creates itself, so a
872    /// spawned task and everything the toolchain runs are covered; a thread
873    /// an embedder created is the one it cannot size, and on it the limit is
874    /// only as good as the stack underneath.
875    ///
876    /// So an embedder calls this from inside [`on_cove_stack`], building the
877    /// interpreter there too. A [`Value`] is `Rc`-based and cannot cross a
878    /// thread boundary in either direction, so the whole run happens inside
879    /// the closure and only what the embedder wants to keep comes back:
880    ///
881    /// ```no_run
882    /// # use cove_runtime::interp::Interpreter;
883    /// # use cove_runtime::Runtime;
884    /// # fn example(runtime: Runtime) -> Result<(), String> {
885    /// let failure: Option<String> = cove_runtime::on_cove_stack(|| {
886    ///     Interpreter::new(&runtime)
887    ///         .run_entry("app", "main", Vec::new())
888    ///         .err()
889    ///         .map(|error| error.message)
890    /// })
891    /// .map_err(|e| format!("no thread to run Cove on: {e}"))?;
892    /// # let _ = failure;
893    /// # Ok(())
894    /// # }
895    /// ```
896    ///
897    /// An embedder that would rather manage the thread itself gives it
898    /// `.stack_size(cove_runtime::STACK_SIZE)` and builds the interpreter
899    /// inside it, which is the same arrangement by hand.
900    ///
901    /// On a smaller stack than that, a deep enough Cove program ends the
902    /// process with a stack overflow instead of returning the depth limit as
903    /// an error. That is a boundary of what this runtime can promise rather
904    /// than a bug in it: the size of a thread somebody else created is not
905    /// something the interpreter can read or change.
906    pub fn run_entry(
907        &mut self,
908        module: &str,
909        name: &str,
910        args: Vec<Rc<str>>,
911    ) -> Result<Value, RuntimeError> {
912        let outcome = self.enter(module, name, args);
913        self.ended(outcome)
914    }
915
916    /// Calls `module.name` with the arguments `args`, and records how the run
917    /// came out.
918    ///
919    /// This is the other public way into a Cove program, and the one
920    /// [`Interpreter::run_entry`] is not: an entry takes the process
921    /// arguments, which are strings, so `run_entry` is how a *command* speaks
922    /// to a program and this is how an *application* does. A rule engine's
923    /// `evaluate(pr: PullRequest) -> Decision` is invoked here with a
924    /// [`Value`] the host built, and answers the `Decision` the host reads —
925    /// which the entry's result already allowed, so this is the way in
926    /// catching up with the way out. See issue #150.
927    ///
928    /// [`Vm::invoke`](crate::Vm::invoke) takes the same three things and
929    /// answers the same way, exactly as the two `run_entry`s do.
930    ///
931    /// # What holds the arguments to anything
932    ///
933    /// Everything the checker settled, and nothing else. Before the first
934    /// instruction runs:
935    ///
936    /// - the declaration must be one a host can call at all — no type
937    ///   parameter, no `var` parameter, no variadic one;
938    /// - `args` must be exactly as long as the declared parameter list, a
939    ///   parameter with a default included;
940    /// - each value must be one its declared type admits, followed as deeply
941    ///   as the type goes, with a nominal type checked by the name the value
942    ///   carries.
943    ///
944    /// A capability is *not* checked here, because an invocation grants
945    /// nothing: what the called function may reach is what the
946    /// [`HostRegistry`] it runs against was granted, exactly as for any other
947    /// run.
948    ///
949    /// Every one of those refusals is a [`RuntimeError`] carrying the rule it
950    /// broke, the span of the parameter it was about, and the signature the
951    /// checker resolved.
952    ///
953    /// # Run this on a thread with at least [`STACK_SIZE`] bytes
954    ///
955    /// For the reason [`Interpreter::run_entry`] gives, and in the same way.
956    pub fn invoke(
957        &mut self,
958        module: &str,
959        name: &str,
960        args: Vec<Value>,
961    ) -> Result<Value, RuntimeError> {
962        let outcome = self.invoke_checked(module, name, args);
963        self.ended(outcome)
964    }
965
966    /// The same call, bounded by `budget` and by nothing else.
967    ///
968    /// # What a budget belongs to
969    ///
970    /// A [`Budget`] used to belong to the [`HostRegistry`]: `set_budget` needs
971    /// `&mut HostRegistry`, a backend holds the registry by shared reference
972    /// for as long as it exists, and so every limit it carried — `fuel`, the
973    /// deadline, `max_host_calls`, `max_tasks` — was spent over the whole life
974    /// of the backend. For a `cove run` that is exactly right, because a run
975    /// is one invocation and `[run.<name>]`'s limits bound it. For an
976    /// embedding it is not: compile-once/invoke-many is the point, an
977    /// application wants to bound one *request*, and the only way to get that
978    /// was to build a registry, a `Runtime` and a backend per request — which
979    /// is 168 allocations of table-building against a request's own 237, and
980    /// is the thing compiling once was for not doing.
981    ///
982    /// A budget belongs to an invocation. It still *lives* on the registry,
983    /// because ADR 0008 draws a spawned task's fuel from the run's budget and
984    /// a task thread reaches the budget through the `Arc<Runtime>` it carries;
985    /// a task's charges are still the invocation's. What this changes is when
986    /// it is put there and how long it stands: `budget` is installed as this
987    /// call is entered, bounds everything the invocation and its tasks do, and
988    /// is left behind afterwards holding what the invocation spent — the same
989    /// state a finished `cove run` leaves and reads its `--stats` out of. The
990    /// next `invoke_within` replaces it.
991    ///
992    /// **The deadline runs from here**, not from wherever `budget` was built.
993    /// A budget built to bound an invocation that has not begun would
994    /// otherwise spend it waiting for its turn. Every count starts at zero for
995    /// the same reason. A [`Cancellation`] is the one thing not reset: a
996    /// caller that wants to stop this invocation from another thread builds
997    /// the budget with
998    /// [`Budget::with_cancellation`](crate::Budget::with_cancellation) and
999    /// keeps the handle, and a flag already raised stays raised.
1000    ///
1001    /// # Why this takes `&mut self` and there is no way to install a budget
1002    /// that does not
1003    ///
1004    /// ADR 0024 states each way a run can be stopped as a bound that holds
1005    /// over the run, in that backend's own fuel. A budget that could be
1006    /// replaced while the run it bounds was executing would make every one of
1007    /// those bounds a claim about something that had changed underneath it,
1008    /// and the ADR's argument would have to be revisited to say what a bound
1009    /// even meant. So the registry has no public way to install one: this and
1010    /// its three siblings are the only doors, each takes `&mut self` on the
1011    /// backend, and a backend running an invocation is mutably borrowed for
1012    /// its whole duration. The shape is what forbids it rather than a rule in
1013    /// a comment.
1014    ///
1015    /// Everything [`Interpreter::invoke`] says about what holds the arguments
1016    /// holds here unchanged, and so does the refusal: the argument check runs
1017    /// before the budget is installed, so a call refused for a wrong argument
1018    /// spends none of it.
1019    pub fn invoke_within(
1020        &mut self,
1021        budget: Budget,
1022        module: &str,
1023        name: &str,
1024        args: Vec<Value>,
1025    ) -> Result<Value, RuntimeError> {
1026        crate::invoke::check(self.program, module, name, &args)?;
1027        self.hosts().begin_run(budget);
1028        self.bind_budget();
1029        let outcome = self.enter_with(module, name, args);
1030        self.ended(outcome)
1031    }
1032
1033    /// [`Interpreter::run_entry`], bounded by `budget` and by nothing else.
1034    ///
1035    /// The command-shaped way in, bounded the way
1036    /// [`Interpreter::invoke_within`] bounds the application-shaped one, and
1037    /// that method's documentation is the description of both.
1038    pub fn run_entry_within(
1039        &mut self,
1040        budget: Budget,
1041        module: &str,
1042        name: &str,
1043        args: Vec<Rc<str>>,
1044    ) -> Result<Value, RuntimeError> {
1045        self.hosts().begin_run(budget);
1046        self.bind_budget();
1047        let outcome = self.enter(module, name, args);
1048        self.ended(outcome)
1049    }
1050
1051    /// The check, and then the call.
1052    fn invoke_checked(
1053        &mut self,
1054        module: &str,
1055        name: &str,
1056        args: Vec<Value>,
1057    ) -> Result<Value, RuntimeError> {
1058        crate::invoke::check(self.program, module, name, &args)?;
1059        self.enter_with(module, name, args)
1060    }
1061
1062    /// Writes a run's terminal event, whichever way in produced it.
1063    ///
1064    /// Every path into a Cove program passes through here, which is what makes
1065    /// "every run has one" true rather than a claim about the paths somebody
1066    /// remembered.
1067    fn ended(&self, outcome: Result<Value, RuntimeError>) -> Result<Value, RuntimeError> {
1068        let (classification, message) = match &outcome {
1069            // Cove's entry returns `Result<Unit, Error>`, so an `Err` is the
1070            // program saying what it was written to say. It is a failure of
1071            // the program's work and not of the run, which is why it is its
1072            // own outcome rather than one more kind of stop.
1073            Ok(value) if value.is_err() => (RunOutcome::Error, returned_error_message(value)),
1074            Ok(_) => (RunOutcome::Success, None),
1075            Err(error) => (error.outcome, Some(error.message.clone())),
1076        };
1077        self.runtime.trace(TraceEvent::RunEnded {
1078            outcome: classification,
1079            message,
1080        });
1081        outcome
1082    }
1083
1084    /// The process arguments as the one value an entry may take them as.
1085    ///
1086    /// The entry-shape rule is the language's and not a backend's, so this and
1087    /// [`crate::Vm`]'s copy of it refuse in the same words.
1088    fn enter(
1089        &mut self,
1090        module: &str,
1091        name: &str,
1092        args: Vec<Rc<str>>,
1093    ) -> Result<Value, RuntimeError> {
1094        let entry = self.program.lookup_fn(module, name).ok_or_else(|| {
1095            RuntimeError::new(format!("this package does not declare `{module}.{name}`"))
1096        })?;
1097        let decl = entry.decl.clone();
1098        let span = decl.span;
1099
1100        let arguments = match decl.params.len() {
1101            0 => Vec::new(),
1102            1 => vec![Value(Repr::Array(
1103                args.into_iter().map(Value::string).collect(),
1104            ))],
1105            other => {
1106                return Err(RuntimeError::new(format!(
1107                    "entry `{module}.{name}` declares {other} parameters"
1108                ))
1109                .at(span)
1110                .with_rule(
1111                    "An entry function takes either no parameters or one `Array<String>` of process arguments.",
1112                )
1113                .with_help(format!(
1114                    "write `fn {name}()` or `fn {name}(args: Array<String>)`"
1115                )));
1116            }
1117        };
1118        self.enter_with(module, name, arguments)
1119    }
1120
1121    /// The call itself, from looking the declaration up to retiring the last
1122    /// heap.
1123    ///
1124    /// The one seam. `run_entry` reaches it having turned the process
1125    /// arguments into the array an entry declares, and [`Interpreter::invoke`]
1126    /// reaches it having held a host's own values to what the checker
1127    /// resolved; nothing below this line knows which of the two happened.
1128    fn enter_with(
1129        &mut self,
1130        module: &str,
1131        name: &str,
1132        args: Vec<Value>,
1133    ) -> Result<Value, RuntimeError> {
1134        let entry = self.program.lookup_fn(module, name).ok_or_else(|| {
1135            RuntimeError::new(format!("this package does not declare `{module}.{name}`"))
1136        })?;
1137        let decl = entry.decl.clone();
1138        let span = decl.span;
1139        let arguments: Vec<EvaluatedArg> = args
1140            .into_iter()
1141            .enumerate()
1142            .map(|(position, value)| EvaluatedArg {
1143                label: None,
1144                spread: false,
1145                slot: ArgSlot::Value(value),
1146                // A host's argument was written nowhere, so what a diagnostic
1147                // about it points at is the parameter it was given to.
1148                span: decl.params.get(position).map_or(span, |param| param.span),
1149            })
1150            .collect();
1151
1152        self.runtime.trace(TraceEvent::EntryEnter {
1153            module: module.to_string(),
1154            function: name.to_string(),
1155        });
1156        self.timings.push(Timing::start());
1157
1158        let outcome = self
1159            .call_target(
1160                &Target {
1161                    name,
1162                    params: &decl.params,
1163                    body: &decl.body,
1164                    module: module.into(),
1165                    receiver: decl.receiver,
1166                    is_async: decl.is_async,
1167                    captures: &[],
1168                    return_type: decl.return_type.as_ref(),
1169                },
1170                None,
1171                arguments,
1172                span,
1173            )
1174            .and_then(|value| match value {
1175                // The host awaits the entry it chose, so an `async fn` entry
1176                // hands back its value rather than a handle the host cannot
1177                // settle.
1178                Value(Repr::Task(task)) => self.settle(&task, span),
1179                value => Ok(value),
1180            });
1181
1182        let timing = self
1183            .timings
1184            .pop()
1185            .expect("an entry pushes exactly the one timing it pops");
1186        self.runtime.trace(TraceEvent::EntryExit {
1187            module: module.to_string(),
1188            function: name.to_string(),
1189            cpu: timing.cpu(),
1190            wait: timing.wait(),
1191        });
1192        // Every task's thread has been joined by now — leaving a scope waits
1193        // for or cancels its children — so every heap but this one has been
1194        // retired and the totals are complete.
1195        self.retire_heap();
1196        let heap = self.heap_stats();
1197        // The object half of the event and none of the word half: this heap
1198        // is a set of `Rc`-ed objects and counts objects, and issue #240's
1199        // rule is that a machine leaves `None` in what it does not count
1200        // rather than a zero that reads as a measurement.
1201        self.runtime.trace(TraceEvent::HeapSummary {
1202            collections: heap.collections,
1203            object_count: Some(heap.allocated_objects),
1204            allocated_bytes: Some(heap.allocated_bytes),
1205            live_bytes: Some(heap.live_bytes),
1206            peak_bytes: Some(heap.peak_bytes),
1207            pause: Some(heap.pause),
1208            allocated_words: None,
1209            capacity_words: None,
1210            live_words: None,
1211        });
1212
1213        outcome
1214    }
1215
1216    fn resolved(&self, module: &str) -> Option<&'a ResolvedModule> {
1217        self.program.modules.get(module)
1218    }
1219
1220    /// Resolves `name` as module `module` sees it, to the module that
1221    /// declares it and whatever `select` finds there.
1222    ///
1223    /// A module's own declaration answers first; failing that, the
1224    /// declaration a `use` imported under that name does. Which module
1225    /// answers matters beyond the declaration itself: a body runs in the
1226    /// module that declares it, so an imported function resolves its own
1227    /// names where it was written, not where it was called.
1228    fn find_declared<T>(
1229        &self,
1230        module: &str,
1231        name: &str,
1232        select: impl Fn(&'a ResolvedModule, &str) -> Option<T>,
1233    ) -> Option<(Rc<str>, T)> {
1234        let resolved = self.resolved(module)?;
1235        if let Some(found) = select(resolved, name) {
1236            return Some((module.into(), found));
1237        }
1238        let owner_name = resolved.imports.get(name)?;
1239        let owner = self.resolved(owner_name)?;
1240        select(owner, name).map(|found| (owner_name.as_str().into(), found))
1241    }
1242
1243    fn find_function(&self, module: &str, name: &str) -> Option<(Rc<str>, Arc<FnDecl>)> {
1244        self.find_declared(module, name, |resolved, name| {
1245            Some(resolved.functions.get(name)?.decl.clone())
1246        })
1247    }
1248
1249    /// The method `type_module.type_name` answers to, and the module whose
1250    /// body runs it.
1251    ///
1252    /// A type's methods usually live with the type. They do not have to: ADR
1253    /// 0006 allows `impl Trait for Type` in the module that declares the
1254    /// trait as well as the one that declares the type, so a conformance
1255    /// written elsewhere puts a method for this type in that other module.
1256    /// The orphan rule bounds the search — only a module declaring one of
1257    /// the two parties can have it — and the conformance itself says which
1258    /// module to look in.
1259    fn find_method(
1260        &self,
1261        type_module: &str,
1262        type_name: &str,
1263        name: &str,
1264    ) -> Option<(Rc<str>, Arc<FnDecl>)> {
1265        // The map is keyed by a pair of owned strings, so a lookup needs a
1266        // pair to compare against. Building one allocated twice on every
1267        // method call a program made, which was 3.4% of `examples/cq`'s run;
1268        // this reuses one pair and keeps its capacity, so the allocation
1269        // happens once for the whole interpreter rather than once per call
1270        // (issue #104).
1271        let mut key = self.method_key.take();
1272        key.0.clear();
1273        key.0.push_str(type_name);
1274        key.1.clear();
1275        key.1.push_str(name);
1276
1277        let found = self
1278            .resolved(type_module)
1279            .and_then(|m| m.methods.get(&key))
1280            .map(|entry| (Rc::from(type_module), entry.decl.clone()))
1281            .or_else(|| {
1282                self.program.modules.iter().find_map(|(module, resolved)| {
1283                    let conforms = resolved.conformances.values().any(|conformance| {
1284                        conformance.type_module == type_module
1285                            && conformance.type_name == type_name
1286                            && conformance.methods.contains(name)
1287                    });
1288                    if !conforms {
1289                        return None;
1290                    }
1291                    let entry = resolved.methods.get(&key)?;
1292                    Some((Rc::from(module.as_str()), entry.decl.clone()))
1293                })
1294            });
1295
1296        self.method_key.set(key);
1297        found
1298    }
1299
1300    fn find_struct(&self, module: &str, name: &str) -> Option<(Rc<str>, Arc<StructDecl>)> {
1301        self.find_declared(module, name, |resolved, name| {
1302            Some(resolved.structs.get(name)?.decl.clone())
1303        })
1304    }
1305
1306    fn find_enum(&self, module: &str, name: &str) -> Option<(Rc<str>, Arc<EnumDecl>)> {
1307        self.find_declared(module, name, |resolved, name| {
1308            Some(resolved.enums.get(name)?.decl.clone())
1309        })
1310    }
1311
1312    /// The module that declares the trait `name` as `module` sees it: itself
1313    /// when it declares the trait, and the module a `use` imported it from
1314    /// otherwise.
1315    fn declaring_module(&self, module: &str, name: &str) -> Option<Rc<str>> {
1316        let resolved = self.resolved(module)?;
1317        if resolved.traits.contains_key(name) {
1318            return Some(module.into());
1319        }
1320        let owner = resolved.imports.get(name)?;
1321        self.resolved(owner)?
1322            .traits
1323            .contains_key(name)
1324            .then(|| owner.as_str().into())
1325    }
1326
1327    /// The module `head` names in `module`, when `use` imported it whole.
1328    fn imported_module(&self, module: &str, head: &str) -> Option<Rc<str>> {
1329        Some(
1330            self.resolved(module)?
1331                .module_imports
1332                .get(head)?
1333                .as_str()
1334                .into(),
1335        )
1336    }
1337
1338    /// The exported declaration `owner.name` reaches, when `owner` exports
1339    /// one.
1340    ///
1341    /// A module-private declaration is not reachable qualified, exactly as
1342    /// it is not importable: `export` is the whole of a module's boundary.
1343    fn find_exported<T>(
1344        &self,
1345        owner: &str,
1346        name: &str,
1347        select: impl Fn(&'a ResolvedModule) -> Option<T>,
1348    ) -> Option<T> {
1349        let resolved = self.resolved(owner)?;
1350        if resolved.exported(name) != Some(true) {
1351            return None;
1352        }
1353        select(resolved)
1354    }
1355
1356    /// The exported function of `owner` named `name`.
1357    fn exported_function(&self, owner: &str, name: &str) -> Option<Arc<FnDecl>> {
1358        self.find_exported(owner, name, |resolved| {
1359            Some(resolved.functions.get(name)?.decl.clone())
1360        })
1361    }
1362
1363    /// `owner.name` as a value: an exported function is an ordinary handle,
1364    /// and an exported struct or enum is the type used as a value, exactly
1365    /// as a bare name for either would be.
1366    fn module_member(&self, owner: &str, name: &str, span: Span) -> Eval {
1367        if let Some(decl) = self.exported_function(owner, name) {
1368            return Ok(declared_as_value(owner.into(), decl));
1369        }
1370        if self
1371            .find_exported(owner, name, |resolved| {
1372                resolved
1373                    .structs
1374                    .contains_key(name)
1375                    .then_some(())
1376                    .or_else(|| resolved.enums.contains_key(name).then_some(()))
1377            })
1378            .is_some()
1379        {
1380            return Ok(Value(Repr::Type(format!("{owner}.{name}").into())));
1381        }
1382        Err(self.no_export(owner, name, span).into())
1383    }
1384
1385    /// Reports a qualified name that no export of `owner` answers, naming
1386    /// the module-private declaration when that is what went wrong.
1387    fn no_export(&self, owner: &str, name: &str, span: Span) -> RuntimeError {
1388        let exported = self.resolved(owner).map(|resolved| resolved.exported(name));
1389        match exported {
1390            Some(Some(false)) => RuntimeError::new(format!(
1391                "`{name}` is declared by module `{owner}`, but is not exported"
1392            ))
1393            .at(span)
1394            .with_rule("An `export` declaration is public; other declarations are module-private.")
1395            .with_help(format!("write `export` on `{name}` in module `{owner}`")),
1396            _ => RuntimeError::new(format!("module `{owner}` declares no `{name}`"))
1397                .at(span)
1398                .with_help(match self.resolved(owner) {
1399                    Some(resolved) if !resolved.exports().is_empty() => {
1400                        format!("module `{owner}` exports {}", resolved.exports().join(", "))
1401                    }
1402                    _ => format!("module `{owner}` exports nothing"),
1403                }),
1404        }
1405    }
1406
1407    /// Whether `name` is a host module this module may address by name.
1408    fn is_host_module(&self, module: &str, name: &str) -> bool {
1409        self.resolved(module)
1410            .map(|m| m.host_uses.contains(name))
1411            .unwrap_or(false)
1412            || self.hosts.contains(name)
1413    }
1414
1415    /// The host module an unqualified `use console.println` import names.
1416    fn host_item(&self, module: &str, name: &str) -> Option<Rc<str>> {
1417        self.resolved(module)?
1418            .host_items
1419            .get(name)
1420            .map(|m| m.as_str().into())
1421    }
1422
1423    // ------------------------------------------------------------- budget
1424
1425    /// Charges [`SAFEPOINT_FUEL`] and checks the deadline and cancellation
1426    /// flag, at a loop back edge, a function call, or an `await`.
1427    ///
1428    /// A stop surfaces as the ordinary [`RuntimeError`] `Budget` already
1429    /// produces, pointing at `span` — the loop, call, or await that hit the
1430    /// limit. It is not a Cove-level `Result`: like any other `RuntimeError`
1431    /// it propagates through `Control::Error` and cannot be caught by `?` or
1432    /// `match` in Cove source, so it terminates the run rather than failing
1433    /// one function of it.
1434    fn charge_safepoint(&mut self, span: Span) -> Result<(), RuntimeError> {
1435        stopped_here(self.cancellation.as_ref(), &self.stops, span)?;
1436        if let Some(budget) = &self.budget {
1437            if let Err(stopped) = budget.safepoint(SAFEPOINT_FUEL) {
1438                return Err(budget.to_runtime_error(stopped).at(span));
1439            }
1440        }
1441        self.collect_if_due();
1442        Ok(())
1443    }
1444
1445    /// Records `wait` against every active [`Timing`] context, so a trace can
1446    /// separate the work a body did from the time it spent waiting for
1447    /// something else to finish — a host call, or a task.
1448    fn charge_wait(&mut self, wait: Duration) {
1449        for timing in &mut self.timings {
1450            timing.add_wait(wait);
1451        }
1452    }
1453
1454    /// Dispatches a host call and records its wait against every active
1455    /// [`Timing`] context, so `EntryExit` and `TaskCompleted` can separate
1456    /// CPU work from time spent waiting on the host.
1457    ///
1458    /// # Why there is no fuel flush here
1459    ///
1460    /// [ADR 0030](../../../docs/adr/0030-a-host-call-asks-the-fuel-limit.md)
1461    /// decides that no Host call begins once the fuel a run has been charged
1462    /// has reached its limit, and the periodic safepoint the linear-memory
1463    /// backend runs every [`crate::SAFEPOINT_STRIDE`] instructions is what
1464    /// makes that true there, at the granularity fuel is charged at on that
1465    /// backend. This one needs nothing,
1466    /// and could do nothing: [`Interpreter::charge_safepoint`] hands
1467    /// [`SAFEPOINT_FUEL`] to the shared budget in the same call that charges
1468    /// it, so there is never a charge standing between two safepoints and the
1469    /// run's charged total cannot move while a straight line runs. A
1470    /// safepoint that reaches the limit stops the run there; nothing after it
1471    /// is dispatched.
1472    ///
1473    /// What that costs is the other half of ADR 0024, which ADR 0030 leaves
1474    /// standing: a straight line reaches no safepoint on this backend at all,
1475    /// so a limit that lets a body in lets every Host call in it through.
1476    /// The property is the same sentence on both backends and the number it
1477    /// admits is not, which is why a fuel limit is not portable between them
1478    /// and why `max_host_calls` is the control that bounds effects exactly.
1479    fn call_host(
1480        &mut self,
1481        module: &str,
1482        op: &str,
1483        values: Vec<Value>,
1484        span: Span,
1485    ) -> Result<Value, RuntimeError> {
1486        stopped_here(self.cancellation.as_ref(), &self.stops, span)?;
1487        let hosts = self.hosts;
1488        let started = Instant::now();
1489        let result = hosts.call_with(
1490            module,
1491            op,
1492            values,
1493            &mut Callback {
1494                interpreter: self,
1495                span,
1496            },
1497        );
1498        self.charge_wait(started.elapsed());
1499        result.map_err(|e| e.at(span))
1500    }
1501
1502    /// Dispatches an operation on a resource handle, through the same
1503    /// boundary and with the same accounting as any other host call.
1504    fn call_host_resource(
1505        &mut self,
1506        handle: &ResourceHandle,
1507        op: &str,
1508        values: Vec<Value>,
1509        span: Span,
1510    ) -> Result<Value, RuntimeError> {
1511        stopped_here(self.cancellation.as_ref(), &self.stops, span)?;
1512        let hosts = self.hosts;
1513        let started = Instant::now();
1514        let result = hosts.call_resource(
1515            handle,
1516            op,
1517            values,
1518            &mut Callback {
1519                interpreter: self,
1520                span,
1521            },
1522        );
1523        self.charge_wait(started.elapsed());
1524        result.map_err(|e| e.at(span))
1525    }
1526
1527    /// Builds one value of a type a host module declares.
1528    ///
1529    /// A host type is ordinary data, so this is [`Interpreter::init_struct`]
1530    /// with the fields read from a schema instead of from a declaration: the
1531    /// labels are checked the same way and the value that comes out is an
1532    /// ordinary struct whose type name is qualified by the module.
1533    fn init_host_type(
1534        &mut self,
1535        module: &str,
1536        declared: TypeSchema,
1537        args: Vec<EvaluatedArg>,
1538        span: Span,
1539    ) -> Result<Value, RuntimeError> {
1540        if declared.is_enum() {
1541            return Err(RuntimeError::new(format!(
1542                "`{module}.{}` is an enum, not a function",
1543                declared.name
1544            ))
1545            .at(span)
1546            .with_help(format!(
1547                "name a case, such as `{module}.{}.{}`",
1548                declared.name, declared.cases[0]
1549            )));
1550        }
1551        let names: Vec<&str> = declared.fields.iter().map(|field| field.name).collect();
1552        let (mut slots, _) = assign_labels(&names, args, declared.name, false)?;
1553        let mut fields = Vec::with_capacity(declared.fields.len());
1554        for (index, field) in declared.fields.iter().enumerate() {
1555            let Some(arg) = slots[index].take() else {
1556                return Err(RuntimeError::new(format!(
1557                    "`{module}.{}` needs a value for field `{}`",
1558                    declared.name, field.name
1559                ))
1560                .at(span)
1561                .with_rule("Struct initialization is a synthesized labeled call.")
1562                .with_help(format!(
1563                    "the Host API schema declares `{module}.{}`",
1564                    declared.initializer()
1565                )));
1566            };
1567            fields.push((field.name.into(), value_of(&arg, field.name, arg.span)?));
1568        }
1569        Ok(Value(Repr::Struct(Rc::new(StructValue {
1570            type_name: format!("{module}.{}", declared.name).into(),
1571            fields,
1572            opaque: false,
1573        }))))
1574    }
1575
1576    /// One case of an enum a host module declares.
1577    fn host_enum_case(
1578        &self,
1579        module: &str,
1580        declared: &TypeSchema,
1581        case: &str,
1582        span: Span,
1583    ) -> Result<Value, RuntimeError> {
1584        host_enum_case(module, declared, case, span)
1585    }
1586}
1587
1588/// `http.Method.Get`: a case of an enum a host declares.
1589///
1590/// A host's enum has a [`TypeSchema`] rather than an `EnumDecl`, so
1591/// [`enum_case`] cannot serve it: there is no declaration to read a case's
1592/// payload arity from, and a host's cases carry none. Both backends reach
1593/// this one function for the same reason they reach [`enum_case`] — a case
1594/// the schema does not name has to fail in the same words whichever backend
1595/// asked.
1596pub(crate) fn host_enum_case(
1597    module: &str,
1598    declared: &TypeSchema,
1599    case: &str,
1600    span: Span,
1601) -> Result<Value, RuntimeError> {
1602    {
1603        if !declared.cases.contains(&case) {
1604            return Err(RuntimeError::new(format!(
1605                "host type `{module}.{}` has no case `{case}`",
1606                declared.name
1607            ))
1608            .at(span)
1609            .with_help(format!("known cases: {}", declared.cases.join(", "))));
1610        }
1611        Ok(Value(Repr::Enum(Box::new(EnumValue {
1612            type_name: format!("{module}.{}", declared.name).into(),
1613            case: case.into(),
1614            payload: crate::value::Payload::Empty,
1615        }))))
1616    }
1617}
1618
1619impl<'a> Interpreter<'a> {
1620    // ---------------------------------------------------------------- calls
1621
1622    fn call_target(
1623        &mut self,
1624        target: &Target<'_>,
1625        receiver: Option<ArgSlot>,
1626        args: Vec<EvaluatedArg>,
1627        span: Span,
1628    ) -> Result<Value, RuntimeError> {
1629        if self.depth >= MAX_CALL_DEPTH {
1630            return Err(RuntimeError::new(format!(
1631                "call depth limit of {MAX_CALL_DEPTH} reached while calling `{}`",
1632                target.name
1633            ))
1634            .at(span)
1635            .with_rule("Recursion depth is a runtime control, not a proof obligation."));
1636        }
1637
1638        // A host-configured `max_call_depth` bounds one stack, and ADR 0008
1639        // gives each task a stack of its own, so it is checked against this
1640        // interpreter's own depth rather than against a count shared with
1641        // every other task: a shallow task must not be stopped because a
1642        // sibling is deep.
1643        let depth = self.depth + 1;
1644        if let Some(limit) = self.call_depth_limit {
1645            if depth > limit {
1646                // There is a budget: the limit was read off one. The error
1647                // names the value it was configured with, which is why it is
1648                // built there rather than here.
1649                if let Some(budget) = &self.budget {
1650                    return Err(budget.to_runtime_error(Stopped::CallDepth).at(span));
1651                }
1652            }
1653        }
1654        // Every call is also a safepoint, so the fuel charge counts the call
1655        // itself.
1656        self.charge_safepoint(span)?;
1657
1658        self.depth += 1;
1659        self.call_sites.push(span);
1660        let result = self
1661            .invoke_body(target, receiver, args, span)
1662            .map_err(|error| self.attach_call_chain(error));
1663        self.call_sites.pop();
1664        self.depth -= 1;
1665        if target.is_async {
1666            // An `async fn` is called like any other function and produces a
1667            // task, so its value is reachable only through `await`.
1668            //
1669            // The body runs here, at the call, and the handle it returns is
1670            // already settled. ADR 0008 gives a thread to `spawn`, which is
1671            // where the language says concurrency begins; nothing may depend
1672            // on when an `async fn` body ran, only on the value `await`
1673            // produces, so a body that is never awaited has still run.
1674            return Ok(Value(Repr::Task(Task::settled(result?))));
1675        }
1676        result
1677    }
1678
1679    /// Attaches this interpreter's call chain to `error`, innermost first.
1680    ///
1681    /// Read here and nowhere else: this runs inside `call_target`, right
1682    /// after `invoke_body` returns and before the entry it pushed for this
1683    /// level is popped, which is the one moment an error can still see the
1684    /// frame that raised it. `RuntimeError::with_chain` is a no-op once a
1685    /// chain is attached, which is what makes calling this at every level on
1686    /// the way out safe: the innermost `call_target` to see the error is the
1687    /// only one whose call is still un-popped, and every level further out
1688    /// finds a chain already there.
1689    ///
1690    /// `self.call_sites[0]` is excluded — it is this run's entry being
1691    /// called, which names no caller, the same way
1692    /// [`crate::vm::exec::Machine::calls`]'s innermost frame names none.
1693    /// Everything above it is a real call site, read outermost-last so the
1694    /// chain comes out innermost-first, the order [`RuntimeError::with_chain`]
1695    /// bounds and [`RuntimeError::to_diagnostic`] renders in.
1696    fn attach_call_chain(&self, error: RuntimeError) -> RuntimeError {
1697        error.with_chain(self.call_sites[1..].iter().rev().copied())
1698    }
1699
1700    fn invoke_body(
1701        &mut self,
1702        target: &Target<'_>,
1703        receiver: Option<ArgSlot>,
1704        args: Vec<EvaluatedArg>,
1705        span: Span,
1706    ) -> Result<Value, RuntimeError> {
1707        let mut env = Env::new(target.module.clone(), Rc::clone(&self.roots));
1708        for (name, value) in target.captures {
1709            env.declare_capture(name.clone(), Place::binding(value.clone()));
1710        }
1711
1712        match (target.receiver, receiver) {
1713            (Some(_), Some(slot)) => {
1714                let place = match slot {
1715                    ArgSlot::Alias(place) => place,
1716                    ArgSlot::Value(value) => Place::binding(value),
1717                };
1718                env.declare("self".into(), place);
1719            }
1720            (Some(_), None) => {
1721                return Err(RuntimeError::new(format!(
1722                    "`{}` is a method and needs a receiver",
1723                    target.name
1724                ))
1725                .at(span));
1726            }
1727            (None, Some(_)) => {
1728                return Err(
1729                    RuntimeError::new(format!("`{}` takes no receiver", target.name)).at(span),
1730                );
1731            }
1732            (None, None) => {}
1733        }
1734
1735        self.bind_params(&mut env, target.params, args, target.name, span)?;
1736        let value = finish(self.eval_block(&mut env, target.body))?;
1737        Ok(match target.return_type {
1738            Some(ty) => self.coerce(&target.module, value, ty),
1739            None => value,
1740        })
1741    }
1742
1743    /// Converts `value` to the written type `ty`, which today means exactly
1744    /// one thing: wrapping a concrete value as a `dyn Trait` value where a
1745    /// `dyn Trait` is written.
1746    ///
1747    /// This is the only implicit conversion in the language, and it happens
1748    /// where a type is *written*: a parameter, an annotated `let`, a struct
1749    /// field, and a declared return type. The checker has already decided the
1750    /// conversion is legal, so this only builds the representation. It walks
1751    /// into `Array<dyn Trait>` and `Option<dyn Trait>` because those are the
1752    /// forms whose elements are written as `dyn` too; every other generic
1753    /// argument is left alone, since a `Vector` is a shared handle whose
1754    /// elements cannot be rewritten behind its other aliases.
1755    fn coerce(&self, module: &str, value: Value, ty: &Type) -> Value {
1756        match &ty.kind {
1757            TypeKind::Dyn(trait_name) => {
1758                // A trait belongs to the module that declares it, which may
1759                // be one this module imported the trait from: a `dyn` value
1760                // built here must carry the same name a value built there
1761                // does, or the two would not compare equal.
1762                let qualified: Rc<str> = match self.declaring_module(module, &trait_name.node) {
1763                    Some(owner) => format!("{owner}.{}", trait_name.node).into(),
1764                    None => trait_name.node.as_str().into(),
1765                };
1766                as_dyn(value, &qualified)
1767            }
1768            TypeKind::Named { path, args } if args.len() == 1 => {
1769                let Some(head) = path.last() else {
1770                    return value;
1771                };
1772                match head.node.as_str() {
1773                    "Array" | "Option" => {
1774                        coerce_inside(value, |item| self.coerce(module, item, &args[0]))
1775                    }
1776                    _ => value,
1777                }
1778            }
1779            _ => value,
1780        }
1781    }
1782
1783    /// Binds a call's arguments into the frame it is filling, for a declared
1784    /// function, a method and a lambda alike.
1785    ///
1786    /// The variadic branch below is a declaration's. It used to be a
1787    /// lambda's too — one `bind_params` serves both, so a lambda's `...`
1788    /// gathered into an `Array` here while the checker typed the same
1789    /// parameter as its element type and said nothing, which is the
1790    /// divergence issue #168 records. Nothing is removed for that:
1791    /// `cove::type::variadic_lambda` refuses such a lambda now, so no
1792    /// checked program reaches this branch through one, and what the branch
1793    /// does for a declaration was never in question. Deleting it to say so
1794    /// would delete the rule this backend is the oracle for.
1795    fn bind_params(
1796        &mut self,
1797        env: &mut Env,
1798        params: &[Param],
1799        args: Vec<EvaluatedArg>,
1800        what: &str,
1801        span: Span,
1802    ) -> Result<(), RuntimeError> {
1803        let names: Vec<&str> = params.iter().map(|p| p.name.node.as_str()).collect();
1804        let variadic = params.last().map(|p| p.variadic).unwrap_or(false);
1805        let (mut slots, rest) = assign_labels(&names, args, what, variadic)?;
1806
1807        for (index, param) in params.iter().enumerate() {
1808            let name: Rc<str> = param.name.node.as_str().into();
1809            if param.variadic {
1810                let mut items = Vec::new();
1811                if let Some(arg) = slots[index].as_ref() {
1812                    items.push(value_of(arg, &param.name.node, span)?);
1813                }
1814                for arg in &rest {
1815                    match &arg.slot {
1816                        ArgSlot::Value(Value(Repr::Array(values))) if arg.spread => {
1817                            items.extend(values.iter().cloned());
1818                        }
1819                        ArgSlot::Value(Value(Repr::Vector(storage))) if arg.spread => {
1820                            items.extend(storage.elements.borrow().iter().cloned());
1821                        }
1822                        ArgSlot::Value(_) if arg.spread => {
1823                            return Err(builtins::spread_needs_a_sequence(arg.span));
1824                        }
1825                        _ => items.push(value_of(arg, &param.name.node, arg.span)?),
1826                    }
1827                }
1828                // A variadic parameter is an immutable `Array<T>` inside the body.
1829                env.declare(name, Place::binding(Value(Repr::Array(items.into()))));
1830                continue;
1831            }
1832
1833            match slots[index].take() {
1834                Some(arg) => match (param.is_var, arg.slot) {
1835                    (true, ArgSlot::Alias(place)) => env.declare(name, place),
1836                    (true, ArgSlot::Value(_)) => {
1837                        return Err(RuntimeError::new(format!(
1838                            "parameter `{}` of `{what}` is declared `var`, but the call site passes a value",
1839                            param.name.node
1840                        ))
1841                        .at(arg.span)
1842                        .with_rule(
1843                            "A `var` parameter is a non-escaping inout alias, marked at both the declaration and the call site.",
1844                        )
1845                        .with_help(format!("write `{what}(var {})`", param.name.node)));
1846                    }
1847                    (false, ArgSlot::Alias(_)) => {
1848                        return Err(RuntimeError::new(format!(
1849                            "parameter `{}` of `{what}` is not declared `var`, so `var` cannot be written at the call site",
1850                            param.name.node
1851                        ))
1852                        .at(arg.span)
1853                        .with_rule(
1854                            "A `var` parameter is a non-escaping inout alias, marked at both the declaration and the call site.",
1855                        ));
1856                    }
1857                    // An ordinary parameter receives a shallow copy and is a
1858                    // read-only place inside the body.
1859                    (false, ArgSlot::Value(value)) => {
1860                        let value = match &param.ty {
1861                            Some(ty) => self.coerce(&env.module, value, ty),
1862                            None => value,
1863                        };
1864                        env.declare(name, Place::binding(value));
1865                    }
1866                },
1867                None => match &param.default {
1868                    // Default arguments are evaluated by the callee.
1869                    Some(default) => {
1870                        let value = finish(self.eval(env, default))?;
1871                        let value = match &param.ty {
1872                            Some(ty) => self.coerce(&env.module, value, ty),
1873                            None => value,
1874                        };
1875                        env.declare(name, Place::binding(value));
1876                    }
1877                    None => {
1878                        return Err(RuntimeError::new(format!(
1879                            "`{what}` needs an argument for `{}`",
1880                            param.name.node
1881                        ))
1882                        .at(span));
1883                    }
1884                },
1885            }
1886        }
1887        Ok(())
1888    }
1889
1890    /// Calls a closure or a bound host operation held in a value.
1891    fn call_value_slots(
1892        &mut self,
1893        callee: Value,
1894        args: Vec<EvaluatedArg>,
1895        span: Span,
1896    ) -> Result<Value, RuntimeError> {
1897        match callee {
1898            Value(Repr::Closure(closure)) => {
1899                let module = closure.module.clone();
1900                // The oracle walks syntax, so a closure whose body is a
1901                // lowered function is one it cannot run. Nothing produces
1902                // that pairing today — a run has one backend, and the
1903                // linear-memory backend is the only party that builds a
1904                // lowered body — so this is said rather than approximated,
1905                // exactly as that backend says the reverse.
1906                let ClosureBody::Tree {
1907                    params,
1908                    block,
1909                    decl,
1910                } = &closure.body
1911                else {
1912                    return Err(RuntimeError::new(
1913                        "this closure was built by the VM, and the interpreter runs syntax",
1914                    )
1915                    .at(span)
1916                    .with_rule(
1917                        "A run has one backend, and a closure belongs to the run that made it.",
1918                    ));
1919                };
1920                self.call_target(
1921                    &Target {
1922                        name: "this closure",
1923                        params,
1924                        body: block,
1925                        module,
1926                        receiver: None,
1927                        is_async: closure.is_async,
1928                        captures: &closure.captures,
1929                        return_type: decl.as_ref().and_then(|decl| decl.return_type.as_ref()),
1930                    },
1931                    None,
1932                    args,
1933                    span,
1934                )
1935            }
1936            Value(Repr::HostFn(host)) => {
1937                let values = plain_values(args, &format!("{}.{}", host.module, host.op))?;
1938                self.call_host(&host.module, &host.op, values, span)
1939            }
1940            other => {
1941                Err(RuntimeError::new(format!("`{}` is not callable", other.type_name())).at(span))
1942            }
1943        }
1944    }
1945
1946    // ---------------------------------------------------------- statements
1947
1948    fn eval_block(&mut self, env: &mut Env, block: &Block) -> Eval {
1949        env.push();
1950        let result = self.eval_block_body(env, block);
1951        env.pop();
1952        result
1953    }
1954
1955    fn eval_block_body(&mut self, env: &mut Env, block: &Block) -> Eval {
1956        for stmt in &block.statements {
1957            match &stmt.kind {
1958                StmtKind::Let {
1959                    name, ty, value, ..
1960                } => {
1961                    let value = self.eval(env, value)?;
1962                    let value = match ty {
1963                        Some(ty) => self.coerce(&env.module, value, ty),
1964                        None => value,
1965                    };
1966                    env.declare(name.node.as_str().into(), Place::binding(value));
1967                }
1968                StmtKind::Expr(expr) => {
1969                    self.eval(env, expr)?;
1970                }
1971                StmtKind::Item(item) => match &item.kind {
1972                    ItemKind::Fn(decl) => {
1973                        let closure = self.make_closure(
1974                            env,
1975                            decl.is_async,
1976                            decl.params.clone(),
1977                            decl.body.clone(),
1978                            stmt.span,
1979                        )?;
1980                        env.declare(decl.name.node.as_str().into(), Place::binding(closure));
1981                    }
1982                    _ => {
1983                        return Err(unsupported(
1984                            "declaring a type inside a function body",
1985                            stmt.span,
1986                        )
1987                        .into())
1988                    }
1989                },
1990            }
1991        }
1992        match &block.tail {
1993            Some(tail) => self.eval(env, tail),
1994            None => Ok(Value(Repr::Unit)),
1995        }
1996    }
1997
1998    // --------------------------------------------------------- expressions
1999
2000    fn eval(&mut self, env: &mut Env, expr: &Expr) -> Eval {
2001        let span = expr.span;
2002        match &expr.kind {
2003            ExprKind::Int(value) => Ok(Value(Repr::Int(*value))),
2004            ExprKind::Float(value) => Ok(Value(Repr::Float(*value))),
2005            ExprKind::Bool(value) => Ok(Value(Repr::Bool(*value))),
2006            ExprKind::Duration(value) => Ok(Value(Repr::Duration(*value))),
2007            ExprKind::Unit => Ok(Value(Repr::Unit)),
2008            ExprKind::Str(parts) => {
2009                let mut text = String::new();
2010                for part in parts {
2011                    match part {
2012                        StrPart::Text(literal) => text.push_str(literal),
2013                        StrPart::Interpolation(expr) => {
2014                            let value = self.eval(env, expr)?;
2015                            text.push_str(&value.to_string());
2016                        }
2017                    }
2018                }
2019                Ok(Value(Repr::Str(text.into())))
2020            }
2021            ExprKind::Ident(name) => self.eval_ident(env, name, span),
2022            ExprKind::ArrayLit(items) => {
2023                let mut values = Vec::with_capacity(items.len());
2024                for item in items {
2025                    values.push(self.eval(env, item)?);
2026                }
2027                Ok(Value(Repr::Array(values.into())))
2028            }
2029            ExprKind::Field { base, name } => self.eval_field(env, base, &name.node, span),
2030            ExprKind::Call {
2031                callee,
2032                generics: _,
2033                args,
2034                trailing,
2035            } => self.eval_call(env, callee, args, trailing.as_deref(), span),
2036            ExprKind::Unary { op, operand } => {
2037                let value = self.eval(env, operand)?;
2038                Ok(unary(*op, value, span)?)
2039            }
2040            ExprKind::Binary { op, lhs, rhs } => match op {
2041                // `&&` and `||` short-circuit; everything else is left to right.
2042                BinaryOp::And | BinaryOp::Or => {
2043                    let left = expect_bool(self.eval(env, lhs)?, *op, span)?;
2044                    if (*op == BinaryOp::And && !left) || (*op == BinaryOp::Or && left) {
2045                        return Ok(Value(Repr::Bool(left)));
2046                    }
2047                    let right = expect_bool(self.eval(env, rhs)?, *op, span)?;
2048                    Ok(Value(Repr::Bool(right)))
2049                }
2050                _ => {
2051                    let left = self.eval(env, lhs)?;
2052                    let right = self.eval(env, rhs)?;
2053                    Ok(binary(*op, left, right, span)?)
2054                }
2055            },
2056            ExprKind::Assign { op, target, value } => {
2057                // That the place is a writable one is `cove-sema`'s to say
2058                // and it has said it: ADR 0021 makes an assignment to a
2059                // read-only place a check-time error, and this is the
2060                // refusal that went with it.
2061                let place = self.resolve_place(env, target)?;
2062                let new_value = match op {
2063                    None => self.eval(env, value)?,
2064                    Some(op) => {
2065                        let current = place.read(span)?;
2066                        let rhs = self.eval(env, value)?;
2067                        binary(*op, current, rhs, span)?
2068                    }
2069                };
2070                place.write(span, new_value)?;
2071                Ok(Value(Repr::Unit))
2072            }
2073            ExprKind::Try(inner) => {
2074                let value = self.eval(env, inner)?;
2075                match &value {
2076                    Value(Repr::Enum(result)) if &*result.type_name == RESULT.name => {
2077                        match value.ok_payload() {
2078                            Some(payload) => {
2079                                Ok(payload.first().cloned().unwrap_or(Value(Repr::Unit)))
2080                            }
2081                            None => Err(Control::Return(value)),
2082                        }
2083                    }
2084                    Value(Repr::Enum(option)) if &*option.type_name == OPTION.name => {
2085                        match value.some_payload() {
2086                            Some(payload) => {
2087                                Ok(payload.first().cloned().unwrap_or(Value(Repr::Unit)))
2088                            }
2089                            None => Err(Control::Return(Value::none())),
2090                        }
2091                    }
2092                    other => {
2093                        let error = RuntimeError::new(format!(
2094                            "`?` needs a `Result` or an `Option`, but found `{}`",
2095                            other.type_name()
2096                        ))
2097                        .at(span)
2098                        .with_rule("`expr?` returns the error from the current function.");
2099                        // A task's value is observable only through `await`,
2100                        // so `?` cannot reach the `Result` inside one.
2101                        Err(match other {
2102                            Value(Repr::Task(_)) => {
2103                                error.with_help("settle the task first, as in `task.await()?`")
2104                            }
2105                            _ => error,
2106                        }
2107                        .into())
2108                    }
2109                }
2110            }
2111            ExprKind::Await(inner) => {
2112                let value = self.eval(env, inner)?;
2113                self.charge_safepoint(span)?;
2114                Ok(self.settle_value(value, span)?)
2115            }
2116            ExprKind::Scope { name, body } => self.eval_scope(env, name, body),
2117            ExprKind::Block(block) => self.eval_block(env, block),
2118            ExprKind::If {
2119                condition,
2120                then_branch,
2121                else_branch,
2122            } => {
2123                let test = self.eval(env, condition)?;
2124                let Value(Repr::Bool(test)) = test else {
2125                    return Err(RuntimeError::new(format!(
2126                        "an `if` condition must be a `Bool`, but found `{}`",
2127                        test.type_name()
2128                    ))
2129                    .at(condition.span)
2130                    .with_rule("There are no implicit boolean conversions.")
2131                    .into());
2132                };
2133                if test {
2134                    let value = self.eval_block(env, then_branch)?;
2135                    // An `if` with no `else` produces `()`. There is no
2136                    // second branch to give the missing case a value, so the
2137                    // branch that ran does not get to supply one either:
2138                    // the same expression would otherwise mean one thing to
2139                    // the checker and another here.
2140                    Ok(match else_branch {
2141                        Some(_) => value,
2142                        None => Value(Repr::Unit),
2143                    })
2144                } else {
2145                    match else_branch {
2146                        Some(branch) => self.eval(env, branch),
2147                        None => Ok(Value(Repr::Unit)),
2148                    }
2149                }
2150            }
2151            ExprKind::Match { scrutinee, arms } => {
2152                let value = self.eval(env, scrutinee)?;
2153                for arm in arms {
2154                    env.push();
2155                    let matched = self.match_pattern(env, &arm.pattern, &value);
2156                    match matched {
2157                        Ok(true) => {
2158                            let result = self.eval(env, &arm.body);
2159                            env.pop();
2160                            return result;
2161                        }
2162                        Ok(false) => env.pop(),
2163                        Err(error) => {
2164                            env.pop();
2165                            return Err(error);
2166                        }
2167                    }
2168                }
2169                Err(no_match(&value, span).into())
2170            }
2171            ExprKind::For {
2172                binding,
2173                iterable,
2174                body,
2175            } => {
2176                let items = self.iterable_items(env, iterable)?;
2177                for item in items {
2178                    // Once per iteration, at the back edge: this is the
2179                    // safepoint that bounds a `for` over an unbounded
2180                    // iterable, since Cove does not prove termination.
2181                    self.charge_safepoint(span)?;
2182                    env.push();
2183                    env.declare(binding.node.as_str().into(), Place::binding(item));
2184                    let result = self.eval_block(env, body);
2185                    env.pop();
2186                    match result {
2187                        Ok(_) => {}
2188                        // A `for` runs out of items, so it can reach its end
2189                        // without breaking and there is nothing there to
2190                        // produce but `()`. Its value is therefore `()`
2191                        // however it leaves, and a `break` operand is
2192                        // evaluated for its effects alone -- the same rule
2193                        // an `if` with no `else` follows. Permanently so:
2194                        // issue #87 decided it.
2195                        Err(Control::Break) => break,
2196                        Err(Control::Continue) => continue,
2197                        Err(other) => return Err(other),
2198                    }
2199                }
2200                Ok(Value(Repr::Unit))
2201            }
2202            ExprKind::While { condition, body } => loop {
2203                let test = self.eval(env, condition)?;
2204                let Value(Repr::Bool(test)) = test else {
2205                    return Err(RuntimeError::new(format!(
2206                        "a `while` condition must be a `Bool`, but found `{}`",
2207                        test.type_name()
2208                    ))
2209                    .at(condition.span)
2210                    .into());
2211                };
2212                if !test {
2213                    return Ok(Value(Repr::Unit));
2214                }
2215                // Once per iteration, at the back edge: this is the
2216                // safepoint that bounds a non-terminating `while`, which is
2217                // otherwise unbounded by anything the type system proves.
2218                self.charge_safepoint(span)?;
2219                match self.eval_block(env, body) {
2220                    Ok(_) => {}
2221                    // A `while` can reach its end without breaking, so it is
2222                    // `()` however it leaves and a `break` operand is
2223                    // evaluated for its effects alone. `while true` is no
2224                    // exception: nothing about the condition makes it a
2225                    // different form. Permanently so: issue #87 decided it.
2226                    Err(Control::Break) => return Ok(Value(Repr::Unit)),
2227                    Err(Control::Continue) => continue,
2228                    Err(other) => return Err(other),
2229                }
2230            },
2231            ExprKind::Return(value) => {
2232                let value = match value {
2233                    Some(expr) => self.eval(env, expr)?,
2234                    None => Value(Repr::Unit),
2235                };
2236                Err(Control::Return(value))
2237            }
2238            ExprKind::Break(value) => {
2239                // The operand is evaluated here, for its effects, and its
2240                // value is discarded: the loop it leaves is `()` however it
2241                // leaves, so there is nowhere for a value to go.
2242                if let Some(expr) = value {
2243                    self.eval(env, expr)?;
2244                }
2245                Err(Control::Break)
2246            }
2247            ExprKind::Continue => Err(Control::Continue),
2248            ExprKind::Lambda {
2249                is_async,
2250                params,
2251                body,
2252            } => self
2253                .make_closure(env, *is_async, params.clone(), body.clone(), span)
2254                .map_err(Control::from),
2255            // A range is an ordinary value, so it evaluates like any other
2256            // expression and `for` simply iterates the value it produces.
2257            ExprKind::Range {
2258                start,
2259                end,
2260                inclusive_end,
2261            } => {
2262                let start = expect_int(self.eval(env, start)?, "a range bound", span)?;
2263                let end = expect_int(self.eval(env, end)?, "a range bound", span)?;
2264                Ok(Value(Repr::Range {
2265                    start,
2266                    end,
2267                    inclusive_end: *inclusive_end,
2268                }))
2269            }
2270        }
2271    }
2272
2273    fn make_closure(
2274        &mut self,
2275        env: &mut Env,
2276        is_async: bool,
2277        params: Vec<Param>,
2278        body: Block,
2279        span: Span,
2280    ) -> Result<Value, RuntimeError> {
2281        // Closures capture by value at creation time, like every other copy.
2282        let mut mentioned = BTreeSet::new();
2283        mention_block(&body, &mut mentioned);
2284        let captures = env.captures(&mentioned, span)?;
2285        Ok(Value(Repr::Closure(Rc::new(Closure {
2286            is_async,
2287            arity: params.len(),
2288            body: ClosureBody::Tree {
2289                params,
2290                block: Arc::new(body),
2291                decl: None,
2292            },
2293            module: env.module.clone(),
2294            captures,
2295        }))))
2296    }
2297
2298    // ---------------------------------------------------------------- tasks
2299
2300    /// Evaluates `scope name { ... }`.
2301    ///
2302    /// The Language Card's rule is the whole of this function: leaving the
2303    /// scope waits for or cancels its child tasks. The scope's value is the
2304    /// value of its block, so a scope is an expression like any other block.
2305    fn eval_scope(&mut self, env: &mut Env, name: &Ident, body: &Block) -> Eval {
2306        let scope = TaskScope::new(name.node.as_str().into());
2307        env.push();
2308        env.declare(
2309            name.node.as_str().into(),
2310            Place::binding(Value(Repr::TaskScope(scope.clone()))),
2311        );
2312        let result = self.eval_block(env, body);
2313        env.pop();
2314        let left = self.leave_scope(&scope, result);
2315        scope.close();
2316        left
2317    }
2318
2319    /// Waits for or cancels the children of a scope that is being left.
2320    ///
2321    /// The waiting, the order it happens in, and what a failed child does are
2322    /// [`crate::task::wait_for_children`]'s. Before ADR 0034 that was also
2323    /// the code the predecessor VM left a scope through; the linear-memory
2324    /// backend keeps its own version of the same rules in `crate::vm`
2325    /// instead, so what holds the two to the same answer now is the
2326    /// differential corpus rather than one shared function. What is here is
2327    /// the translation into this
2328    /// evaluator's own control flow: a child that answered `Err(error)`
2329    /// returns that error from the enclosing function, exactly as `?` would,
2330    /// and a child that raised propagates as itself. Either way the tasks
2331    /// still running are cancelled and waited for, as they are when the body
2332    /// itself leaves early through `return`, `?`, or an error.
2333    fn leave_scope(&mut self, scope: &Rc<TaskScope>, result: Eval) -> Eval {
2334        let value = match result {
2335            Ok(value) => value,
2336            early => {
2337                self.cancel_scope(scope);
2338                return early;
2339            }
2340        };
2341        match task::wait_for_children(self, scope) {
2342            None => Ok(value),
2343            Some(failure) => {
2344                self.cancel_scope(scope);
2345                Err(match failure {
2346                    ChildFailure::Returned(value) => Control::Return(value),
2347                    ChildFailure::Raised(error) => Control::Error(error),
2348                })
2349            }
2350        }
2351    }
2352
2353    /// Cancels every running child of `scope` and waits for it to stop.
2354    fn cancel_scope(&mut self, scope: &Rc<TaskScope>) {
2355        task::cancel_children(self, scope);
2356    }
2357
2358    /// Waits for a task's thread and returns the value its body produced.
2359    fn settle(&mut self, task: &Rc<Task>, span: Span) -> Result<Value, RuntimeError> {
2360        task::settle(self, task, span)
2361    }
2362
2363    /// `await expr`, and the postfix `expr.await()` that means the same thing.
2364    fn settle_value(&mut self, value: Value, span: Span) -> Result<Value, RuntimeError> {
2365        match value {
2366            Value(Repr::Task(task)) => self.settle(&task, span),
2367            other => Err(RuntimeError::new(format!(
2368                "`await` needs a task, but found `{}`",
2369                other.type_name()
2370            ))
2371            .at(span)
2372            .with_rule(
2373                "`await` settles a task. Only a task spawned into a scope, or one returned by an `async fn`, has a value to settle.",
2374            )
2375            .with_help("call an `async fn`, or spawn the work into a task scope, and await that handle")),
2376        }
2377    }
2378
2379    /// `scope.spawn { ... }`, which starts a thread for the body.
2380    ///
2381    /// Everything a `spawn` decides — that the scope is still open, that the
2382    /// body is a closure, that every capture may cross, what the concurrency
2383    /// limit says, and what a trace records — is
2384    /// [`crate::task::spawn_into`]'s, and is therefore the same decision the
2385    /// VM's `spawn` reaches. What this contributes is the one thing that
2386    /// differs: the new thread runs an [`Interpreter`] of its own.
2387    fn spawn(
2388        &mut self,
2389        scope: &Rc<TaskScope>,
2390        body: Value,
2391        span: Span,
2392    ) -> Result<Value, RuntimeError> {
2393        task::spawn_into(self, scope, body, span, run_task)
2394    }
2395
2396    /// Dispatches the operations of a task scope and of a task handle.
2397    fn call_task_method(
2398        &mut self,
2399        env: &mut Env,
2400        receiver: Value,
2401        name: &str,
2402        args: &[Arg],
2403        trailing: Option<&Expr>,
2404        span: Span,
2405    ) -> Eval {
2406        let arguments = self.eval_args(env, args, trailing)?;
2407        let mut values = plain_values(arguments, name)?;
2408        match (&receiver, name) {
2409            (Value(Repr::TaskScope(scope)), "spawn") => {
2410                if values.len() != 1 {
2411                    return Err(RuntimeError::new(format!(
2412                        "`spawn` takes one trailing closure, but {} argument(s) were given",
2413                        values.len()
2414                    ))
2415                    .at(span)
2416                    .with_help(format!("write `{}.spawn {{ ... }}`", scope.name))
2417                    .into());
2418                }
2419                Ok(self.spawn(scope, values.remove(0), span)?)
2420            }
2421            (Value(Repr::Task(task)), "await") => {
2422                expect_no_arguments("await", &values, span)?;
2423                self.charge_safepoint(span)?;
2424                Ok(self.settle(task, span)?)
2425            }
2426            (Value(Repr::Task(task)), "cancel") => {
2427                expect_no_arguments("cancel", &values, span)?;
2428                // Asking is all this does. A cancelled task stops at its next
2429                // safepoint, and whether it stopped or had already finished is
2430                // known only once something waits for it — which is what
2431                // `await` and leaving the scope do, and where `TaskCancelled`
2432                // is traced.
2433                task.cancel();
2434                Ok(Value(Repr::Unit))
2435            }
2436            (_, "await") => {
2437                self.charge_safepoint(span)?;
2438                Ok(self.settle_value(receiver.clone(), span)?)
2439            }
2440            (other, _) => Err(RuntimeError::new(format!(
2441                "`{}` has no method `{name}`",
2442                other.type_name()
2443            ))
2444            .at(span)
2445            .into()),
2446        }
2447    }
2448
2449    /// Dispatches the one operation of a `Shared`: `lock`.
2450    ///
2451    /// There is no `get` and no `set`, by design. Every access is scoped, so
2452    /// a read-modify-write cannot be written as two operations that race;
2453    /// see [`crate::shared`].
2454    fn call_shared_method(
2455        &mut self,
2456        env: &mut Env,
2457        receiver: Value,
2458        name: &str,
2459        args: &[Arg],
2460        trailing: Option<&Expr>,
2461        span: Span,
2462    ) -> Eval {
2463        let Value(Repr::Shared(cell)) = receiver else {
2464            unreachable!("only a `Shared` receiver reaches this dispatch");
2465        };
2466        if name != "lock" {
2467            return Err(RuntimeError::new(format!("`Shared` has no method `{name}`"))
2468                .at(span)
2469                .with_rule(
2470                    "`lock` is a `Shared`'s only operation: every access to the value it holds is scoped, so there is no `get` and no `set`.",
2471                )
2472                .with_help("write `shared.lock(fn(var value) { ... })`")
2473                .into());
2474        }
2475        let arguments = self.eval_args(env, args, trailing)?;
2476        let mut values = plain_values(arguments, name)?;
2477        if values.len() != 1 {
2478            return Err(RuntimeError::new(format!(
2479                "`lock` takes one closure, but {} argument(s) were given",
2480                values.len()
2481            ))
2482            .at(span)
2483            .with_help("write `shared.lock(fn(var value) { ... })`")
2484            .into());
2485        }
2486        let body = values.remove(0);
2487        let Value(Repr::Closure(closure)) = &body else {
2488            return Err(RuntimeError::new(format!(
2489                "`lock` takes the work to run as a closure, but found `{}`",
2490                body.type_name()
2491            ))
2492            .at(span)
2493            .with_help("write `shared.lock(fn(var value) { ... })`")
2494            .into());
2495        };
2496        if closure.arity == 0 {
2497            return Err(RuntimeError::new(
2498                "`lock` gives the wrapped value to its closure, but this closure takes no parameter",
2499            )
2500            .at(span)
2501            .with_help("write `shared.lock(fn(var value) { ... })`")
2502            .into());
2503        }
2504        // A closure declaring `var` receives the wrapped value as an alias and
2505        // mutates it where it lies; one that does not receives a copy, exactly
2506        // as an ordinary parameter does anywhere else in the language.
2507        //
2508        // The parameter is the tree body's to state, which is why this reads
2509        // it there. The linear-memory backend never asks this question at
2510        // all: `cove_ir::lower`'s own `shared_lock` reads whether the
2511        // callback's first parameter is `var` at lowering time, while it is
2512        // still syntax, and lowers the call one way or the other. A closure
2513        // whose body is lowered cannot reach this evaluator to run in the
2514        // first place, so the `false` answered for `ClosureBody::Linear`
2515        // below is never acted on for real — it exists so this match is
2516        // exhaustive, and the refusal `call_value_slots` gives a moment
2517        // later is the words for a body this evaluator can never run.
2518        let wants_alias = match &closure.body {
2519            ClosureBody::Tree { params, .. } => params.first().is_some_and(|param| param.is_var),
2520            ClosureBody::Linear(_) => false,
2521        };
2522        Ok(cell.lock(span, |value| {
2523            let place = Place::binding(value);
2524            let slot = match wants_alias {
2525                true => ArgSlot::Alias(place.clone()),
2526                false => ArgSlot::Value(place.read(span)?),
2527            };
2528            let result = self.call_value_slots(
2529                body.clone(),
2530                vec![EvaluatedArg {
2531                    label: None,
2532                    spread: false,
2533                    slot,
2534                    span,
2535                }],
2536                span,
2537            )?;
2538            let updated = place.read(span)?;
2539            Ok((result, updated))
2540        })?)
2541    }
2542
2543    fn iterable_items(&mut self, env: &mut Env, expr: &Expr) -> Result<Vec<Value>, Control> {
2544        let value = self.eval(env, expr)?;
2545        Ok(items_of(value, expr.span)?)
2546    }
2547
2548    fn eval_ident(&mut self, env: &mut Env, name: &str, span: Span) -> Eval {
2549        if let Some(place) = env.lookup(name) {
2550            return Ok(place.read(span)?);
2551        }
2552        if name == NONE_CASE.name {
2553            return Ok(Value::none());
2554        }
2555        let module = env.module.clone();
2556        if let Some((owner, decl)) = self.find_function(&module, name) {
2557            return Ok(declared_as_value(owner, decl));
2558        }
2559        // A type is named by the module that declares it, wherever it is
2560        // written: two modules may each declare a `Config`, and a value has
2561        // to say which one it is.
2562        if let Some((owner, _)) = self.find_struct(&module, name) {
2563            return Ok(Value(Repr::Type(format!("{owner}.{name}").into())));
2564        }
2565        if let Some((owner, _)) = self.find_enum(&module, name) {
2566            return Ok(Value(Repr::Type(format!("{owner}.{name}").into())));
2567        }
2568        if builtins::is_builtin_type(name) {
2569            return Ok(Value(Repr::Type(name.into())));
2570        }
2571        if let Some(owner) = self.imported_module(&module, name) {
2572            return Err(RuntimeError::new(format!("`{name}` is a module, not a value"))
2573                .at(span)
2574                .with_rule(
2575                    "A module imported whole is a namespace; its exported declarations are the values.",
2576                )
2577                .with_help(format!(
2578                    "name one of its exports, such as `{name}.<declaration>`, or import the declaration with `use {owner}.<declaration>`"
2579                ))
2580                .into());
2581        }
2582        if self.is_host_module(&module, name) {
2583            return Ok(Value(Repr::HostModule(name.into())));
2584        }
2585        if let Some(host) = self.host_item(&module, name) {
2586            return Ok(Value(Repr::HostFn(Rc::new(HostFnValue {
2587                module: host,
2588                op: name.into(),
2589            }))));
2590        }
2591        Err(
2592            RuntimeError::new(format!("cannot find `{name}` in this scope"))
2593                .at(span)
2594                .into(),
2595        )
2596    }
2597
2598    fn eval_field(&mut self, env: &mut Env, base: &Expr, name: &str, span: Span) -> Eval {
2599        if let ExprKind::Ident(head) = &base.kind {
2600            if env.lookup(head).is_none() {
2601                let module = env.module.clone();
2602                if let Some((owner, decl)) = self.find_enum(&module, head) {
2603                    return Ok(self.enum_case(&owner, &decl, name, Vec::new(), span)?);
2604                }
2605                if self.is_host_module(&module, head) {
2606                    // `http.Method` names a type the host declares, while
2607                    // `http.fetch` names one of its operations. A type is not
2608                    // callable, so the two cannot be confused.
2609                    if self.hosts.host_type(head, name).is_some() {
2610                        return Ok(Value(Repr::Type(format!("{head}.{name}").into())));
2611                    }
2612                    return Ok(Value(Repr::HostFn(Rc::new(HostFnValue {
2613                        module: head.as_str().into(),
2614                        op: name.into(),
2615                    }))));
2616                }
2617                // `booking.create` and `booking.Status`: a module imported
2618                // whole answers with the exported declaration it names.
2619                if let Some(owner) = self.imported_module(&module, head) {
2620                    return self.module_member(&owner, name, span);
2621                }
2622            }
2623        }
2624
2625        let base_value = self.eval(env, base)?;
2626        match &base_value {
2627            Value(Repr::Struct(value)) => match value.get(name) {
2628                Some(field) => Ok(field.clone()),
2629                None => Err(no_field(&value.type_name, name, span).into()),
2630            },
2631            // `booking.Status.Confirmed`, once `booking.Status` named the
2632            // type: a case of an enum reached through its module.
2633            Value(Repr::Type(type_name)) => match type_name.rsplit_once('.') {
2634                Some((owner, short)) => match self.find_enum(owner, short) {
2635                    Some((owner, decl)) => {
2636                        Ok(self.enum_case(&owner, &decl, name, Vec::new(), span)?)
2637                    }
2638                    // `http.Method.Get`: a case of an enum a host declares.
2639                    None => match self.hosts.host_type(owner, short) {
2640                        Some(declared) => Ok(self.host_enum_case(owner, &declared, name, span)?),
2641                        None => Err(no_field(type_name, name, span).into()),
2642                    },
2643                },
2644                None => Err(no_field(type_name, name, span).into()),
2645            },
2646            Value(Repr::HostModule(module)) => match self.hosts.host_type(module, name) {
2647                Some(_) => Ok(Value(Repr::Type(format!("{module}.{name}").into()))),
2648                None => Ok(Value(Repr::HostFn(Rc::new(HostFnValue {
2649                    module: module.clone(),
2650                    op: name.into(),
2651                })))),
2652            },
2653            other => Err(RuntimeError::new(format!(
2654                "`{}` has no field `{name}`",
2655                other.type_name()
2656            ))
2657            .at(span)
2658            .into()),
2659        }
2660    }
2661
2662    /// Builds one case of an enum declared in `module`.
2663    fn enum_case(
2664        &mut self,
2665        module: &str,
2666        decl: &Arc<EnumDecl>,
2667        case: &str,
2668        mut payload: Vec<Value>,
2669        span: Span,
2670    ) -> Result<Value, RuntimeError> {
2671        enum_case(self.program, module, decl, case, &mut payload, span)
2672    }
2673
2674    // ---------------------------------------------------------------- calls
2675
2676    fn eval_call(
2677        &mut self,
2678        env: &mut Env,
2679        callee: &Expr,
2680        args: &[Arg],
2681        trailing: Option<&Expr>,
2682        span: Span,
2683    ) -> Eval {
2684        match &callee.kind {
2685            ExprKind::Ident(name) => {
2686                if let Some(place) = env.lookup(name) {
2687                    let value = place.read(span)?;
2688                    let args = self.eval_args(env, args, trailing)?;
2689                    return Ok(self.call_value_slots(value, args, span)?);
2690                }
2691                let module = env.module.clone();
2692                if let Some((owner, decl)) = self.find_function(&module, name) {
2693                    let args = self.eval_args(env, args, trailing)?;
2694                    return Ok(self.call_target(
2695                        &Target {
2696                            name,
2697                            params: &decl.params,
2698                            body: &decl.body,
2699                            module: owner,
2700                            receiver: decl.receiver,
2701                            is_async: decl.is_async,
2702                            captures: &[],
2703                            return_type: decl.return_type.as_ref(),
2704                        },
2705                        None,
2706                        args,
2707                        span,
2708                    )?);
2709                }
2710                if let Some((owner, decl)) = self.find_struct(&module, name) {
2711                    let args = self.eval_args(env, args, trailing)?;
2712                    return Ok(self.init_struct(&owner, &decl, args, span)?);
2713                }
2714                if self.find_enum(&module, name).is_some() {
2715                    return Err(
2716                        RuntimeError::new(format!("`{name}` is an enum, not a function"))
2717                            .at(span)
2718                            .with_help(format!("name a case, such as `{name}.Case(...)`"))
2719                            .into(),
2720                    );
2721                }
2722                if let Some(host) = self.host_item(&module, name) {
2723                    let args = self.eval_args(env, args, trailing)?;
2724                    let values = plain_values(args, name)?;
2725                    return Ok(self.call_host(&host, name, values, span)?);
2726                }
2727                if name == MAP_ENTRY.name {
2728                    let args = self.eval_args(env, args, trailing)?;
2729                    return Ok(init_map_entry(args, span)?);
2730                }
2731                // The builtins that are called on nothing, asked of the
2732                // shared table once: an assertion goes through the path that
2733                // keeps its arguments' source text, and a constructor
2734                // through the one that only needs their values.
2735                if let Some(schema) = builtins::free_builtin(name) {
2736                    return match schema.kind {
2737                        FreeBuiltinKind::Assertion => {
2738                            self.assertion(env, name, args, trailing, span)
2739                        }
2740                        FreeBuiltinKind::Constructor => {
2741                            let args = self.eval_args(env, args, trailing)?;
2742                            let mut values = plain_values(args, name)?;
2743                            Ok(builtins::call_constructor(name, &mut values, span)?)
2744                        }
2745                    };
2746                }
2747                if name == NONE_CASE.name {
2748                    return Err(RuntimeError::new("`None` is a value, not a call")
2749                        .at(span)
2750                        .with_help("write `None`")
2751                        .into());
2752                }
2753                Err(
2754                    RuntimeError::new(format!("cannot find `{name}` in this scope"))
2755                        .at(span)
2756                        .into(),
2757                )
2758            }
2759            ExprKind::Field { base, name } => {
2760                if let ExprKind::Ident(head) = &base.kind {
2761                    if env.lookup(head).is_none() {
2762                        let module = env.module.clone();
2763                        if self.is_host_module(&module, head) {
2764                            // `http.Route(method: ..., path: ...)` initializes
2765                            // a type the host declares; anything else is one
2766                            // of its operations.
2767                            if let Some(declared) = self.hosts.host_type(head, &name.node) {
2768                                let args = self.eval_args(env, args, trailing)?;
2769                                return Ok(self.init_host_type(head, declared, args, span)?);
2770                            }
2771                            let args = self.eval_args(env, args, trailing)?;
2772                            let values = plain_values(args, &format!("{head}.{}", name.node))?;
2773                            return Ok(self.call_host(head, &name.node, values, span)?);
2774                        }
2775                        if let Some((owner, enum_decl)) = self.find_enum(&module, head) {
2776                            // A case wins over an associated function of the
2777                            // same name, so naming a case never changes
2778                            // meaning when an `impl` block is added.
2779                            let is_case = enum_decl
2780                                .cases
2781                                .iter()
2782                                .any(|case| case.name.node == name.node);
2783                            if !is_case {
2784                                if let Some((declaring, decl)) =
2785                                    self.find_method(&owner, head, &name.node)
2786                                {
2787                                    let args = self.eval_args(env, args, trailing)?;
2788                                    return Ok(self.call_target(
2789                                        &Target {
2790                                            name: &name.node,
2791                                            params: &decl.params,
2792                                            body: &decl.body,
2793                                            module: declaring,
2794                                            receiver: decl.receiver,
2795                                            is_async: decl.is_async,
2796                                            captures: &[],
2797                                            return_type: decl.return_type.as_ref(),
2798                                        },
2799                                        None,
2800                                        args,
2801                                        span,
2802                                    )?);
2803                                }
2804                            }
2805                            let args = self.eval_args(env, args, trailing)?;
2806                            let values = plain_values(args, &format!("{head}.{}", name.node))?;
2807                            return Ok(
2808                                self.enum_case(&owner, &enum_decl, &name.node, values, span)?
2809                            );
2810                        }
2811                        if let Some((owner, _)) = self.find_struct(&module, head) {
2812                            if let Some((declaring, decl)) =
2813                                self.find_method(&owner, head, &name.node)
2814                            {
2815                                let args = self.eval_args(env, args, trailing)?;
2816                                return Ok(self.call_target(
2817                                    &Target {
2818                                        name: &name.node,
2819                                        params: &decl.params,
2820                                        body: &decl.body,
2821                                        module: declaring,
2822                                        receiver: decl.receiver,
2823                                        is_async: decl.is_async,
2824                                        captures: &[],
2825                                        return_type: decl.return_type.as_ref(),
2826                                    },
2827                                    None,
2828                                    args,
2829                                    span,
2830                                )?);
2831                            }
2832                        }
2833                        // `booking.create(...)`: a module imported whole is
2834                        // called through the declaration it exports.
2835                        if let Some(owner) = self.imported_module(&module, head) {
2836                            if let Some(decl) = self.exported_function(&owner, &name.node) {
2837                                let args = self.eval_args(env, args, trailing)?;
2838                                return Ok(self.call_target(
2839                                    &Target {
2840                                        name: &name.node,
2841                                        params: &decl.params,
2842                                        body: &decl.body,
2843                                        module: owner,
2844                                        receiver: decl.receiver,
2845                                        is_async: decl.is_async,
2846                                        captures: &[],
2847                                        return_type: decl.return_type.as_ref(),
2848                                    },
2849                                    None,
2850                                    args,
2851                                    span,
2852                                )?);
2853                            }
2854                            if let Some(decl) = self.find_exported(&owner, &name.node, |resolved| {
2855                                Some(resolved.structs.get(&name.node)?.decl.clone())
2856                            }) {
2857                                let args = self.eval_args(env, args, trailing)?;
2858                                return Ok(self.init_struct(&owner, &decl, args, span)?);
2859                            }
2860                            if self
2861                                .find_exported(&owner, &name.node, |resolved| {
2862                                    resolved.enums.get(&name.node)
2863                                })
2864                                .is_some()
2865                            {
2866                                return Err(RuntimeError::new(format!(
2867                                    "`{head}.{}` is an enum, not a function",
2868                                    name.node
2869                                ))
2870                                .at(span)
2871                                .with_help(format!(
2872                                    "name a case, such as `{head}.{}.Case(...)`",
2873                                    name.node
2874                                ))
2875                                .into());
2876                            }
2877                            return Err(self.no_export(&owner, &name.node, span).into());
2878                        }
2879                        if builtins::is_builtin_type(head) {
2880                            // An associated builtin function whose body has
2881                            // moved to the standard library — every
2882                            // `Duration` unit but `nanos` — is resolved
2883                            // first and generically, the same way
2884                            // `eval_method_call` resolves a method's
2885                            // binding before `builtins::call_method` is
2886                            // ever asked: `standard_associated_binding`
2887                            // is `standard_binding`'s counterpart for a
2888                            // call written on the type's own name rather
2889                            // than on a value.
2890                            if let Some(binding) =
2891                                cove_schema::builtins::standard_associated_binding(head, &name.node)
2892                            {
2893                                return self.call_std_associated_binding(
2894                                    env, binding, args, trailing, span,
2895                                );
2896                            }
2897                            let args = self.eval_args(env, args, trailing)?;
2898                            let mut values = plain_values(args, &format!("{head}.{}", name.node))?;
2899                            return Ok(builtins::call_associated(
2900                                self,
2901                                head,
2902                                &name.node,
2903                                &mut values,
2904                                span,
2905                            )?);
2906                        }
2907                    }
2908                }
2909                self.eval_method_call(env, base, &name.node, args, trailing, span)
2910            }
2911            _ => {
2912                let value = self.eval(env, callee)?;
2913                let args = self.eval_args(env, args, trailing)?;
2914                Ok(self.call_value_slots(value, args, span)?)
2915            }
2916        }
2917    }
2918
2919    /// `assert(condition)` and `assertEqual(actual, expected)`.
2920    ///
2921    /// The source text of each argument is read back out of the
2922    /// [`SourceMap`] with the expression's own span, so a failure message
2923    /// names the condition in the words the test was written in. That is
2924    /// what makes these builtins rather than library functions.
2925    fn assertion(
2926        &mut self,
2927        env: &mut Env,
2928        name: &str,
2929        args: &[Arg],
2930        trailing: Option<&Expr>,
2931        span: Span,
2932    ) -> Eval {
2933        let spans: Vec<Span> = args
2934            .iter()
2935            .map(|arg| arg.value.span)
2936            .chain(trailing.map(|expr| expr.span))
2937            .collect();
2938        let evaluated = self.eval_args(env, args, trailing)?;
2939        let mut values = plain_values(evaluated, name)?;
2940        let sources: Vec<&str> = spans.iter().map(|span| self.source_text(*span)).collect();
2941        let outcome = builtins::call_assertion(name, &mut values, &sources, span)?;
2942        if let Some(payload) = outcome.err_payload() {
2943            self.assertion_failure = Some((span, payload[0].to_string()));
2944        }
2945        Ok(outcome)
2946    }
2947
2948    fn eval_method_call(
2949        &mut self,
2950        env: &mut Env,
2951        receiver: &Expr,
2952        name: &str,
2953        args: &[Arg],
2954        trailing: Option<&Expr>,
2955        span: Span,
2956    ) -> Eval {
2957        // The receiver is evaluated before the arguments: evaluation is left
2958        // to right everywhere.
2959        let place = self.resolve_place_opt(env, receiver)?;
2960        let mut temporary = match &place {
2961            Some(_) => None,
2962            None => Some(self.eval(env, receiver)?),
2963        };
2964
2965        // Dynamic dispatch: a `dyn Trait` receiver is unwrapped to the
2966        // concrete value it carries, and the implementation is found from
2967        // *that* value's type. This is what makes the dispatch dynamic — the
2968        // static type says only which trait the method must come from.
2969        let mut place = place;
2970        let dispatch_from = match (&place, &temporary) {
2971            (Some(place), _) => place.with_ref(span, dyn_receiver)?,
2972            (_, Some(value)) => dyn_receiver(value),
2973            _ => None,
2974        };
2975        if let Some(concrete) = dispatch_from {
2976            place = None;
2977            temporary = Some(concrete);
2978        }
2979
2980        // The name of the declared type, when the receiver is one, because a
2981        // method a package declares can only be found on a struct or an enum.
2982        // A builtin receiver answers `None` and no name is built: `type_name`
2983        // returns an owned `String`, and building one on every method call to
2984        // discover that `Array` has no `.` in it was measurable (issue #104).
2985        let declared = match (&place, &temporary) {
2986            (Some(place), _) => {
2987                place.with_ref(span, |value| value.declared_type_name().cloned())?
2988            }
2989            (_, Some(value)) => value.declared_type_name().cloned(),
2990            _ => unreachable!("a receiver is either a place or a temporary"),
2991        };
2992
2993        // A resource handle's methods belong to the host that issued it, so
2994        // they are dispatched through the boundary rather than looked up in
2995        // the package. A handle is a name; the host owns what it names.
2996        let handle = match (&place, &temporary) {
2997            (Some(place), _) => place.with_ref(span, |value| match value {
2998                Value(Repr::Resource(handle)) => Some(handle.clone()),
2999                _ => None,
3000            })?,
3001            (_, Some(Value(Repr::Resource(handle)))) => Some(handle.clone()),
3002            _ => None,
3003        };
3004        if let Some(handle) = handle {
3005            let what = format!("{}.{name}", handle.qualified_type());
3006            let args = self.eval_args(env, args, trailing)?;
3007            let values = plain_values(args, &what)?;
3008            return Ok(self.call_host_resource(&handle, name, values, span)?);
3009        }
3010
3011        if let Some((type_module, short)) =
3012            declared.as_deref().and_then(|name| name.rsplit_once('.'))
3013        {
3014            if let Some((module, decl)) = self.find_method(type_module, short, name) {
3015                let receiver_slot = match decl.receiver {
3016                    // That the receiver of a `var self` method is a
3017                    // writable place is `cove-sema`'s to say (ADR 0021).
3018                    // What is left is a receiver that is no place at all,
3019                    // which leaves nothing to alias.
3020                    Some(Receiver { is_var: true, .. }) => {
3021                        let Some(place) = place else {
3022                            return Err(var_self_needs_place(name, receiver, span).into());
3023                        };
3024                        ArgSlot::Alias(place)
3025                    }
3026                    _ => ArgSlot::Value(match (place, temporary) {
3027                        (Some(place), _) => place.read(span)?,
3028                        (_, Some(value)) => value,
3029                        _ => unreachable!("a receiver is either a place or a temporary"),
3030                    }),
3031                };
3032                let args = self.eval_args(env, args, trailing)?;
3033                return Ok(self.call_target(
3034                    &Target {
3035                        name,
3036                        params: &decl.params,
3037                        body: &decl.body,
3038                        module,
3039                        receiver: decl.receiver,
3040                        is_async: decl.is_async,
3041                        captures: &[],
3042                        return_type: decl.return_type.as_ref(),
3043                    },
3044                    Some(receiver_slot),
3045                    args,
3046                    span,
3047                )?);
3048            }
3049        }
3050
3051        // A method whose implementation has moved out of Rust and into the
3052        // standard library is resolved next, and generically:
3053        // `cove_schema::builtins::standard_binding` is the same table
3054        // `cove_ir`'s lowering consults before its own per-type dispatch, so
3055        // the tree-walking oracle and the lowered backend agree about which
3056        // methods these are without either restating the other's list. It
3057        // only applies to a builtin receiver, and telling one apart is the
3058        // dot: a builtin `Option` or `Result` is a `Repr::Enum` too and
3059        // answers its own bare name, where a declared enum answers
3060        // `rules.policy.Verdict`. That is the same test the declared-method
3061        // lookup above already makes, and reading `declared.is_none()` here
3062        // instead is what made this hook skip every `Option` and `Result`
3063        // method the first time they were bound — the two builtins that are
3064        // not, in fact, `None`. When it applies, this reaches the
3065        // declared function `binding` names exactly as a call written
3066        // `isEmpty(items)` would: `Interpreter::call_target` is the one path
3067        // every call to a declared function takes, with the receiver
3068        // supplied as its first argument. Nothing here is specific to
3069        // `Array` or to `isEmpty`; the table in `cove-schema` is the only
3070        // thing that says which receiver and method this applies to.
3071        if declared.as_deref().is_none_or(|name| !name.contains('.')) {
3072            let builtin_receiver = match (&place, &temporary) {
3073                (Some(place), _) => place.with_ref(span, |value| value.type_name())?,
3074                (_, Some(value)) => value.type_name(),
3075                _ => unreachable!("a receiver is either a place or a temporary"),
3076            };
3077            if let Some(binding) = cove_schema::builtins::standard_binding(&builtin_receiver, name)
3078            {
3079                let receiver_value = match (place, temporary) {
3080                    (Some(place), _) => place.read(span)?,
3081                    (_, Some(value)) => value,
3082                    _ => unreachable!("a receiver is either a place or a temporary"),
3083                };
3084                return self.call_std_binding(env, binding, receiver_value, args, trailing, span);
3085            }
3086        }
3087
3088        // `snapshot()` is the builtin `Snapshot` trait's one method. A struct
3089        // or enum conformance was already tried above like any other method;
3090        // reaching here means either the receiver is a builtin value type,
3091        // or it is a struct or enum with no conformance, which
3092        // `Interpreter::snapshot` reports.
3093        if name == "snapshot" {
3094            let args = self.eval_args(env, args, trailing)?;
3095            if !args.is_empty() {
3096                return Err(RuntimeError::new(format!(
3097                    "`snapshot` takes 0 argument(s), but {} were given",
3098                    args.len()
3099                ))
3100                .at(span)
3101                .into());
3102            }
3103            let receiver_value = match (place, temporary) {
3104                (Some(place), _) => place.read(span)?,
3105                (_, Some(value)) => value,
3106                _ => unreachable!("a receiver is either a place or a temporary"),
3107            };
3108            return Ok(self.snapshot(&receiver_value, span)?);
3109        }
3110
3111        // `Shared` is a runtime value rather than a declared type, and `lock`
3112        // takes the closure itself rather than the closure's value, so it is
3113        // dispatched here.
3114        // `Shared`, a task scope, and a task handle are runtime values rather
3115        // than declared types, so they are recognized by what they are rather
3116        // than by a name built to compare against a literal.
3117        let is_shared = match (&place, &temporary) {
3118            (Some(place), _) => {
3119                place.with_ref(span, |value| matches!(value, Value(Repr::Shared(_))))?
3120            }
3121            (_, Some(value)) => matches!(value, Value(Repr::Shared(_))),
3122            _ => unreachable!("a receiver is either a place or a temporary"),
3123        };
3124        if is_shared {
3125            let receiver_value = match (&place, &temporary) {
3126                (Some(place), _) => place.read(span)?,
3127                (_, Some(value)) => value.clone(),
3128                _ => unreachable!("a receiver is either a place or a temporary"),
3129            };
3130            return self.call_shared_method(env, receiver_value, name, args, trailing, span);
3131        }
3132
3133        // A task scope and a task handle are runtime values rather than
3134        // declared types, so their operations are dispatched here.
3135        // `examples/tasks/load.cove` writes the await as a postfix call, and
3136        // `bookings.await()` means what `await bookings` means.
3137        let is_task = match (&place, &temporary) {
3138            (Some(place), _) => place.with_ref(span, |value| {
3139                matches!(value, Value(Repr::Task(_)) | Value(Repr::TaskScope(_)))
3140            })?,
3141            (_, Some(value)) => matches!(value, Value(Repr::Task(_)) | Value(Repr::TaskScope(_))),
3142            _ => unreachable!("a receiver is either a place or a temporary"),
3143        };
3144        if name == "await" || is_task {
3145            let receiver_value = match (&place, &temporary) {
3146                (Some(place), _) => place.read(span)?,
3147                (_, Some(value)) => value.clone(),
3148                _ => unreachable!("a receiver is either a place or a temporary"),
3149            };
3150            return self.call_task_method(env, receiver_value, name, args, trailing, span);
3151        }
3152
3153        // Every `var self` method takes a receiver that is a place — and a
3154        // writable one — and that is `cove-sema`'s to say (ADR 0021). What
3155        // is left is one of them called on something that is no place at
3156        // all, which has nowhere to write *to*: refusing is not a language
3157        // rule but the last thing this evaluator can do with an argument it
3158        // was not given. `freeze` is the exception, because it has an answer
3159        // for a temporary — the temporary holds the only handle to its own
3160        // storage, so freezing it writes nowhere.
3161        //
3162        // Which names those are is the shared table's to say, and asking it
3163        // is what makes a mutating method the table gains arrive here with
3164        // the rule already applied. It is the same question `cove-sema`'s
3165        // `mutating_method` asks of a builtin receiver, in the same two
3166        // parts.
3167        //
3168        // The *receiver's* type is asked and not the name alone, because a
3169        // name is not enough: `pop` is a mutating method of a `Vector` and
3170        // no method of an `Array` at all, so `[1].pop()` is told the second
3171        // rather than told to find a place for a method it does not have. A
3172        // receiver that is no builtin — a struct with no such method, a host
3173        // resource — falls through to the dispatch below, which is what
3174        // names it.
3175        if name != "freeze" && place.is_none() {
3176            let is_var_self = temporary.as_ref().is_some_and(|value| {
3177                cove_schema::builtins::builtin(&value.type_name())
3178                    .and_then(|schema| schema.method(name))
3179                    .is_some_and(|method| method.mutating)
3180            });
3181            if is_var_self {
3182                return Err(var_self_needs_place(name, receiver, span).into());
3183            }
3184        }
3185
3186        let args = self.eval_args(env, args, trailing)?;
3187        let mut values = plain_values(args, name)?;
3188
3189        if name == "freeze" {
3190            // `freeze` needs the storage handle where it lives, so that the
3191            // uniqueness check counts the caller's own handle only once.
3192            if let Some(place) = &place {
3193                return Ok(place.with_mut(span, |slot| match slot {
3194                    Value(Repr::Vector(storage)) => builtins::freeze(storage, span),
3195                    other => Err(RuntimeError::new(format!(
3196                        "`{}` has no method `freeze`",
3197                        other.type_name()
3198                    ))
3199                    .at(span)),
3200                })??);
3201            }
3202        }
3203
3204        let receiver_value = match (place, temporary) {
3205            (Some(place), _) => place.read(span)?,
3206            (_, Some(value)) => value,
3207            _ => unreachable!("a receiver is either a place or a temporary"),
3208        };
3209        Ok(builtins::call_method(
3210            self,
3211            &receiver_value,
3212            name,
3213            &mut values,
3214            span,
3215        )?)
3216    }
3217
3218    /// A call to a builtin method the standard library implements rather
3219    /// than a Rust arm of [`builtins::call_method`].
3220    ///
3221    /// `binding` names a declared function of the package — `std.array`'s
3222    /// `isEmpty`, so far — and this reaches it exactly the way
3223    /// `eval_call`'s own `ExprKind::Ident` arm reaches an ordinary call to a
3224    /// declared function: through [`Interpreter::call_target`], the one path
3225    /// every such call takes. The one thing this does that an ordinary call
3226    /// does not is decide the argument list, because the method call the
3227    /// program wrote has an implicit receiver and the function it becomes
3228    /// does not: `receiver` is pushed on as the first argument and whatever
3229    /// the call site wrote follows it.
3230    fn call_std_binding(
3231        &mut self,
3232        env: &mut Env,
3233        binding: &cove_schema::builtins::StdBinding,
3234        receiver: Value,
3235        args: &[Arg],
3236        trailing: Option<&Expr>,
3237        span: Span,
3238    ) -> Eval {
3239        let Some((owner, decl)) = self.find_function(binding.module, binding.function) else {
3240            // The package this program resolved against is missing the
3241            // module `cove_schema::builtins::STANDARD_LIBRARY` names, which
3242            // `cove_sema::Compiler::compile` already refuses before a
3243            // program reaches this evaluator at all. Reaching this arm
3244            // means a caller resolved a package some other way and skipped
3245            // that check; the error says so rather than panicking.
3246            return Err(RuntimeError::new(format!(
3247                "`{}.{}` names no function of `{}` — the package is missing the standard \
3248                 library module `cove_sema::stdlib::attach` adds",
3249                binding.receiver, binding.method, binding.module
3250            ))
3251            .at(span)
3252            .into());
3253        };
3254        let mut evaluated = Vec::with_capacity(args.len() + 1);
3255        evaluated.push(EvaluatedArg {
3256            label: None,
3257            spread: false,
3258            slot: ArgSlot::Value(receiver),
3259            span,
3260        });
3261        evaluated.extend(self.eval_args(env, args, trailing)?);
3262        Ok(self.call_target(
3263            &Target {
3264                name: binding.function,
3265                params: &decl.params,
3266                body: &decl.body,
3267                module: owner,
3268                receiver: decl.receiver,
3269                is_async: decl.is_async,
3270                captures: &[],
3271                return_type: decl.return_type.as_ref(),
3272            },
3273            None,
3274            evaluated,
3275            span,
3276        )?)
3277    }
3278
3279    /// A call to an associated builtin function the standard library
3280    /// implements rather than a Rust arm of [`builtins::call_associated`],
3281    /// such as `Duration.millis(n)`.
3282    ///
3283    /// Symmetric to [`Interpreter::call_std_binding`], but simpler: an
3284    /// associated call has no implicit receiver, so `args` is already
3285    /// exactly the argument list the declared function needs.
3286    fn call_std_associated_binding(
3287        &mut self,
3288        env: &mut Env,
3289        binding: &cove_schema::builtins::StdBinding,
3290        args: &[Arg],
3291        trailing: Option<&Expr>,
3292        span: Span,
3293    ) -> Eval {
3294        let Some((owner, decl)) = self.find_function(binding.module, binding.function) else {
3295            // As in `call_std_binding`: reachable only if a caller resolved
3296            // a package without attaching the standard library.
3297            return Err(RuntimeError::new(format!(
3298                "`{}.{}` names no function of `{}` — the package is missing the standard \
3299                 library module `cove_sema::stdlib::attach` adds",
3300                binding.receiver, binding.method, binding.module
3301            ))
3302            .at(span)
3303            .into());
3304        };
3305        let evaluated = self.eval_args(env, args, trailing)?;
3306        Ok(self.call_target(
3307            &Target {
3308                name: binding.function,
3309                params: &decl.params,
3310                body: &decl.body,
3311                module: owner,
3312                receiver: decl.receiver,
3313                is_async: decl.is_async,
3314                captures: &[],
3315                return_type: decl.return_type.as_ref(),
3316            },
3317            None,
3318            evaluated,
3319            span,
3320        )?)
3321    }
3322
3323    /// Struct initialization is a synthesized labeled call.
3324    fn init_struct(
3325        &mut self,
3326        module: &str,
3327        decl: &Arc<StructDecl>,
3328        args: Vec<EvaluatedArg>,
3329        span: Span,
3330    ) -> Result<Value, RuntimeError> {
3331        let names: Vec<&str> = decl.fields.iter().map(|f| f.name.node.as_str()).collect();
3332        let (mut slots, _) = assign_labels(&names, args, &decl.name.node, false)?;
3333        let mut fields = Vec::with_capacity(decl.fields.len());
3334        for (index, field) in decl.fields.iter().enumerate() {
3335            let Some(arg) = slots[index].take() else {
3336                return Err(RuntimeError::new(format!(
3337                    "`{}` needs a value for field `{}`",
3338                    decl.name.node, field.name.node
3339                ))
3340                .at(span)
3341                .with_rule("Struct initialization is a synthesized labeled call.")
3342                .with_help(format!(
3343                    "add `{}: <value>` to the initializer",
3344                    field.name.node
3345                )));
3346            };
3347            let value = value_of(&arg, &field.name.node, arg.span)?;
3348            fields.push((
3349                field.name.node.as_str().into(),
3350                self.coerce(module, value, &field.ty),
3351            ));
3352        }
3353        Ok(Value(Repr::Struct(Rc::new(StructValue {
3354            type_name: format!("{module}.{}", decl.name.node).into(),
3355            fields,
3356            opaque: self.is_opaque(module, &decl.name.node),
3357        }))))
3358    }
3359
3360    /// Whether `module` declared this struct `export opaque struct`, which
3361    /// is what makes a value of it render as its name alone (ADR 0014).
3362    ///
3363    /// The checker refuses to let another module name a field; this is the
3364    /// other half, and it applies to every reader including the declaring
3365    /// module, because a rendered string goes wherever it is passed.
3366    fn is_opaque(&self, module: &str, name: &str) -> bool {
3367        self.resolved(module)
3368            .and_then(|resolved| resolved.structs.get(name))
3369            .is_some_and(|entry| entry.opaque)
3370    }
3371
3372    fn eval_args(
3373        &mut self,
3374        env: &mut Env,
3375        args: &[Arg],
3376        trailing: Option<&Expr>,
3377    ) -> Result<Vec<EvaluatedArg>, Control> {
3378        let mut evaluated = Vec::with_capacity(args.len() + usize::from(trailing.is_some()));
3379        for arg in args {
3380            let slot = if arg.is_var {
3381                // That the argument is a writable place is `cove-sema`'s to
3382                // say (ADR 0021); `resolve_place` still answers whether it
3383                // is a place at all, because it has to build one either way.
3384                let place = self.resolve_place(env, &arg.value)?;
3385                ArgSlot::Alias(place)
3386            } else {
3387                ArgSlot::Value(self.eval(env, &arg.value)?)
3388            };
3389            evaluated.push(EvaluatedArg {
3390                label: arg.label.as_ref().map(|l| l.node.as_str().into()),
3391                spread: arg.spread,
3392                slot,
3393                span: arg.span,
3394            });
3395        }
3396        if let Some(trailing) = trailing {
3397            let value = self.eval_trailing(env, trailing)?;
3398            evaluated.push(EvaluatedArg {
3399                label: None,
3400                spread: false,
3401                slot: ArgSlot::Value(value),
3402                span: trailing.span,
3403            });
3404        }
3405        Ok(evaluated)
3406    }
3407
3408    /// A trailing block is a closure argument: `tasks.spawn { ... }`.
3409    fn eval_trailing(&mut self, env: &mut Env, expr: &Expr) -> Eval {
3410        match &expr.kind {
3411            ExprKind::Block(block) => self
3412                .make_closure(env, false, Vec::new(), block.clone(), expr.span)
3413                .map_err(Control::from),
3414            _ => self.eval(env, expr),
3415        }
3416    }
3417
3418    // --------------------------------------------------------------- places
3419
3420    /// Resolves an lvalue, or reports why the expression is not a place.
3421    ///
3422    /// The last arm — an expression that is no place at all — is refused by
3423    /// `cove-sema` before the run (ADR 0021), so no checked program reaches
3424    /// it. It stays for the same reason `var_self_needs_place` does: this
3425    /// function must answer with a `Place` or with an error, and there is no
3426    /// place to build from a call's result.
3427    fn resolve_place(&mut self, env: &mut Env, expr: &Expr) -> Result<Place, Control> {
3428        match &expr.kind {
3429            ExprKind::Ident(name) => match env.lookup(name) {
3430                Some(place) => Ok(place.clone()),
3431                None => Err(
3432                    RuntimeError::new(format!("cannot find `{name}` in this scope"))
3433                        .at(expr.span)
3434                        .into(),
3435                ),
3436            },
3437            ExprKind::Field { base, name } => {
3438                let base_place = self.resolve_place(env, base)?;
3439                base_place.with_ref(expr.span, |value| match value {
3440                    Value(Repr::Struct(value)) => match value.get(&name.node) {
3441                        Some(_) => Ok(()),
3442                        None => Err(no_field(&value.type_name, &name.node, expr.span)),
3443                    },
3444                    other => Err(not_a_struct(other, &name.node, expr.span)),
3445                })??;
3446                Ok(base_place.field(name.node.as_str().into()))
3447            }
3448            _ => Err(RuntimeError::new(
3449                "this expression is not a place, so it cannot be assigned or aliased",
3450            )
3451            .at(expr.span)
3452            .with_rule("Only variables and their struct fields are places.")
3453            .into()),
3454        }
3455    }
3456
3457    /// Resolves an lvalue when the expression denotes one, without failing.
3458    fn resolve_place_opt(&mut self, env: &mut Env, expr: &Expr) -> Result<Option<Place>, Control> {
3459        match &expr.kind {
3460            ExprKind::Ident(name) => Ok(env.lookup(name).cloned()),
3461            ExprKind::Field { base, name } => {
3462                let Some(base_place) = self.resolve_place_opt(env, base)? else {
3463                    return Ok(None);
3464                };
3465                let is_field = base_place.with_ref(expr.span, |value| match value {
3466                    Value(Repr::Struct(value)) => value.get(&name.node).is_some(),
3467                    _ => false,
3468                })?;
3469                Ok(is_field.then(|| base_place.field(name.node.as_str().into())))
3470            }
3471            _ => Ok(None),
3472        }
3473    }
3474
3475    // ------------------------------------------------------------ patterns
3476
3477    fn match_pattern(
3478        &mut self,
3479        env: &mut Env,
3480        pattern: &Pattern,
3481        value: &Value,
3482    ) -> Result<bool, Control> {
3483        match &pattern.kind {
3484            PatternKind::Wildcard => Ok(true),
3485            PatternKind::Binding(name) => {
3486                // `None` is a case, not a name to bind.
3487                if name == NONE_CASE.name {
3488                    if let Value(Repr::Enum(option)) = value {
3489                        if &*option.type_name == OPTION.name {
3490                            return Ok(&*option.case == NONE_CASE.name);
3491                        }
3492                    }
3493                }
3494                env.declare(name.as_str().into(), Place::binding(value.clone()));
3495                Ok(true)
3496            }
3497            PatternKind::Literal(expr) => {
3498                let literal = self.eval(env, expr)?;
3499                Ok(value.eq_value(&literal))
3500            }
3501            PatternKind::Variant { path, payload } => {
3502                let Value(Repr::Enum(subject)) = value else {
3503                    return Ok(false);
3504                };
3505                let Some(case) = path.last() else {
3506                    return Ok(false);
3507                };
3508                if &*subject.case != case.node.as_str() {
3509                    return Ok(false);
3510                }
3511                if path.len() >= 2 {
3512                    let expected = &path[path.len() - 2].node;
3513                    let actual = subject
3514                        .type_name
3515                        .rsplit('.')
3516                        .next()
3517                        .unwrap_or(&subject.type_name);
3518                    if actual != expected {
3519                        return Ok(false);
3520                    }
3521                }
3522                if payload.len() != subject.payload.len() {
3523                    return Err(RuntimeError::new(format!(
3524                        "case `{}` carries {} value(s), but the pattern binds {}",
3525                        case.node,
3526                        subject.payload.len(),
3527                        payload.len()
3528                    ))
3529                    .at(pattern.span)
3530                    .into());
3531                }
3532                for (sub, value) in payload.iter().zip(subject.payload.iter()) {
3533                    if !self.match_pattern(env, sub, value)? {
3534                        return Ok(false);
3535                    }
3536                }
3537                Ok(true)
3538            }
3539        }
3540    }
3541
3542    /// An independent, mutable copy of `value`'s own graph, per the builtin
3543    /// `Snapshot` trait.
3544    ///
3545    /// Immutable values return themselves, since sharing their storage is
3546    /// unobservable. `Vector` is the one MVP type with an independent
3547    /// mutable graph to copy: it allocates fresh storage and snapshots each
3548    /// element recursively. A `dyn Trait` value snapshots the concrete value
3549    /// it carries, keeping the same trait. A struct or enum dispatches
3550    /// through its own `impl Snapshot for Type`, exactly like any other
3551    /// method call. Closures, tasks, task scopes, and host handles have no
3552    /// independent graph to copy and do not conform by default.
3553    ///
3554    /// The MVP's value model has no way to construct a cycle — every
3555    /// container (`Struct`, `Enum`, `Array`, `Vector`) owns `Value`s by
3556    /// `Rc`, and a value is built bottom-up from values that already exist,
3557    /// so nothing can point back to a container still being built. Snapshots
3558    /// therefore only need this straightforward structural copy; "preserves
3559    /// cycles" is not yet a case the MVP can exercise.
3560    fn snapshot(&mut self, value: &Value, span: Span) -> Result<Value, RuntimeError> {
3561        match value {
3562            Value(Repr::Dyn(wrapped)) => Ok(Value(Repr::Dyn(Rc::new(DynValue {
3563                trait_name: wrapped.trait_name.clone(),
3564                value: self.snapshot(&wrapped.value, span)?,
3565            })))),
3566            Value(Repr::Struct(s)) => self.dispatch_snapshot(&s.type_name, value.clone(), span),
3567            Value(Repr::Enum(e)) => self.dispatch_snapshot(&e.type_name, value.clone(), span),
3568            // Everything a conformance is not consulted about, which both
3569            // backends answer the same way and therefore answer in one
3570            // place.
3571            other => builtins::snapshot(self, other, span),
3572        }
3573    }
3574
3575    /// Calls `type_name`'s own `snapshot` method, which exists exactly when
3576    /// some module wrote `impl Snapshot for Type`.
3577    fn dispatch_snapshot(
3578        &mut self,
3579        type_name: &str,
3580        receiver: Value,
3581        span: Span,
3582    ) -> Result<Value, RuntimeError> {
3583        let Some((type_module, short)) = type_name.rsplit_once('.') else {
3584            return Err(builtins::no_snapshot_conformance(&receiver, span));
3585        };
3586        let Some((module, decl)) = self.find_method(type_module, short, "snapshot") else {
3587            return Err(builtins::no_snapshot_conformance(&receiver, span));
3588        };
3589        self.call_target(
3590            &Target {
3591                name: "snapshot",
3592                params: &decl.params,
3593                body: &decl.body,
3594                module,
3595                receiver: decl.receiver,
3596                is_async: decl.is_async,
3597                captures: &[],
3598                return_type: decl.return_type.as_ref(),
3599            },
3600            Some(ArgSlot::Value(receiver)),
3601            Vec::new(),
3602            span,
3603        )
3604    }
3605}
3606
3607impl Callable for Interpreter<'_> {
3608    fn allocate_vector(&mut self, elements: Vec<Value>) -> Value {
3609        Interpreter::allocate_vector(self, elements)
3610    }
3611
3612    fn snapshot(&mut self, value: &Value, span: Span) -> Result<Value, RuntimeError> {
3613        Interpreter::snapshot(self, value, span)
3614    }
3615
3616    /// The caller's vector is drained rather than consumed, so that a
3617    /// higher-order builtin can hand the same one down for every element it
3618    /// walks. See [`crate::builtins::Callable::call_value`] for why, and
3619    /// `builtins::walk_with` for what pays for it.
3620    ///
3621    /// One vector is still built per call here, because a slot carries a
3622    /// label and a span beside its value and the interpreter binds
3623    /// parameters out of that shape. It is one of the several this backend
3624    /// builds per call — an `Env`, the parameter names, the label
3625    /// assignment — rather than the only one, unlike the linear-memory
3626    /// backend, whose calling convention needs no vector at all: an
3627    /// argument already lives in the slot a frame reserved for it before the
3628    /// call began.
3629    fn call_value(
3630        &mut self,
3631        callee: &Value,
3632        args: &mut Vec<Value>,
3633        span: Span,
3634    ) -> Result<Value, RuntimeError> {
3635        let args = args
3636            .drain(..)
3637            .map(|value| EvaluatedArg {
3638                label: None,
3639                spread: false,
3640                slot: ArgSlot::Value(value),
3641                span,
3642            })
3643            .collect();
3644        self.call_value_slots(callee.clone(), args, span)
3645    }
3646
3647    fn arity(&self, callee: &Value) -> Option<usize> {
3648        match callee {
3649            Value(Repr::Closure(closure)) => Some(closure.arity),
3650            _ => None,
3651        }
3652    }
3653}
3654
3655/// A declared function used as a value: a closure over nothing.
3656///
3657/// `Env::captures` is not consulted, because a declaration reads no
3658/// environment — the same fact `cove_ir::lower`'s own `close_over` states
3659/// when it lowers a function used as a value: an environment object with no
3660/// captures at all, rather than a list of names to read.
3661///
3662/// A bare name and a `module.name` both reach a declaration this way, and
3663/// they build the same closure out of it, so they build it here rather than
3664/// twice: the arity a closure answers has to be the length of the list its
3665/// call binds against, and one place that reads both off the same
3666/// declaration is one place they can be made to agree.
3667fn declared_as_value(module: Rc<str>, decl: Arc<FnDecl>) -> Value {
3668    Value(Repr::Closure(Rc::new(Closure {
3669        is_async: decl.is_async,
3670        arity: decl.params.len(),
3671        body: ClosureBody::Tree {
3672            params: decl.params.clone(),
3673            block: Arc::new(decl.body.clone()),
3674            decl: Some(decl),
3675        },
3676        module,
3677        captures: Vec::new(),
3678    })))
3679}
3680
3681// -------------------------------------------------------------- operators
3682
3683/// Integer arithmetic traps instead of wrapping.
3684///
3685/// Overflow of `+`, `-`, `*`, and unary `-`, and division or remainder by
3686/// zero, are broken invariants: they raise a [`RuntimeError`] naming the
3687/// operation rather than producing a defined-but-wrong value. There are no
3688/// implicit numeric, string, or boolean conversions, so mixed operands are
3689/// rejected too.
3690/// Whether a value is one an `impl Trait for Type` can be written for, and
3691/// so one a `dyn Trait` can be holding. Traits are implemented for structs
3692/// and enums; nothing else in the value domain can be behind a trait object.
3693fn conformable(value: &Value) -> bool {
3694    matches!(value, Value(Repr::Struct(_)) | Value(Repr::Enum(_)))
3695}
3696
3697/// Wraps a concrete value as the `dyn Trait` value a written type asks for.
3698///
3699/// The language's one implicit conversion, and the one place a Cove value's
3700/// runtime representation depends on its static type. Each backend has
3701/// exactly one place it does this, so that neither builds a different dyn
3702/// value in a second place by accident: the interpreter reaches it from
3703/// [`Interpreter::coerce`], which walks the written type at the moment of
3704/// the conversion, and `cove_ir::lower`'s own `Body::erase` walks the same
3705/// written type at lowering time instead — this function is what it calls
3706/// `interp::as_dyn` in its own words.
3707///
3708/// A value that is already a trait object is left alone rather than wrapped
3709/// again, so `dyn Trait` does not nest. That is what makes the conversion
3710/// idempotent — `f(x)` and `f(g(x))`, where `f` and `g` both take and
3711/// answer a `dyn Display`, hand the body the same value — and it is why
3712/// dispatch finds the concrete type exactly one step in.
3713pub(crate) fn as_dyn(value: Value, trait_name: &Rc<str>) -> Value {
3714    if matches!(value, Value(Repr::Dyn(_))) {
3715        return value;
3716    }
3717    Value(Repr::Dyn(Rc::new(DynValue {
3718        trait_name: Rc::clone(trait_name),
3719        value,
3720    })))
3721}
3722
3723/// Applies `each` to what an `Array` holds and to what an `Option` holds,
3724/// and answers everything else unchanged.
3725///
3726/// One step into a container whose elements a written type says are `dyn`
3727/// too — the interpreter's own version of a rule `cove_ir::lower` applies at
3728/// lowering time instead. `Array<dyn Display>` and `Option<dyn Display>` are
3729/// the two forms of it, and nothing else is reached: a `Vector` is a shared
3730/// handle whose elements cannot be rewritten behind its other aliases, and a
3731/// `Map`'s and a `Set`'s elements are not what one argument of one head
3732/// names.
3733///
3734/// The step does not ask which of the two it is taking, because the value is
3735/// what says whether a layer was an array or an option; the lowering asks
3736/// the same question earlier, of the written type, before the value it
3737/// describes exists to be walked.
3738pub(crate) fn coerce_inside(value: Value, mut each: impl FnMut(Value) -> Value) -> Value {
3739    match value {
3740        Value(Repr::Array(items)) => Value(Repr::Array(items.iter().cloned().map(each).collect())),
3741        Value(Repr::Enum(mut option)) if &*option.type_name == "Option" => {
3742            for item in &mut option.payload {
3743                *item = each(item.clone());
3744            }
3745            Value(Repr::Enum(option))
3746        }
3747        other => other,
3748    }
3749}
3750
3751/// The value a method call dispatches from, when its receiver is a trait
3752/// object, and nothing when it is not one.
3753///
3754/// A `dyn Trait` receiver is unwrapped to the concrete value it carries, and
3755/// the implementation is found from *that* value's type. This is what makes
3756/// the dispatch dynamic: the static type says only which trait the method
3757/// must come from. The interpreter asks it of a place or of a temporary
3758/// here, in one place, so it cannot decide to dispatch from the wrapper
3759/// instead somewhere else; the linear-memory backend asks the same question
3760/// of the slot its receiver argument stands in, reading the layout directly
3761/// rather than through a materialized `Value`.
3762///
3763/// `None` is not an error. The checker converts where a type is *written*
3764/// and does not convert a lambda's inferred result, though it gives both the
3765/// type `dyn Trait`, so a receiver whose static type is a trait object may
3766/// hold the concrete value unwrapped — and it dispatches from itself, which
3767/// is the same answer.
3768pub(crate) fn dyn_receiver(value: &Value) -> Option<Value> {
3769    match value {
3770        Value(Repr::Dyn(object)) => Some(object.value.clone()),
3771        _ => None,
3772    }
3773}
3774
3775pub(crate) fn binary(
3776    op: BinaryOp,
3777    lhs: Value,
3778    rhs: Value,
3779    span: Span,
3780) -> Result<Value, RuntimeError> {
3781    match op {
3782        BinaryOp::Eq | BinaryOp::Ne => {
3783            // Through the `dyn Trait` wrapper: a written `dyn Trait` is
3784            // wrapped here and a lambda's inferred one is not, though the
3785            // checker gives both the same type, so a comparison reaching
3786            // one compares what it holds. Erasing settles that pair on its
3787            // own, since both sides then name the concrete type.
3788            //
3789            // What erasing cannot settle is two trait objects over
3790            // *different* concrete types, which the checker agreed about and
3791            // this guard would refuse. Only a struct or an enum can be
3792            // behind a trait object, because only those can carry an `impl`,
3793            // so the guard is dropped for exactly that pair and stands
3794            // everywhere else — including against a value whose type the
3795            // checker abstained about, which is where dropping it wholesale
3796            // turned an error into a silent `false`.
3797            let objects = (matches!(lhs, Value(Repr::Dyn(_)))
3798                || matches!(rhs, Value(Repr::Dyn(_))))
3799                && conformable(lhs.erased())
3800                && conformable(rhs.erased());
3801            let (lhs, rhs) = (lhs.erased(), rhs.erased());
3802            if !objects && !lhs.same_type_as(rhs) {
3803                return Err(RuntimeError::new(format!(
3804                    "cannot compare `{}` with `{}`",
3805                    lhs.type_name(),
3806                    rhs.type_name()
3807                ))
3808                .at(span)
3809                .with_rule("`==` means value equality between values of the same type."));
3810            }
3811            let equal = lhs.eq_value(rhs);
3812            Ok(Value(Repr::Bool(if op == BinaryOp::Eq {
3813                equal
3814            } else {
3815                !equal
3816            })))
3817        }
3818        // `is` is narrower than `==`: same shared storage, not same value.
3819        // `Vector` is the one MVP type with storage of its own; everything
3820        // else has no identity `is` can answer, which is a distinct error
3821        // from a type mismatch.
3822        BinaryOp::Is => {
3823            // Through the wrapper here too, so that `is` names what it is
3824            // looking at rather than where the value was converted. No trait
3825            // object can hold a `Vector` today — a trait is implemented for a
3826            // struct or an enum — so this changes only which type name the
3827            // failure below reports.
3828            let (lhs, rhs) = (lhs.erased(), rhs.erased());
3829            if !lhs.same_type_as(rhs) {
3830                return Err(RuntimeError::new(format!(
3831                    "cannot compare the identity of `{}` with `{}`",
3832                    lhs.type_name(),
3833                    rhs.type_name()
3834                ))
3835                .at(span)
3836                .with_rule("`is` compares identity between values of the same type."));
3837            }
3838            match (lhs, rhs) {
3839                (Value(Repr::Vector(a)), Value(Repr::Vector(b))) => {
3840                    Ok(Value(Repr::Bool(Rc::ptr_eq(a, b))))
3841                }
3842                _ => Err(identity_not_available(lhs, span)),
3843            }
3844        }
3845        BinaryOp::And | BinaryOp::Or => unreachable!("short-circuited in `eval`"),
3846        BinaryOp::Add | BinaryOp::Sub | BinaryOp::Mul | BinaryOp::Div | BinaryOp::Rem => {
3847            match (&lhs, &rhs) {
3848                (Value(Repr::Int(a)), Value(Repr::Int(b))) => {
3849                    let (a, b) = (*a, *b);
3850                    let value = match op {
3851                        BinaryOp::Add => a.checked_add(b).ok_or_else(|| overflow("addition", span)),
3852                        BinaryOp::Sub => a
3853                            .checked_sub(b)
3854                            .ok_or_else(|| overflow("subtraction", span)),
3855                        BinaryOp::Mul => a
3856                            .checked_mul(b)
3857                            .ok_or_else(|| overflow("multiplication", span)),
3858                        BinaryOp::Div => {
3859                            if b == 0 {
3860                                Err(divide_by_zero("division", span))
3861                            } else {
3862                                a.checked_div(b).ok_or_else(|| overflow("division", span))
3863                            }
3864                        }
3865                        BinaryOp::Rem => {
3866                            if b == 0 {
3867                                Err(divide_by_zero("remainder", span))
3868                            } else {
3869                                a.checked_rem(b).ok_or_else(|| overflow("remainder", span))
3870                            }
3871                        }
3872                        _ => unreachable!("checked above"),
3873                    }?;
3874                    Ok(Value(Repr::Int(value)))
3875                }
3876                (Value(Repr::Float(a)), Value(Repr::Float(b))) => {
3877                    Ok(Value(Repr::Float(match op {
3878                        BinaryOp::Add => a + b,
3879                        BinaryOp::Sub => a - b,
3880                        BinaryOp::Mul => a * b,
3881                        BinaryOp::Div => a / b,
3882                        BinaryOp::Rem => a % b,
3883                        _ => unreachable!("checked above"),
3884                    })))
3885                }
3886                (Value(Repr::Duration(a)), Value(Repr::Duration(b)))
3887                    if matches!(op, BinaryOp::Add | BinaryOp::Sub) =>
3888                {
3889                    let value = match op {
3890                        BinaryOp::Add => a.checked_add(*b),
3891                        _ => a.checked_sub(*b),
3892                    }
3893                    .ok_or_else(|| overflow("duration arithmetic", span))?;
3894                    Ok(Value(Repr::Duration(value)))
3895                }
3896                (Value(Repr::Str(_)), Value(Repr::Str(_))) if op == BinaryOp::Add => {
3897                    Err(RuntimeError::new("`+` is not defined for `String`")
3898                        .at(span)
3899                        .with_rule("There are no implicit string conversions.")
3900                        .with_help("use string interpolation, such as \"{left}{right}\""))
3901                }
3902                _ => Err(operator_type_error(op, &lhs, &rhs, span)),
3903            }
3904        }
3905        BinaryOp::Lt | BinaryOp::Le | BinaryOp::Gt | BinaryOp::Ge => {
3906            let ordering = match (&lhs, &rhs) {
3907                (Value(Repr::Int(a)), Value(Repr::Int(b))) => a.partial_cmp(b),
3908                (Value(Repr::Float(a)), Value(Repr::Float(b))) => a.partial_cmp(b),
3909                (Value(Repr::Duration(a)), Value(Repr::Duration(b))) => a.partial_cmp(b),
3910                (Value(Repr::Str(a)), Value(Repr::Str(b))) => a.partial_cmp(b),
3911                _ => return Err(operator_type_error(op, &lhs, &rhs, span)),
3912            };
3913            let Some(ordering) = ordering else {
3914                return Ok(Value(Repr::Bool(false)));
3915            };
3916            Ok(Value(Repr::Bool(match op {
3917                BinaryOp::Lt => ordering.is_lt(),
3918                BinaryOp::Le => ordering.is_le(),
3919                BinaryOp::Gt => ordering.is_gt(),
3920                _ => ordering.is_ge(),
3921            })))
3922        }
3923    }
3924}
3925
3926pub(crate) fn unary(op: UnaryOp, value: Value, span: Span) -> Result<Value, RuntimeError> {
3927    match (op, value) {
3928        (UnaryOp::Not, Value(Repr::Bool(value))) => Ok(Value(Repr::Bool(!value))),
3929        (UnaryOp::Neg, Value(Repr::Int(value))) => Ok(Value(Repr::Int(
3930            value
3931                .checked_neg()
3932                .ok_or_else(|| overflow("negation", span))?,
3933        ))),
3934        (UnaryOp::Neg, Value(Repr::Float(value))) => Ok(Value(Repr::Float(-value))),
3935        (UnaryOp::Neg, Value(Repr::Duration(value))) => Ok(Value(Repr::Duration(
3936            value
3937                .checked_neg()
3938                .ok_or_else(|| overflow("negation", span))?,
3939        ))),
3940        (op, value) => Err(RuntimeError::new(format!(
3941            "`{}` is not defined for `{}`",
3942            match op {
3943                UnaryOp::Not => "!",
3944                UnaryOp::Neg => "-",
3945            },
3946            value.type_name()
3947        ))
3948        .at(span)
3949        .with_rule("There are no implicit numeric, string, or boolean conversions.")),
3950    }
3951}
3952
3953// ------------------------------------------------------------------ enums
3954
3955/// Builds one case of the enum `module` declares as `decl`.
3956///
3957/// A free function because the interpreter builds a declared enum's value
3958/// by looking its case up by name at every evaluation — walking syntax does
3959/// not resolve a case to a fixed position the way lowering does.
3960/// `cove_ir::lower` needs no runtime equivalent of this: it resolves the
3961/// case statically and lowers straight to the words a value of it holds,
3962/// since `cove-sema` refuses a missing case or a wrong-length payload before
3963/// either backend runs.
3964///
3965/// Which errors those are is the whole of what this decides: a case the
3966/// declaration does not write, and a payload whose length is not the one the
3967/// case carries. `cove-sema` refuses both before either backend sees them,
3968/// which is why neither message names a fix a checked program could need —
3969/// they are the floor under a checker that stops proving it.
3970pub(crate) fn enum_case(
3971    program: &Program,
3972    module: &str,
3973    decl: &Arc<EnumDecl>,
3974    case: &str,
3975    payload: &mut Vec<Value>,
3976    span: Span,
3977) -> Result<Value, RuntimeError> {
3978    let Some(found) = decl.cases.iter().find(|c| c.name.node == case) else {
3979        return Err(RuntimeError::new(format!(
3980            "enum `{}` has no case or associated function `{case}`",
3981            decl.name.node
3982        ))
3983        .at(span)
3984        .with_rule(
3985            "`Enum.name` is a case when the enum declares one, and otherwise an associated function declared in an `impl` block.",
3986        )
3987        .with_help(known_members(program, module, decl)));
3988    };
3989    if found.payload.len() != payload.len() {
3990        return Err(RuntimeError::new(format!(
3991            "case `{}.{case}` carries {} value(s), but {} were given",
3992            decl.name.node,
3993            found.payload.len(),
3994            payload.len()
3995        ))
3996        .at(span));
3997    }
3998    Ok(Value(Repr::Enum(Box::new(EnumValue {
3999        type_name: format!("{module}.{}", decl.name.node).into(),
4000        case: case.into(),
4001        // Drained rather than taken whole: the caller lent this vector and
4002        // wants it back with its capacity, and a `Payload` built from a
4003        // draining iterator allocates nothing at all for the arities that
4004        // occur. See `crate::value::Payload`.
4005        payload: payload.drain(..).collect(),
4006    }))))
4007}
4008
4009/// The cases and associated functions `Enum.name` could have meant.
4010fn known_members(program: &Program, module: &str, decl: &Arc<EnumDecl>) -> String {
4011    let cases: Vec<&str> = decl
4012        .cases
4013        .iter()
4014        .map(|case| case.name.node.as_str())
4015        .collect();
4016    let mut help = format!("known cases: {}", cases.join(", "));
4017    let functions: Vec<&str> = match program.modules.get(module) {
4018        Some(resolved) => resolved
4019            .methods
4020            .keys()
4021            .filter(|(type_name, _)| *type_name == decl.name.node)
4022            .map(|(_, name)| name.as_str())
4023            .collect(),
4024        None => Vec::new(),
4025    };
4026    if !functions.is_empty() {
4027        help.push_str(&format!("; known functions: {}", functions.join(", ")));
4028    }
4029    help
4030}
4031
4032/// No arm of a `match` covered `value`.
4033///
4034/// Static exhaustiveness checking is future work; until then a `match` that
4035/// covers no case fails rather than silently producing a value. Both
4036/// backends refuse for the same rule — it is one and not two — though not
4037/// always in the same words: `cove_ir::lower` emits a fixed trap message at
4038/// the point a `match` has no arm left, because formatting the actual value
4039/// there would mean calling back into a builtin from an instruction that is
4040/// already failing. This is the oracle's fuller version of the same refusal.
4041pub(crate) fn no_match(value: &Value, span: Span) -> RuntimeError {
4042    RuntimeError::new(format!("no `match` arm covers `{value}`"))
4043        .at(span)
4044        .with_rule("`match` must cover every enum case.")
4045        .with_help("add an arm for this case, or a `_` arm")
4046}
4047
4048// -------------------------------------------------------------- arguments
4049
4050/// `MapEntry(key: ..., value: ...)` is a synthesized labeled call for a
4051/// builtin struct, exactly like a user struct's synthesized initializer. It
4052/// exists only so `Map.of` has an ordinary call-shaped way to build the pairs
4053/// it collects; it is not a declared struct because nothing else derives it.
4054///
4055/// Its labels are the fields `cove_schema::builtins::MAP_ENTRY` declares, in
4056/// declaration order, which is also what the checker checks the call against.
4057fn init_map_entry(args: Vec<EvaluatedArg>, span: Span) -> Result<Value, RuntimeError> {
4058    let labels: Vec<&str> = MAP_ENTRY.fields.iter().map(|field| field.name).collect();
4059    let (mut slots, _) = assign_labels(&labels, args, MAP_ENTRY.name, false)?;
4060    let mut fields = Vec::with_capacity(labels.len());
4061    for (index, field_name) in labels.iter().enumerate() {
4062        let Some(arg) = slots[index].take() else {
4063            return Err(RuntimeError::new(format!(
4064                "`{}` needs a value for field `{field_name}`",
4065                MAP_ENTRY.name
4066            ))
4067            .at(span)
4068            .with_rule("Struct initialization is a synthesized labeled call.")
4069            .with_help(format!("add `{field_name}: <value>` to the initializer")));
4070        };
4071        fields.push(((*field_name).into(), value_of(&arg, field_name, arg.span)?));
4072    }
4073    Ok(Value(Repr::Struct(Rc::new(StructValue {
4074        type_name: MAP_ENTRY.name.into(),
4075        fields,
4076        opaque: false,
4077    }))))
4078}
4079
4080/// Matches call-site arguments to declared names.
4081///
4082/// Positional arguments may precede labels and are matched to names in
4083/// declaration order; after the first label every argument must be labeled.
4084///
4085/// The out-of-order refusal below is one `cove-sema` reports before the run
4086/// (ADR 0021), so no checked program reaches it. It stays because this is
4087/// the oracle's own statement of the evaluation-order rule the linear-memory
4088/// backend's calling convention is built on too — `cove_ir::lower` lowers
4089/// every argument in source order at each call site rather than through one
4090/// shared matching step — and a rule two backends both rely on is better
4091/// stated twice than assumed twice.
4092#[allow(clippy::type_complexity)]
4093fn assign_labels(
4094    names: &[&str],
4095    args: Vec<EvaluatedArg>,
4096    what: &str,
4097    variadic_last: bool,
4098) -> Result<(Vec<Option<EvaluatedArg>>, Vec<EvaluatedArg>), RuntimeError> {
4099    let mut slots: Vec<Option<EvaluatedArg>> = (0..names.len()).map(|_| None).collect();
4100    let mut rest = Vec::new();
4101    let mut next = 0usize;
4102    let mut labeled = false;
4103
4104    for arg in args {
4105        match &arg.label {
4106            Some(label) => {
4107                labeled = true;
4108                let Some(index) = names.iter().position(|n| *n == &**label) else {
4109                    return Err(RuntimeError::new(format!(
4110                        "`{what}` has no parameter labeled `{label}`"
4111                    ))
4112                    .at(arg.span)
4113                    .with_rule("Argument labels are parameter names and part of the API contract.")
4114                    .with_help(format!("known labels: {}", names.join(", "))));
4115                };
4116                if slots[index].is_some() {
4117                    return Err(RuntimeError::new(format!(
4118                        "`{what}` was given `{label}` more than once"
4119                    ))
4120                    .at(arg.span));
4121                }
4122                // Labels are static parameter names, so left-to-right
4123                // evaluation of the call must match the declaration order.
4124                if index < next {
4125                    return Err(RuntimeError::new(format!(
4126                        "`{what}` was given the label `{label}` out of declaration order"
4127                    ))
4128                    .at(arg.span)
4129                    .with_rule(
4130                        "Labeled arguments appear in declaration order, so argument order matches parameter order.",
4131                    )
4132                    .with_help(format!(
4133                        "write the arguments in this order: {}",
4134                        names.join(", ")
4135                    )));
4136                }
4137                slots[index] = Some(arg);
4138                next = index + 1;
4139            }
4140            None => {
4141                if labeled {
4142                    return Err(RuntimeError::new(format!(
4143                        "`{what}` was given a positional argument after a labeled one"
4144                    ))
4145                    .at(arg.span)
4146                    .with_rule(
4147                        "Positional arguments may precede labels; after the first label every argument must be labeled.",
4148                    ));
4149                }
4150                if variadic_last && next + 1 >= names.len() {
4151                    rest.push(arg);
4152                } else if next < names.len() {
4153                    slots[next] = Some(arg);
4154                    next += 1;
4155                } else {
4156                    return Err(RuntimeError::new(format!(
4157                        "`{what}` takes {} argument(s), but more were given",
4158                        names.len()
4159                    ))
4160                    .at(arg.span));
4161                }
4162            }
4163        }
4164    }
4165    Ok((slots, rest))
4166}
4167
4168/// Rejects `var` and `...` where only a plain value is meaningful.
4169fn plain_values(args: Vec<EvaluatedArg>, what: &str) -> Result<Vec<Value>, RuntimeError> {
4170    let mut values = Vec::with_capacity(args.len());
4171    for arg in &args {
4172        values.push(value_of(arg, what, arg.span)?);
4173    }
4174    Ok(values)
4175}
4176
4177fn value_of(arg: &EvaluatedArg, what: &str, span: Span) -> Result<Value, RuntimeError> {
4178    match &arg.slot {
4179        ArgSlot::Value(value) => Ok(value.clone()),
4180        ArgSlot::Alias(_) => Err(RuntimeError::new(format!(
4181            "`{what}` does not take a `var` argument"
4182        ))
4183        .at(span)
4184        .with_rule(
4185            "A `var` parameter is a non-escaping inout alias, marked at both the declaration and the call site.",
4186        )),
4187    }
4188}
4189
4190// ------------------------------------------------------------- free names
4191
4192/// Every name a block can read from the environment around it.
4193///
4194/// The set over-approximates: a name the body binds for itself is listed too.
4195/// Over-approximating is safe, because a closure that captures a name it never
4196/// reads is only holding one value more than it needs, while missing one would
4197/// leave the body unable to resolve it.
4198fn mention_block(block: &Block, out: &mut BTreeSet<String>) {
4199    for stmt in &block.statements {
4200        match &stmt.kind {
4201            StmtKind::Let { value, .. } => mention_expr(value, out),
4202            StmtKind::Expr(expr) => mention_expr(expr, out),
4203            StmtKind::Item(item) => match &item.kind {
4204                ItemKind::Fn(decl) => mention_fn(decl, out),
4205                ItemKind::Impl(block) => {
4206                    for item in &block.items {
4207                        if let ItemKind::Fn(decl) = &item.kind {
4208                            mention_fn(decl, out);
4209                        }
4210                    }
4211                }
4212                // A trait's default bodies are reached through the
4213                // conformances resolution recorded them under, not through
4214                // this closure's environment.
4215                ItemKind::Struct(_)
4216                | ItemKind::Enum(_)
4217                | ItemKind::Trait(_)
4218                | ItemKind::TypeAlias(_) => {}
4219            },
4220        }
4221    }
4222    if let Some(tail) = &block.tail {
4223        mention_expr(tail, out);
4224    }
4225}
4226
4227fn mention_fn(decl: &FnDecl, out: &mut BTreeSet<String>) {
4228    mention_params(&decl.params, out);
4229    mention_block(&decl.body, out);
4230}
4231
4232/// A default argument is evaluated by the callee, so the names it reads belong
4233/// to the body.
4234fn mention_params(params: &[Param], out: &mut BTreeSet<String>) {
4235    for param in params {
4236        if let Some(default) = &param.default {
4237            mention_expr(default, out);
4238        }
4239    }
4240}
4241
4242fn mention_expr(expr: &Expr, out: &mut BTreeSet<String>) {
4243    match &expr.kind {
4244        ExprKind::Int(_)
4245        | ExprKind::Float(_)
4246        | ExprKind::Bool(_)
4247        | ExprKind::Duration(_)
4248        | ExprKind::Unit => {}
4249        ExprKind::Str(parts) => {
4250            for part in parts {
4251                if let StrPart::Interpolation(inner) = part {
4252                    mention_expr(inner, out);
4253                }
4254            }
4255        }
4256        ExprKind::Ident(name) => {
4257            out.insert(name.clone());
4258        }
4259        ExprKind::ArrayLit(items) => {
4260            for item in items {
4261                mention_expr(item, out);
4262            }
4263        }
4264        // A field name is not a binding; only the base can read one.
4265        ExprKind::Field { base, .. } => mention_expr(base, out),
4266        ExprKind::Call {
4267            callee,
4268            args,
4269            trailing,
4270            ..
4271        } => {
4272            mention_expr(callee, out);
4273            for arg in args {
4274                mention_expr(&arg.value, out);
4275            }
4276            if let Some(trailing) = trailing {
4277                mention_expr(trailing, out);
4278            }
4279        }
4280        ExprKind::Unary { operand, .. } => mention_expr(operand, out),
4281        ExprKind::Binary { lhs, rhs, .. } => {
4282            mention_expr(lhs, out);
4283            mention_expr(rhs, out);
4284        }
4285        ExprKind::Assign { target, value, .. } => {
4286            mention_expr(target, out);
4287            mention_expr(value, out);
4288        }
4289        ExprKind::Try(inner) | ExprKind::Await(inner) => mention_expr(inner, out),
4290        ExprKind::Block(block) => mention_block(block, out),
4291        ExprKind::If {
4292            condition,
4293            then_branch,
4294            else_branch,
4295        } => {
4296            mention_expr(condition, out);
4297            mention_block(then_branch, out);
4298            if let Some(branch) = else_branch {
4299                mention_expr(branch, out);
4300            }
4301        }
4302        ExprKind::Match { scrutinee, arms } => {
4303            mention_expr(scrutinee, out);
4304            for arm in arms {
4305                mention_pattern(&arm.pattern, out);
4306                mention_expr(&arm.body, out);
4307            }
4308        }
4309        ExprKind::For { iterable, body, .. } => {
4310            mention_expr(iterable, out);
4311            mention_block(body, out);
4312        }
4313        ExprKind::While { condition, body } => {
4314            mention_expr(condition, out);
4315            mention_block(body, out);
4316        }
4317        ExprKind::Return(inner) | ExprKind::Break(inner) => {
4318            if let Some(inner) = inner {
4319                mention_expr(inner, out);
4320            }
4321        }
4322        ExprKind::Continue => {}
4323        ExprKind::Lambda { params, body, .. } => {
4324            mention_params(params, out);
4325            mention_block(body, out);
4326        }
4327        // The scope name is bound by the `scope`, so it shadows anything the
4328        // surrounding environment holds under that name.
4329        ExprKind::Scope { body, .. } => mention_block(body, out),
4330        ExprKind::Range { start, end, .. } => {
4331            mention_expr(start, out);
4332            mention_expr(end, out);
4333        }
4334    }
4335}
4336
4337/// Pattern bindings are binders, so only a literal pattern reads a name.
4338fn mention_pattern(pattern: &Pattern, out: &mut BTreeSet<String>) {
4339    match &pattern.kind {
4340        PatternKind::Wildcard | PatternKind::Binding(_) => {}
4341        PatternKind::Literal(expr) => mention_expr(expr, out),
4342        PatternKind::Variant { payload, .. } => {
4343            for sub in payload {
4344                mention_pattern(sub, out);
4345            }
4346        }
4347    }
4348}
4349
4350// ------------------------------------------------------------------ tasks
4351
4352/// A stable key for a task's trace id, valid for as long as some `Rc<Task>`
4353/// keeps the task alive — which every task the interpreter still holds a
4354/// handle to does.
4355/// Runs one spawned task's body on its own thread.
4356///
4357/// The body arrives as a [`Transfer`] and the value leaves as one: both
4358/// directions are a task boundary, so both are the copy the task-safety rule
4359/// demands. A task that produces a value no boundary may carry is reported
4360/// here rather than handing the value to a thread that cannot own it.
4361fn run_task(
4362    runtime: Runtime,
4363    id: u64,
4364    cancellation: Cancellation,
4365    body: Transfer,
4366    span: Span,
4367) -> TaskOutcome {
4368    let mut interpreter = Interpreter::for_task(&runtime, id, cancellation.clone());
4369    interpreter.timings.push(Timing::start());
4370    let result = interpreter.call_value_slots(body.into_value(), Vec::new(), span);
4371    let timing = interpreter
4372        .timings
4373        .pop()
4374        .expect("a task pushes exactly the one timing it pops");
4375    // This task's heap ends with this thread. What it did joins the run's
4376    // totals, and what it was holding stops counting against the run's memory
4377    // budget, before the value it produced crosses back.
4378    interpreter.retire_heap();
4379    task::finished(&runtime, id, &cancellation, span, result, timing.cpu())
4380}
4381
4382/// What an entry that returned `Err(...)` said, for the run's terminal event.
4383///
4384/// The `Error` inside prints as its own message, which is the same text `cove
4385/// run` reports and the same text the program would have printed, so a trace
4386/// and a terminal say the same thing about the same failure.
4387pub(crate) fn returned_error_message(value: &Value) -> Option<String> {
4388    let Value(Repr::Enum(result)) = value else {
4389        return None;
4390    };
4391    result.payload.first().map(ToString::to_string)
4392}
4393
4394/// The way back into a running program for a host call that was handed work.
4395///
4396/// [`crate::host::Reentry`] is the whole of what a host may do with a Cove
4397/// callback, and this is its one real implementation. The callback runs on
4398/// this interpreter — this task's stack, this task's heap, this run's budget
4399/// — because a host that ran Cove code anywhere else would be running it
4400/// outside the controls the run was given.
4401///
4402/// Holding `&mut Interpreter` is what makes the rest of that trait's contract
4403/// true rather than merely stated. There can be one of these per host call
4404/// and it cannot be moved to another thread, so a host cannot use its way
4405/// back concurrently; it borrows a frame of the calling task, so a host
4406/// cannot keep it; and every level of nesting is another one of these further
4407/// down the same native stack, which is what [`MAX_REENTRY_DEPTH`] counts.
4408struct Callback<'i, 'a> {
4409    interpreter: &'i mut Interpreter<'a>,
4410    /// Where the host call that is running this callback was written, so a
4411    /// failure inside it points at the call rather than at nothing.
4412    span: Span,
4413}
4414
4415impl Callback<'_, '_> {
4416    fn run(&mut self, callee: &Value, args: Vec<Value>) -> Result<Value, RuntimeError> {
4417        let span = self.span;
4418        // The count is raised for as long as the callback runs and dropped
4419        // when it returns, so a host that runs its callback twice pays for
4420        // one level twice over rather than for two levels at once. What is
4421        // bounded is how many are stacked on this thread, because that is
4422        // what is spending the native stack.
4423        if self.interpreter.reentry_depth >= MAX_REENTRY_DEPTH {
4424            return Err(reentry_too_deep(span));
4425        }
4426        let args: Vec<EvaluatedArg> = args
4427            .into_iter()
4428            .map(|value| EvaluatedArg {
4429                label: None,
4430                spread: false,
4431                slot: ArgSlot::Value(value),
4432                span,
4433            })
4434            .collect();
4435        self.interpreter.reentry_depth += 1;
4436        let result = self
4437            .interpreter
4438            .call_value_slots(callee.clone(), args, span);
4439        self.interpreter.reentry_depth -= 1;
4440        // An `async fn` answers with a task. A host was handed a callback and
4441        // not a task, so settling it here is what `await` would have done at
4442        // the call site the host is standing in for.
4443        match result? {
4444            Value(Repr::Task(task)) => self.interpreter.settle(&task, span),
4445            other => Ok(other),
4446        }
4447    }
4448}
4449
4450impl Reentry for Callback<'_, '_> {
4451    fn call(&mut self, callee: &Value, args: Vec<Value>) -> Result<Value, RuntimeError> {
4452        self.run(callee, args)
4453    }
4454
4455    fn call_until(
4456        &mut self,
4457        callee: &Value,
4458        args: Vec<Value>,
4459        stop: &Cancellation,
4460    ) -> Result<Value, RuntimeError> {
4461        self.interpreter.stops.push(stop.clone());
4462        let result = self.run(callee, args);
4463        self.interpreter.stops.pop();
4464        result
4465    }
4466
4467    /// Everything [`Interpreter::charge_safepoint`] would stop on, asked from
4468    /// outside the interpreter.
4469    ///
4470    /// A host that is waiting is standing where a safepoint would be, so it
4471    /// is owed the same answer a safepoint gets: this task's own flag, the
4472    /// flag of every bounded call this thread is inside, and the run's own
4473    /// cancellation. Reading only the first would have told a host blocked
4474    /// inside a `clock.timeout` body that nothing was wrong, and told a host
4475    /// on the entry task — which has no flag of its own — that nothing was
4476    /// ever wrong.
4477    fn is_cancelled(&self) -> bool {
4478        if self
4479            .interpreter
4480            .cancellation
4481            .as_ref()
4482            .is_some_and(Cancellation::is_cancelled)
4483        {
4484            return true;
4485        }
4486        if self
4487            .interpreter
4488            .stops
4489            .iter()
4490            .any(Cancellation::is_cancelled)
4491        {
4492            return true;
4493        }
4494        self.interpreter
4495            .budget
4496            .as_ref()
4497            .is_some_and(Meter::is_cancelled)
4498    }
4499
4500    /// What the run's deadline leaves, read from the one budget that knows
4501    /// when the run started.
4502    ///
4503    /// A run with no deadline answers `None`, and one whose deadline has
4504    /// passed answers zero rather than wrapping: the subtraction saturates,
4505    /// so a host comparing the answer against zero is comparing against the
4506    /// only value that can mean "no time left".
4507    fn time_left(&self) -> Option<Duration> {
4508        let budget = self.interpreter.budget.as_ref()?;
4509        let deadline = budget.limits().deadline?;
4510        Some(deadline.saturating_sub(budget.elapsed()))
4511    }
4512
4513    /// The task whose stack this call is standing on, which is the task the
4514    /// boundary records the call against.
4515    ///
4516    /// A callback runs on the calling task, so a host call made from inside
4517    /// one is made by the same task as the call that ran it: the answer does
4518    /// not change with nesting, and a trace of a nested call attributes it
4519    /// where the work is actually being charged.
4520    fn task(&self) -> u64 {
4521        self.interpreter.task_id()
4522    }
4523}
4524
4525/// The two stop flags a thread owns, asked in one place so that both
4526/// backends ask them the same way and in the same order.
4527///
4528/// `cancellation` is this task's own, set by `Task::cancel` and by a scope
4529/// that left with children still running; `stops` are the flags of the
4530/// bounded calls this thread is inside, innermost last. Neither is the run's
4531/// cancellation, which lives in the shared [`crate::budget::Budget`] and is
4532/// read there.
4533///
4534/// # Why this is asked before a Host call and not only at safepoints
4535///
4536/// A safepoint is where a *budget* is measured, and a budget can only be
4537/// measured where the work is counted. A flag is not measured: it is already
4538/// true or already false, and reading it costs an atomic load. So the two
4539/// are not on the same schedule, and the Host boundary is the place where
4540/// the difference matters — an effect a host performs is the one thing a
4541/// stopped run cannot take back.
4542///
4543/// [`crate::budget::Budget::charge_host_call`] already refuses a call made
4544/// by a run that was cancelled or is past its deadline. It cannot ask these
4545/// two, because a `Budget` is shared by every task of a run and these belong
4546/// to one thread. So the backend asks them, at the same boundary, and a
4547/// cancelled task or a bounded call that has been asked to stop performs no
4548/// further Host effect.
4549pub(crate) fn stopped_here(
4550    cancellation: Option<&Cancellation>,
4551    stops: &[Cancellation],
4552    span: Span,
4553) -> Result<(), RuntimeError> {
4554    if cancellation.is_some_and(Cancellation::is_cancelled) {
4555        return Err(task_cancelled(span));
4556    }
4557    // A bounded call's flag stops only the body it bounds. The host that
4558    // raised it turns the stop into the answer it promised — a timeout
4559    // reports that it timed out — so this need only say that the body is not
4560    // to continue.
4561    if stops.iter().any(Cancellation::is_cancelled) {
4562        return Err(work_stopped(span));
4563    }
4564    Ok(())
4565}
4566
4567/// A host tried to run a Cove callback more levels deep than the runtime
4568/// allows.
4569///
4570/// Here rather than beside either caller because both backends raise it and
4571/// it is one fact about the run: `MAX_REENTRY_DEPTH` bounds how many host
4572/// calls running a callback may be stacked on one thread, and its
4573/// documentation is where the reasoning is. A backend that wrote these words
4574/// out for itself would be a second copy of a sentence that has to match.
4575pub(crate) fn reentry_too_deep(span: Span) -> RuntimeError {
4576    RuntimeError::new(format!(
4577        "reentry depth limit of {MAX_REENTRY_DEPTH} reached while a host ran a Cove callback"
4578    ))
4579    .at(span)
4580    .with_rule("A host runs a Cove callback on the calling task's own stack, and how deep that may nest is a runtime control.")
4581    .with_help("a callback is Cove code and may call a host that is handed work of its own; that nesting is what this bounds")
4582}
4583
4584/// Work a host call bounded, stopped at a safepoint because the bound was
4585/// reached.
4586///
4587/// The host that raised the flag reports what the bound was — `clock.timeout`
4588/// says it timed out — so this message is only what the body itself can say.
4589pub(crate) fn work_stopped(span: Span) -> RuntimeError {
4590    RuntimeError::new("this work was stopped before it finished")
4591        .at(span)
4592        .with_rule(
4593            "A host call that bounds the work it was given stops that work at its next safepoint.",
4594        )
4595}
4596
4597/// A task that stopped because its own cancellation was requested.
4598///
4599/// Both backends raise this one, at the same safepoint, so a task the
4600/// program cancelled stops in the same words whichever ran it.
4601pub(crate) fn task_cancelled(span: Span) -> RuntimeError {
4602    RuntimeError::new("this task was cancelled")
4603        .at(span)
4604        .with_rule("Leaving a task scope waits for or cancels its child tasks.")
4605}
4606
4607fn expect_no_arguments(what: &str, values: &[Value], span: Span) -> Result<(), RuntimeError> {
4608    if values.is_empty() {
4609        return Ok(());
4610    }
4611    Err(RuntimeError::new(format!(
4612        "`{what}` takes no arguments, but {} were given",
4613        values.len()
4614    ))
4615    .at(span))
4616}
4617
4618// ------------------------------------------------------------ diagnostics
4619
4620fn unsupported(what: &str, span: Span) -> RuntimeError {
4621    RuntimeError::new(format!(
4622        "{what} is not implemented yet in the MVP interpreter"
4623    ))
4624    .at(span)
4625    .with_rule("The MVP interpreter runs the subset of Cove that the MVP defines.")
4626}
4627
4628/// `Int` arithmetic overflowed.
4629///
4630/// `crate::builtins` reports `Int.abs()` on the most negative `Int` through
4631/// this too, rather than writing the same sentence out a second time: an
4632/// overflow is one rule, so it is one message wherever it is reached from.
4633/// The values `for` walks over `value`, in the order it walks them.
4634///
4635/// One function, so that this backend walks a collection one way rather
4636/// than in each of its callers' own words. It was not always agreed between
4637/// backends: the predecessor VM once lowered a sequence to a
4638/// `length()`/`get(i)` index walk, and a `Map` answers neither shape — it
4639/// walks as the `MapEntry` of each pair, and a `Set` in ascending order.
4640/// `cove_ir::lower`'s own walk states the same order independently now
4641/// (`crates/cove-ir/src/lower/walks.rs`), and the differential corpus is
4642/// what keeps the two agreeing.
4643pub(crate) fn items_of(value: Value, span: Span) -> Result<Vec<Value>, RuntimeError> {
4644    // Iteration reads a snapshot of the elements; rejecting structural
4645    // mutation during iteration is future work.
4646    match value {
4647        Value(Repr::Array(items)) => Ok(items.iter().cloned().collect()),
4648        Value(Repr::Vector(storage)) => Ok(storage.elements.borrow().clone()),
4649        // An empty or reversed range such as `3..<0` iterates zero times.
4650        Value(Repr::Range {
4651            start,
4652            end,
4653            inclusive_end,
4654        }) => Ok(RangeBounds::of(start, end, inclusive_end).items()),
4655        // A `Set` is `BTreeSet<MapKey>`-backed, so it iterates its
4656        // elements in ascending order, the same order `Display` shows.
4657        Value(Repr::Set(items)) => Ok(items.iter().map(|key| key.to_value()).collect()),
4658        // A `Map` iterates in ascending key order, matching its
4659        // `BTreeMap` storage. Each binding is a `MapEntry` carrying that
4660        // iteration's `key` and `value`, the same shape `Map.of` accepts.
4661        Value(Repr::Map(entries)) => Ok(entries
4662            .iter()
4663            .map(|(key, value)| {
4664                Value(Repr::Struct(Rc::new(StructValue {
4665                    type_name: MAP_ENTRY.name.into(),
4666                    fields: vec![
4667                        ("key".into(), key.to_value()),
4668                        ("value".into(), value.clone()),
4669                    ],
4670                    opaque: false,
4671                })))
4672            })
4673            .collect()),
4674        other => Err(RuntimeError::new(format!(
4675            "`for` iterates an `Array`, a `Vector`, a `Range`, a `Set`, or a `Map`, but found `{}`",
4676            other.type_name()
4677        ))
4678        .at(span)),
4679    }
4680}
4681
4682pub(crate) fn overflow(operation: &str, span: Span) -> RuntimeError {
4683    RuntimeError::new(format!("`Int` {operation} overflowed"))
4684        .at(span)
4685        .with_rule("Integer overflow is a broken invariant, not a wrapped result.")
4686}
4687
4688/// `Int` division or remainder was asked for zero.
4689///
4690/// Reachable from outside for the reason [`overflow`] is: the linear-memory
4691/// backend's own arithmetic keeps this message word for word rather than
4692/// sharing this function, because dividing by zero is one rule of the
4693/// language and not one rule per backend — the differential corpus is what
4694/// compares them.
4695pub(crate) fn divide_by_zero(operation: &str, span: Span) -> RuntimeError {
4696    RuntimeError::new(format!("`Int` {operation} by zero"))
4697        .at(span)
4698        .with_rule("Division and remainder by zero are broken invariants.")
4699}
4700
4701fn operator_type_error(op: BinaryOp, lhs: &Value, rhs: &Value, span: Span) -> RuntimeError {
4702    RuntimeError::new(format!(
4703        "`{}` is not defined for `{}` and `{}`",
4704        operator_text(op),
4705        lhs.type_name(),
4706        rhs.type_name()
4707    ))
4708    .at(span)
4709    .with_rule("There are no implicit numeric, string, or boolean conversions.")
4710}
4711
4712fn operator_text(op: BinaryOp) -> &'static str {
4713    match op {
4714        BinaryOp::Add => "+",
4715        BinaryOp::Sub => "-",
4716        BinaryOp::Mul => "*",
4717        BinaryOp::Div => "/",
4718        BinaryOp::Rem => "%",
4719        BinaryOp::Eq => "==",
4720        BinaryOp::Ne => "!=",
4721        BinaryOp::Lt => "<",
4722        BinaryOp::Le => "<=",
4723        BinaryOp::Gt => ">",
4724        BinaryOp::Ge => ">=",
4725        BinaryOp::Is => "is",
4726        BinaryOp::And => "&&",
4727        BinaryOp::Or => "||",
4728    }
4729}
4730
4731/// `a is b` where `a` and `b` share a type that has no shared-storage
4732/// identity to compare.
4733fn identity_not_available(value: &Value, span: Span) -> RuntimeError {
4734    RuntimeError::new(format!("identity is not available for `{}`", value.type_name()))
4735        .at(span)
4736        .with_rule("`==` means value equality. Identity, when available, is explicit.")
4737        .with_help(
4738            "`is` is defined for `Vector`; compare other values with `==`, or call `toArray()` for an independent copy",
4739        )
4740}
4741
4742/// `value.snapshot()` where `value` is a closure, a task, a task scope, a
4743/// host handle, or a struct or enum with no `impl Snapshot for Type`.
4744/// The source text `span` covers, for a diagnostic that quotes the code it is
4745/// about.
4746///
4747/// Both backends quote an assertion's condition, and both reach it through
4748/// here, so neither can word it differently from the other.
4749pub(crate) fn source_text(sources: &SourceMap, span: Span) -> &str {
4750    let file = sources.get(span.file);
4751    file.text
4752        .get(span.start as usize..span.end as usize)
4753        .unwrap_or("?")
4754}
4755
4756pub(crate) fn no_field(type_name: &str, field: &str, span: Span) -> RuntimeError {
4757    RuntimeError::new(format!("`{type_name}` has no field `{field}`")).at(span)
4758}
4759
4760pub(crate) fn not_a_struct(value: &Value, field: &str, span: Span) -> RuntimeError {
4761    RuntimeError::new(format!("`{}` has no field `{field}`", value.type_name()))
4762        .at(span)
4763        .with_rule("Only struct fields are places.")
4764}
4765
4766/// A `var self` receiver that is no place at all, which leaves nothing to
4767/// alias.
4768///
4769/// `cove-sema` refuses this before the run (ADR 0021), so no checked program
4770/// reaches it. It stays because deleting it would not leave this evaluator
4771/// doing something defined: a `var self` method binds the caller's place,
4772/// and with no place there is nothing to bind. That makes it the guard ADR
4773/// 0004 describes rather than a user-facing diagnostic.
4774fn var_self_needs_place(method: &str, receiver: &Expr, span: Span) -> RuntimeError {
4775    RuntimeError::new(format!(
4776        "`{method}` takes a `var self` receiver, but `{}` is not a place",
4777        describe_place(receiver)
4778    ))
4779    .at(span)
4780    .with_rule("A mutating receiver declares `var self` and mutates the caller's place.")
4781    .with_help("bind the value with `var` first, then call the method on that binding")
4782}
4783
4784fn expect_bool(value: Value, op: BinaryOp, span: Span) -> Result<bool, RuntimeError> {
4785    match value {
4786        Value(Repr::Bool(value)) => Ok(value),
4787        other => Err(RuntimeError::new(format!(
4788            "`{}` needs `Bool` operands, but found `{}`",
4789            operator_text(op),
4790            other.type_name()
4791        ))
4792        .at(span)
4793        .with_rule("There are no implicit boolean conversions.")),
4794    }
4795}
4796
4797fn expect_int(value: Value, what: &str, span: Span) -> Result<i64, RuntimeError> {
4798    match value {
4799        Value(Repr::Int(value)) => Ok(value),
4800        other => Err(RuntimeError::new(format!(
4801            "{what} must be an `Int`, but found `{}`",
4802            other.type_name()
4803        ))
4804        .at(span)),
4805    }
4806}
4807
4808/// How an lvalue is written in source, for diagnostics.
4809fn describe_place(expr: &Expr) -> String {
4810    match &expr.kind {
4811        ExprKind::Ident(name) => name.clone(),
4812        ExprKind::Field { base, name } => format!("{}.{}", describe_place(base), name.node),
4813        _ => "this expression".to_string(),
4814    }
4815}
4816
4817#[cfg(test)]
4818mod tests {
4819    use super::*;
4820    use std::collections::BTreeMap;
4821    use std::io::Write;
4822    use std::path::{Path, PathBuf};
4823
4824    use std::sync::Mutex;
4825    use std::time::Duration;
4826
4827    use cove_diag::Diagnostic;
4828    use cove_sema::config::Config;
4829    use cove_sema::package::{Module, Package, Unit};
4830
4831    use crate::budget::{Budget, Limits};
4832    use crate::host::{Console, Documents, Env as EnvHost, Grants, HostRegistry};
4833    use crate::trace::TraceSink;
4834
4835    /// A `console` sink the tests can read back.
4836    ///
4837    /// Synchronized because a host is reachable from every task of a run, and
4838    /// a test that spawns tasks prints from more than one thread.
4839    #[derive(Clone, Default)]
4840    struct Buffer(Arc<Mutex<Vec<u8>>>);
4841
4842    impl Write for Buffer {
4843        fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
4844            self.written().extend_from_slice(buf);
4845            Ok(buf.len())
4846        }
4847
4848        fn flush(&mut self) -> std::io::Result<()> {
4849            Ok(())
4850        }
4851    }
4852
4853    impl Buffer {
4854        fn written(&self) -> std::sync::MutexGuard<'_, Vec<u8>> {
4855            self.0.lock().expect("no test panics while printing")
4856        }
4857
4858        fn text(&self) -> String {
4859            String::from_utf8(self.written().clone()).expect("console output is UTF-8")
4860        }
4861    }
4862
4863    /// Parses `source` as the single unit of module `test`.
4864    fn program_of(source: &str) -> (Arc<SourceMap>, Arc<Program>) {
4865        let mut sources = SourceMap::new();
4866        let path = PathBuf::from("test/main.cove");
4867        let file = sources.add(path.clone(), source);
4868        let ast = cove_syntax::parse_file(&sources, file).expect("test source parses");
4869        let mut modules = BTreeMap::new();
4870        modules.insert(
4871            "test".to_string(),
4872            Module {
4873                name: "test".to_string(),
4874                dir: PathBuf::from("test"),
4875                units: vec![Unit { file, path, ast }],
4876            },
4877        );
4878        for (name, module) in cove_sema::stdlib::attach(&mut sources).expect("stdlib parses") {
4879            modules.insert(name, module);
4880        }
4881        let package = Package {
4882            root: PathBuf::new(),
4883            config: Config::default(),
4884            modules,
4885        };
4886        let program = cove_sema::resolve::resolve(&package).expect("test source resolves");
4887        (Arc::new(sources), Arc::new(program))
4888    }
4889
4890    /// Like [`program_of`], for source the checker is expected to reject:
4891    /// answers the type-checker's diagnostics instead of panicking on them.
4892    ///
4893    /// `resolve` alone does not run `Checker` — it settles names, imports,
4894    /// capabilities, and the call graph, and an arity mismatch is none of
4895    /// those. `cove_sema::typeck::check` is the separate pass that reports
4896    /// it, which is why this asks for it explicitly rather than reusing
4897    /// `program_of`'s `resolve(&package).expect(..)`.
4898    fn check_errors_of(source: &str) -> Vec<Diagnostic> {
4899        let mut sources = SourceMap::new();
4900        let path = PathBuf::from("test/main.cove");
4901        let file = sources.add(path.clone(), source);
4902        let ast = cove_syntax::parse_file(&sources, file).expect("test source parses");
4903        let mut modules = BTreeMap::new();
4904        modules.insert(
4905            "test".to_string(),
4906            Module {
4907                name: "test".to_string(),
4908                dir: PathBuf::from("test"),
4909                units: vec![Unit { file, path, ast }],
4910            },
4911        );
4912        for (name, module) in cove_sema::stdlib::attach(&mut sources).expect("stdlib parses") {
4913            modules.insert(name, module);
4914        }
4915        let package = Package {
4916            root: PathBuf::new(),
4917            config: Config::default(),
4918            modules,
4919        };
4920        let program = cove_sema::resolve::resolve(&package).expect("test source resolves");
4921        cove_sema::typeck::check(&package, &program)
4922            .into_iter()
4923            .filter(|d| d.severity == cove_diag::Severity::Error)
4924            .collect()
4925    }
4926
4927    /// Parses several modules, so one can `use` another.
4928    fn program_of_modules(modules: &[(&str, &str)]) -> (Arc<SourceMap>, Arc<Program>) {
4929        let mut sources = SourceMap::new();
4930        let mut map = BTreeMap::new();
4931        for (name, source) in modules {
4932            let path = PathBuf::from(format!("{name}/main.cove"));
4933            let file = sources.add(path.clone(), *source);
4934            let ast = cove_syntax::parse_file(&sources, file).expect("test source parses");
4935            map.insert(
4936                (*name).to_string(),
4937                Module {
4938                    name: (*name).to_string(),
4939                    dir: PathBuf::from(*name),
4940                    units: vec![Unit { file, path, ast }],
4941                },
4942            );
4943        }
4944        for (name, module) in cove_sema::stdlib::attach(&mut sources).expect("stdlib parses") {
4945            map.insert(name, module);
4946        }
4947        let package = Package {
4948            root: PathBuf::new(),
4949            config: Config::default(),
4950            modules: map,
4951        };
4952        let program = cove_sema::resolve::resolve(&package).expect("test package resolves");
4953        (Arc::new(sources), Arc::new(program))
4954    }
4955
4956    /// Runs `app.main` of a package written inline, with `console` granted.
4957    fn run_modules(modules: &[(&str, &str)]) -> Run {
4958        let (sources, program) = program_of_modules(modules);
4959        run_in(
4960            &program,
4961            &sources,
4962            "app",
4963            "main",
4964            &[],
4965            &["console"],
4966            BTreeMap::new(),
4967        )
4968    }
4969
4970    struct Run {
4971        value: Result<Value, RuntimeError>,
4972        output: String,
4973    }
4974
4975    impl Run {
4976        fn value(self) -> Value {
4977            self.value.expect("the program ran without a runtime error")
4978        }
4979
4980        fn error(self) -> RuntimeError {
4981            match self.value {
4982                Ok(value) => panic!("expected a runtime error, but the program returned {value}"),
4983                Err(error) => error,
4984            }
4985        }
4986    }
4987
4988    fn run_in(
4989        program: &Arc<Program>,
4990        sources: &Arc<SourceMap>,
4991        module: &str,
4992        entry: &str,
4993        args: &[&str],
4994        grants: &[&str],
4995        env: BTreeMap<String, String>,
4996    ) -> Run {
4997        let buffer = Buffer::default();
4998        let mut hosts = HostRegistry::new(Grants::new(grants.to_vec()));
4999        hosts.register(Box::new(Console::new(buffer.clone(), Buffer::default())));
5000        hosts.register(Box::new(EnvHost::new(env)));
5001        let runtime = Runtime::new(program.clone(), sources.clone(), Arc::new(hosts));
5002        let value = Interpreter::new(&runtime).run_entry(
5003            module,
5004            entry,
5005            args.iter().map(|a| (*a).into()).collect(),
5006        );
5007        Run {
5008            value,
5009            output: buffer.text(),
5010        }
5011    }
5012
5013    /// Runs `test.main` with `console` and `env` granted.
5014    fn run_entry_of(source: &str, entry: &str, args: &[&str]) -> Run {
5015        let (sources, program) = program_of(source);
5016        run_in(
5017            &program,
5018            &sources,
5019            "test",
5020            entry,
5021            args,
5022            &["console", "env"],
5023            BTreeMap::new(),
5024        )
5025    }
5026
5027    /// Runs `body` inside a `main` that returns `Result<Unit, Error>`.
5028    fn run_body(body: &str) -> Run {
5029        let source = format!(
5030            "use console.println\n\nexport fn main() -> Result<Unit, Error> {{\n{body}\n  Ok(())\n}}\n"
5031        );
5032        run_entry_of(&source, "main", &[])
5033    }
5034
5035    fn output_of(body: &str) -> String {
5036        run_body(body).output
5037    }
5038
5039    fn error_of(body: &str) -> RuntimeError {
5040        run_body(body).error()
5041    }
5042
5043    // ---------------------------------------------------- assertions
5044
5045    /// Runs `test.check`, a test-shaped function holding `body`, and returns
5046    /// the `Result` it produced.
5047    fn run_assertion(body: &str) -> Run {
5048        let source = format!("test fn check() -> Result<Unit, Error> {{\n{body}\n}}\n");
5049        let (sources, program) = program_of(&source);
5050        run_in(
5051            &program,
5052            &sources,
5053            "test",
5054            "check",
5055            &[],
5056            &[],
5057            BTreeMap::new(),
5058        )
5059    }
5060
5061    /// The message a failed assertion reported, or `None` when it held.
5062    fn assertion_message(body: &str) -> Option<String> {
5063        run_assertion(body)
5064            .value()
5065            .err_payload()
5066            .map(|payload| payload[0].to_string())
5067    }
5068
5069    #[test]
5070    fn a_holding_assertion_produces_ok() {
5071        assert!(run_assertion("  assert(1 + 1 == 2)").value().is_ok());
5072    }
5073
5074    #[test]
5075    fn a_failing_assertion_names_the_conditions_source_text() {
5076        assert_eq!(
5077            assertion_message("  assert(1 + 1 == 3)").as_deref(),
5078            Some("assertion failed: `1 + 1 == 3`")
5079        );
5080    }
5081
5082    #[test]
5083    fn a_failing_assertion_is_an_err_rather_than_a_panic() {
5084        // `?` propagates it, so the test's own `Err` is the assertion's.
5085        assert_eq!(
5086            assertion_message("  assert(false)?\n  Ok(())").as_deref(),
5087            Some("assertion failed: `false`")
5088        );
5089    }
5090
5091    #[test]
5092    fn assert_equal_reports_both_values_and_the_actual_expressions_source() {
5093        assert_eq!(assertion_message("  assertEqual(2 + 2, 4)"), None);
5094        assert_eq!(
5095            assertion_message("  assertEqual(2 + 2, 5)").as_deref(),
5096            Some("assertion failed: `2 + 2` is `4`, expected `5`")
5097        );
5098    }
5099
5100    #[test]
5101    fn a_failed_assertion_records_where_it_was_written() {
5102        let source = "test fn check() -> Result<Unit, Error> {\n  assert(1 == 2)\n}\n";
5103        let (sources, program) = program_of(source);
5104        let hosts = HostRegistry::new(Grants::default());
5105        let runtime = Runtime::new(program, sources.clone(), Arc::new(hosts));
5106        let mut interpreter = Interpreter::new(&runtime);
5107        interpreter
5108            .run_entry("test", "check", Vec::new())
5109            .expect("the assertion fails as an `Err`, not a runtime error");
5110        let (span, message) = interpreter
5111            .assertion_failure()
5112            .expect("the failure was recorded");
5113        assert_eq!(message, "assertion failed: `1 == 2`");
5114        assert_eq!(sources.get(span.file).line_col(span.start).0, 2);
5115    }
5116
5117    #[test]
5118    fn a_holding_assertion_records_nothing() {
5119        let source = "test fn check() -> Result<Unit, Error> {\n  assert(1 == 1)\n}\n";
5120        let (sources, program) = program_of(source);
5121        let hosts = HostRegistry::new(Grants::default());
5122        let runtime = Runtime::new(program, sources, Arc::new(hosts));
5123        let mut interpreter = Interpreter::new(&runtime);
5124        interpreter.run_entry("test", "check", Vec::new()).unwrap();
5125        assert!(interpreter.assertion_failure().is_none());
5126    }
5127
5128    #[test]
5129    fn assert_equal_refuses_the_comparison_that_equality_refuses() {
5130        let error = run_assertion("  assertEqual(1, \"1\")").error();
5131        assert!(
5132            error.message.contains("cannot compare `Int` with `String`"),
5133            "{}",
5134            error.message
5135        );
5136    }
5137
5138    #[test]
5139    fn a_module_declaration_wins_over_the_assertion_builtin() {
5140        let source = "fn assert(value: Int) -> Int {\n  value\n}\n\n                      export fn main() -> Int {\n  assert(7)\n}\n";
5141        let (sources, program) = program_of(source);
5142        let run = run_in(
5143            &program,
5144            &sources,
5145            "test",
5146            "main",
5147            &[],
5148            &[],
5149            BTreeMap::new(),
5150        );
5151        assert!(matches!(run.value(), Value(Repr::Int(7))));
5152    }
5153
5154    // -------------------------------------------------------- traits
5155
5156    /// A trait with one required and one defaulted method, two conforming
5157    /// types (one of which overrides the default), and a function for each
5158    /// dispatch form.
5159    const TRAITS: &str = r##"
5160use console.println
5161
5162trait Display {
5163  fn describe(self) -> String
5164
5165  fn label(self) -> String { "<{self.describe()}>" }
5166}
5167
5168struct Booking(id: Int)
5169
5170struct Receipt(total: Int)
5171
5172impl Display for Booking {
5173  fn describe(self) -> String { "booking {self.id}" }
5174  fn label(self) -> String { "#{self.id}" }
5175}
5176
5177impl Display for Receipt {
5178  fn describe(self) -> String { "receipt for {self.total}" }
5179}
5180
5181fn render<T: Display>(value: T) -> String {
5182  "{value.label()} / {value.describe()}"
5183}
5184
5185fn renderAll(values: Array<dyn Display>) -> String {
5186  var out = Vector.of("")
5187  for value in values {
5188    out.push(value.label())
5189  }
5190  "{out.toArray()}"
5191}
5192"##;
5193
5194    fn run_with_traits(body: &str) -> Run {
5195        let source =
5196            format!("{TRAITS}\nexport fn main() -> Result<Unit, Error> {{\n{body}\n  Ok(())\n}}\n");
5197        run_entry_of(&source, "main", &[])
5198    }
5199
5200    #[test]
5201    fn a_default_body_runs_unless_the_conformance_overrides_it() {
5202        let output = run_with_traits(
5203            "  console.println(render(Booking(id: 7)))?\n  console.println(render(Receipt(total: 12)))?",
5204        )
5205        .output;
5206        assert_eq!(
5207            output,
5208            "#7 / booking 7\n<receipt for 12> / receipt for 12\n"
5209        );
5210    }
5211
5212    #[test]
5213    fn dynamic_dispatch_finds_the_implementation_from_the_value() {
5214        // One call site, two concrete types, two different implementations —
5215        // including one that runs the trait's default body.
5216        let output = run_with_traits(
5217            "  let mixed: Array<dyn Display> = [Booking(id: 1), Receipt(total: 2)]\n  console.println(renderAll(mixed))?",
5218        )
5219        .output;
5220        assert_eq!(output, "[, #1, <receipt for 2>]\n");
5221    }
5222
5223    #[test]
5224    fn a_dyn_value_carries_its_concrete_value_and_its_trait() {
5225        let (sources, program) = program_of(&format!(
5226            "{TRAITS}\nexport fn main() -> dyn Display {{\n  Booking(id: 3)\n}}\n"
5227        ));
5228        let value = run_in(
5229            &program,
5230            &sources,
5231            "test",
5232            "main",
5233            &[],
5234            &["console"],
5235            BTreeMap::new(),
5236        )
5237        .value();
5238        let Value(Repr::Dyn(trait_object)) = &value else {
5239            panic!("expected a trait object, found {value:?}");
5240        };
5241        assert_eq!(&*trait_object.trait_name, "test.Display");
5242        assert_eq!(trait_object.value.type_name(), "test.Booking");
5243        assert_eq!(value.type_name(), "dyn test.Display");
5244        // A trait object shows the value it holds: the wrapper is a
5245        // representation, not something the program put there.
5246        assert_eq!(value.to_string(), "Booking(id: 3)");
5247    }
5248
5249    #[test]
5250    fn a_trait_object_keys_as_the_value_it_holds() {
5251        // `==` looks through the wrapper, so keying has to look through it
5252        // too: two values the language calls equal have to be usable in the
5253        // same places. The written `dyn Display` below is wrapped and the
5254        // one the function value produces is not, and neither difference is
5255        // one a program is allowed to see.
5256        let output = run_with_traits(
5257            "  let written: dyn Display = Booking(id: 1)\n  let make: fn(Int) -> dyn Display = fn(id) { Booking(id: id) }\n  let inferred = make(1)\n  console.println(\"{written == inferred}\")?\n  console.println(\"{Set.of(written) == Set.of(inferred)}\")?",
5258        )
5259        .output;
5260        assert_eq!(output, "true\ntrue\n");
5261    }
5262
5263    #[test]
5264    fn a_trait_object_is_still_incomparable_with_an_unrelated_value() {
5265        // The wrapper explains one mismatch and no other. Where the checker
5266        // abstained about one side — a host operation whose schema declares
5267        // `Any`, say — an unknown matches every type, so nothing static
5268        // refused the comparison and this guard is the only thing left. It
5269        // must report, not answer `false`.
5270        let (sources, program) = program_of(&format!(
5271            "{TRAITS}\nexport fn main() -> dyn Display {{\n  Booking(id: 3)\n}}\n"
5272        ));
5273        let object = run_in(
5274            &program,
5275            &sources,
5276            "test",
5277            "main",
5278            &[],
5279            &["console"],
5280            BTreeMap::new(),
5281        )
5282        .value();
5283        let span = Span::new(cove_diag::FileId(0), 0, 0);
5284        let error = binary(BinaryOp::Eq, object.clone(), Value(Repr::Int(1)), span)
5285            .expect_err("a trait object and an `Int` are not the same type");
5286        assert_eq!(error.message, "cannot compare `test.Booking` with `Int`");
5287        // Two trait objects over different concrete types keep answering
5288        // `false`, which is what dropping the guard was for.
5289        let other = Value(Repr::Dyn(Rc::new(DynValue {
5290            trait_name: "test.Display".into(),
5291            value: Value(Repr::Struct(Rc::new(StructValue {
5292                type_name: "test.Receipt".into(),
5293                fields: vec![("total".into(), Value(Repr::Int(2)))],
5294                opaque: false,
5295            }))),
5296        })));
5297        let answer = binary(BinaryOp::Eq, object, other, span)
5298            .expect("two trait objects at one trait are comparable");
5299        assert!(answer.eq_value(&Value(Repr::Bool(false))));
5300    }
5301
5302    #[test]
5303    fn static_and_dynamic_dispatch_reach_the_same_implementation() {
5304        let output = run_with_traits(
5305            "  let one: dyn Display = Booking(id: 5)\n  console.println(render(Booking(id: 5)))?\n  console.println(\"{one.label()} / {one.describe()}\")?",
5306        )
5307        .output;
5308        let lines: Vec<&str> = output.lines().collect();
5309        assert_eq!(lines[0], lines[1]);
5310    }
5311
5312    #[test]
5313    fn a_trait_object_is_equal_to_one_holding_an_equal_value() {
5314        let output = run_with_traits(
5315            "  let a: dyn Display = Booking(id: 1)\n  let b: dyn Display = Booking(id: 1)\n  let c: dyn Display = Receipt(total: 1)\n  console.println(\"{a == b} {a == c}\")?",
5316        )
5317        .output;
5318        assert_eq!(output, "true false\n");
5319    }
5320
5321    // ------------------------------------------------------------ imports
5322
5323    /// The module a body runs in is the module that declares it, so an
5324    /// imported function resolves its own names where it was written.
5325    #[test]
5326    fn an_imported_function_runs_in_the_module_that_declares_it() {
5327        let run = run_modules(&[
5328            (
5329                "greet",
5330                "use console.println\n\nfn punctuation() -> String {\n  \"!\"\n}\n\n\
5331                 /// Greets by name.\nexport fn greeting(name: String) -> String {\n  \"Hello, {name}{punctuation()}\"\n}\n",
5332            ),
5333            (
5334                "app",
5335                "use console.println\nuse greet.greeting\n\n\
5336                 /// Entry point.\nexport fn main() -> Result<Unit, Error> {\n  console.println(greeting(\"world\"))?\n  Ok(())\n}\n",
5337            ),
5338        ]);
5339        assert_eq!(run.output, "Hello, world!\n");
5340    }
5341
5342    #[test]
5343    fn a_module_imported_whole_is_called_qualified() {
5344        let run = run_modules(&[
5345            (
5346                "greet",
5347                "/// Greets by name.\nexport fn greeting(name: String) -> String {\n  \"Hello, {name}!\"\n}\n",
5348            ),
5349            (
5350                "app",
5351                "use console.println\nuse greet\n\n\
5352                 /// Entry point.\nexport fn main() -> Result<Unit, Error> {\n  console.println(greet.greeting(\"world\"))?\n  Ok(())\n}\n",
5353            ),
5354        ]);
5355        assert_eq!(run.output, "Hello, world!\n");
5356    }
5357
5358    #[test]
5359    fn an_imported_struct_is_constructed_and_its_methods_run() {
5360        let run = run_modules(&[
5361            (
5362                "booking",
5363                "/// A booking.\nexport struct Booking {\n  id: String\n}\n\n\
5364                 impl Booking {\n  /// The id, in a sentence.\n  export fn describe(self) -> String {\n    \"booking {self.id}\"\n  }\n}\n",
5365            ),
5366            (
5367                "app",
5368                "use console.println\nuse booking.Booking\n\n\
5369                 /// Entry point.\nexport fn main() -> Result<Unit, Error> {\n  \
5370                 let made = Booking(id: \"7\")\n  console.println(made.describe())?\n  Ok(())\n}\n",
5371            ),
5372        ]);
5373        assert_eq!(run.output, "booking 7\n");
5374    }
5375
5376    /// A value carries the module that declares its type, so a method of an
5377    /// imported type dispatches even when the value crossed a boundary.
5378    #[test]
5379    fn an_imported_type_s_value_keeps_its_methods_across_a_boundary() {
5380        let run = run_modules(&[
5381            (
5382                "booking",
5383                "/// A booking.\nexport struct Booking {\n  id: String\n}\n\n\
5384                 impl Booking {\n  /// The id, in a sentence.\n  export fn describe(self) -> String {\n    \"booking {self.id}\"\n  }\n}\n\n\
5385                 /// Makes one.\nexport fn make() -> Booking {\n  Booking(id: \"9\")\n}\n",
5386            ),
5387            (
5388                "app",
5389                "use console.println\nuse booking.make\n\n\
5390                 /// Entry point.\nexport fn main() -> Result<Unit, Error> {\n  console.println(make().describe())?\n  Ok(())\n}\n",
5391            ),
5392        ]);
5393        assert_eq!(run.output, "booking 9\n");
5394    }
5395
5396    #[test]
5397    fn an_imported_enum_s_cases_are_built_and_matched() {
5398        let run = run_modules(&[
5399            (
5400                "levels",
5401                "/// Levels.\nexport enum LogLevel {\n  Debug\n  Info\n}\n",
5402            ),
5403            (
5404                "app",
5405                "use console.println\nuse levels.LogLevel\n\n\
5406                 /// Names a level.\nfn name(level: LogLevel) -> String {\n  \
5407                 match level {\n    LogLevel.Debug => \"debug\"\n    LogLevel.Info => \"info\"\n  }\n}\n\n\
5408                 /// Entry point.\nexport fn main() -> Result<Unit, Error> {\n  console.println(name(LogLevel.Info))?\n  Ok(())\n}\n",
5409            ),
5410        ]);
5411        assert_eq!(run.output, "info\n");
5412    }
5413
5414    /// An enum reached through a module imported whole: `levels.LogLevel`
5415    /// names the type, and the case follows it.
5416    #[test]
5417    fn an_enum_case_is_reached_through_a_module_imported_whole() {
5418        let run = run_modules(&[
5419            (
5420                "levels",
5421                "/// Levels.\nexport enum LogLevel {\n  Debug\n  Info\n}\n",
5422            ),
5423            (
5424                "app",
5425                "use console.println\nuse levels\n\n\
5426                 /// Entry point.\nexport fn main() -> Result<Unit, Error> {\n  console.println(\"{levels.LogLevel.Info}\")?\n  Ok(())\n}\n",
5427            ),
5428        ]);
5429        assert_eq!(run.output, "Info\n");
5430    }
5431
5432    /// An imported function is an ordinary handle value, so it can be passed
5433    /// where any other closure can.
5434    #[test]
5435    fn an_imported_function_is_an_ordinary_value() {
5436        let run = run_modules(&[
5437            (
5438                "greet",
5439                "/// Greets by name.\nexport fn greeting(name: String) -> String {\n  \"Hello, {name}!\"\n}\n",
5440            ),
5441            (
5442                "app",
5443                "use console.println\nuse greet\n\n\
5444                 /// Applies `f`.\nfn apply(f: fn(String) -> String) -> String {\n  f(\"world\")\n}\n\n\
5445                 /// Entry point.\nexport fn main() -> Result<Unit, Error> {\n  console.println(apply(greet.greeting))?\n  Ok(())\n}\n",
5446            ),
5447        ]);
5448        assert_eq!(run.output, "Hello, world!\n");
5449    }
5450
5451    /// `export` is the whole of a module's boundary: a qualified name
5452    /// reaches exactly what a `use` of it would.
5453    #[test]
5454    fn a_qualified_name_that_is_not_exported_is_refused() {
5455        let (sources, program) = program_of_modules(&[
5456            (
5457                "greet",
5458                "fn secret() -> String {\n  \"s\"\n}\n\n/// Greets.\nexport fn greeting() -> String {\n  \"hi\"\n}\n",
5459            ),
5460            (
5461                "app",
5462                "use greet\n\n/// Entry point.\nexport fn main() -> String {\n  greet.secret()\n}\n",
5463            ),
5464        ]);
5465        let error = run_in(
5466            &program,
5467            &sources,
5468            "app",
5469            "main",
5470            &[],
5471            &["console"],
5472            BTreeMap::new(),
5473        )
5474        .error();
5475        assert!(error.message.contains("not exported"), "{}", error.message);
5476    }
5477
5478    #[test]
5479    fn a_module_used_as_a_value_is_refused() {
5480        let (sources, program) = program_of_modules(&[
5481            (
5482                "greet",
5483                "/// Greets.\nexport fn greeting() -> String {\n  \"hi\"\n}\n",
5484            ),
5485            (
5486                "app",
5487                "use greet\n\n/// Entry point.\nexport fn main() -> String {\n  let m = greet\n  \"x\"\n}\n",
5488            ),
5489        ]);
5490        let error = run_in(
5491            &program,
5492            &sources,
5493            "app",
5494            "main",
5495            &[],
5496            &["console"],
5497            BTreeMap::new(),
5498        )
5499        .error();
5500        assert!(
5501            error.message.contains("is a module, not a value"),
5502            "{}",
5503            error.message
5504        );
5505    }
5506
5507    /// A host call inside an imported function is charged to the grant the
5508    /// host gave the entry, not to the module that wrote the call.
5509    #[test]
5510    fn a_host_call_inside_an_imported_function_still_needs_the_grant() {
5511        let (sources, program) = program_of_modules(&[
5512            (
5513                "log",
5514                "use console.println\n\n/// Logs.\nexport fn log(msg: String) -> Result<Unit, Error> {\n  console.println(msg)\n}\n",
5515            ),
5516            (
5517                "app",
5518                "use log.log\n\n/// Entry point.\nexport fn main() -> Result<Unit, Error> {\n  log(\"hi\")?\n  Ok(())\n}\n",
5519            ),
5520        ]);
5521        let granted = run_in(
5522            &program,
5523            &sources,
5524            "app",
5525            "main",
5526            &[],
5527            &["console"],
5528            BTreeMap::new(),
5529        );
5530        assert_eq!(granted.output, "hi\n");
5531
5532        let denied = run_in(&program, &sources, "app", "main", &[], &[], BTreeMap::new());
5533        assert!(denied.value.is_err() || denied.output.is_empty());
5534    }
5535
5536    // ------------------------------------------ conformances across modules
5537
5538    const DISPLAY: &str = "\
5539/// Renders itself.
5540export trait Display {
5541  /// The full form.
5542  fn describe(self) -> String
5543
5544  /// A short form, defaulting to the full one.
5545  fn label(self) -> String { \"<{self.describe()}>\" }
5546}
5547
5548/// Renders anything that conforms, through static dispatch.
5549export fn render<T: Display>(value: T) -> String {
5550  value.label()
5551}
5552
5553/// Renders through dynamic dispatch.
5554export fn renderDyn(value: dyn Display) -> String {
5555  value.label()
5556}
5557";
5558
5559    const BOOKING: &str = "\
5560/// A booking.
5561export struct Booking {
5562  id: Int
5563}
5564";
5565
5566    /// ADR 0006 allows the conformance where the type is declared, so the
5567    /// trait may be imported; both dispatch forms must reach it.
5568    #[test]
5569    fn a_conformance_to_an_imported_trait_dispatches_both_ways() {
5570        let booking = format!(
5571            "use display.Display\n\n{BOOKING}\nimpl Display for Booking {{\n  \
5572             /// The full form.\n  fn describe(self) -> String {{\n    \"booking {{self.id}}\"\n  }}\n}}\n"
5573        );
5574        let run = run_modules(&[
5575            ("display", DISPLAY),
5576            ("booking", &booking),
5577            (
5578                "app",
5579                "use console.println\nuse booking.Booking\nuse display.render\nuse display.renderDyn\n\n\
5580                 /// Entry point.\nexport fn main() -> Result<Unit, Error> {\n  \
5581                 let one = Booking(id: 7)\n  \
5582                 console.println(render(one))?\n  \
5583                 console.println(renderDyn(one))?\n  \
5584                 Ok(())\n}\n",
5585            ),
5586        ]);
5587        // The default body comes from the trait's module, the `describe` it
5588        // calls from the conformance's, and both dispatch forms agree.
5589        assert_eq!(run.output, "<booking 7>\n<booking 7>\n");
5590    }
5591
5592    /// And the reverse: the conformance is declared with the trait, for an
5593    /// imported type, so the type's methods do not all live with the type.
5594    #[test]
5595    fn a_conformance_to_an_imported_type_dispatches_both_ways() {
5596        let display = format!(
5597            "use booking.Booking\n\n{DISPLAY}\nimpl Display for Booking {{\n  \
5598             /// The full form.\n  fn describe(self) -> String {{\n    \"booking {{self.id}}\"\n  }}\n}}\n"
5599        );
5600        let run = run_modules(&[
5601            ("booking", BOOKING),
5602            ("display", &display),
5603            (
5604                "app",
5605                "use console.println\nuse booking.Booking\nuse display.render\nuse display.Display\n\n\
5606                 /// Entry point.\nexport fn main() -> Result<Unit, Error> {\n  \
5607                 let one = Booking(id: 7)\n  \
5608                 console.println(render(one))?\n  \
5609                 console.println(one.describe())?\n  \
5610                 let shown: dyn Display = one\n  \
5611                 console.println(shown.label())?\n  \
5612                 Ok(())\n}\n",
5613            ),
5614        ]);
5615        assert_eq!(run.output, "<booking 7>\nbooking 7\n<booking 7>\n");
5616    }
5617
5618    /// A `dyn` value names its trait by the module that declares it,
5619    /// wherever the conversion was written, so two `dyn` values of the same
5620    /// trait built in different modules are the same kind of value.
5621    #[test]
5622    fn a_dyn_value_names_its_trait_by_the_module_that_declares_it() {
5623        let booking = format!(
5624            "use display.Display\n\n{BOOKING}\nimpl Display for Booking {{\n  \
5625             /// The full form.\n  fn describe(self) -> String {{\n    \"b\"\n  }}\n}}\n\n\
5626             /// Wraps one here, in the module that declares the type.\n\
5627             export fn shown(value: Booking) -> dyn Display {{\n  value\n}}\n"
5628        );
5629        let (sources, program) = program_of_modules(&[
5630            ("display", DISPLAY),
5631            ("booking", &booking),
5632            (
5633                "app",
5634                "use booking.Booking\nuse booking.shown\nuse display.Display\n\n\
5635                 /// Entry point: wraps one here too.\n\
5636                 export fn main() -> Bool {\n  \
5637                 let here: dyn Display = Booking(id: 1)\n  \
5638                 here == shown(Booking(id: 1))\n}\n",
5639            ),
5640        ]);
5641        let run = run_in(
5642            &program,
5643            &sources,
5644            "app",
5645            "main",
5646            &[],
5647            &["console"],
5648            BTreeMap::new(),
5649        );
5650        assert_eq!(run.value().to_string(), "true");
5651    }
5652
5653    // ------------------------------------------------------------- rule 1
5654
5655    #[test]
5656    fn struct_fields_copy_and_vector_handles_alias() {
5657        let source = r#"
5658use console.println
5659
5660struct Draft {
5661  count: Int
5662  guests: Vector<String>
5663}
5664
5665export fn main() -> Result<Unit, Error> {
5666  var original = Draft(count: 1, guests: Vector.of("Alice"))
5667  var alias = original
5668  alias.count = 2
5669  alias.guests.push("Bob")
5670  console.println("{original.count} {alias.count}")?
5671  console.println("{original.guests.length()} {alias.guests.length()}")?
5672  Ok(())
5673}
5674"#;
5675        let run = run_entry_of(source, "main", &[]);
5676        assert_eq!(run.output, "1 2\n2 2\n");
5677    }
5678
5679    #[test]
5680    fn passing_a_struct_argument_copies_it() {
5681        let source = r#"
5682use console.println
5683
5684struct Point {
5685  x: Int
5686}
5687
5688fn shift(point: Point) -> Int {
5689  point.x
5690}
5691
5692export fn main() -> Result<Unit, Error> {
5693  var origin = Point(x: 1)
5694  let seen = shift(origin)
5695  origin.x = 9
5696  console.println("{seen} {origin.x}")?
5697  Ok(())
5698}
5699"#;
5700        assert_eq!(run_entry_of(source, "main", &[]).output, "1 9\n");
5701    }
5702
5703    // ------------------------------------------------------------- rule 2
5704
5705    // Assigning to a `let` binding, calling a `var self` method through
5706    // one, and passing one as a `var` argument were three tests here. ADR
5707    // 0021 made all three check-time errors and this evaluator stopped
5708    // refusing them, so a test here would assert that a program `cove check`
5709    // rejects still runs. `cove-sema` pins the rule now; what is left below
5710    // is what this evaluator still decides.
5711
5712    #[test]
5713    fn assigning_to_a_var_field_updates_the_place() {
5714        let source = r#"
5715use console.println
5716
5717struct Counter {
5718  value: Int
5719}
5720
5721export fn main() -> Result<Unit, Error> {
5722  var counter = Counter(value: 1)
5723  counter.value += 4
5724  console.println("{counter.value}")?
5725  Ok(())
5726}
5727"#;
5728        assert_eq!(run_entry_of(source, "main", &[]).output, "5\n");
5729    }
5730
5731    // ------------------------------------------------------------- rule 3
5732
5733    const COUNTER: &str = r#"
5734use console.println
5735
5736struct Counter {
5737  value: Int
5738}
5739
5740impl Counter {
5741  fn bump(var self) {
5742    self.value = self.value + 1
5743  }
5744
5745  fn read(self) -> Int {
5746    self.value
5747  }
5748}
5749"#;
5750
5751    #[test]
5752    fn var_self_mutation_is_visible_in_the_caller() {
5753        let source = format!(
5754            "{COUNTER}
5755export fn main() -> Result<Unit, Error> {{
5756  var counter = Counter(value: 1)
5757  counter.bump()
5758  counter.bump()
5759  console.println(\"{{counter.value}} {{counter.read()}}\")?
5760  Ok(())
5761}}
5762"
5763        );
5764        assert_eq!(run_entry_of(&source, "main", &[]).output, "3 3\n");
5765    }
5766
5767    /// A receiver that is no place at all leaves a `var self` method
5768    /// nothing to alias, so this evaluator still refuses it — as a guard
5769    /// rather than as a diagnostic, since `cove-sema` reports it first.
5770    #[test]
5771    fn var_self_on_a_temporary_is_rejected() {
5772        let source = format!(
5773            "{COUNTER}
5774export fn main() -> Result<Unit, Error> {{
5775  Counter(value: 1).bump()
5776  Ok(())
5777}}
5778"
5779        );
5780        let error = run_entry_of(&source, "main", &[]).error();
5781        assert!(
5782            error.message.contains("is not a place"),
5783            "{}",
5784            error.message
5785        );
5786    }
5787
5788    #[test]
5789    fn a_var_parameter_aliases_the_caller_place() {
5790        let source = r#"
5791use console.println
5792
5793fn fill(var output: Vector<Int>) {
5794  output.push(1)
5795  output.push(2)
5796}
5797
5798export fn main() -> Result<Unit, Error> {
5799  var items = Vector.of()
5800  fill(var items)
5801  console.println("{items}")?
5802  Ok(())
5803}
5804"#;
5805        assert_eq!(run_entry_of(source, "main", &[]).output, "[1, 2]\n");
5806    }
5807
5808    #[test]
5809    fn a_var_parameter_must_be_marked_at_the_call_site() {
5810        let source = r#"
5811fn fill(var output: Vector<Int>) {
5812  output.push(1)
5813}
5814
5815export fn main() -> Result<Unit, Error> {
5816  var items = Vector.of()
5817  fill(items)
5818  Ok(())
5819}
5820"#;
5821        let error = run_entry_of(source, "main", &[]).error();
5822        assert!(
5823            error.message.contains("declared `var`"),
5824            "{}",
5825            error.message
5826        );
5827        assert_eq!(error.help.as_deref(), Some("write `fill(var output)`"));
5828    }
5829
5830    // ------------------------------------------------------------- rule 4
5831
5832    #[test]
5833    fn array_literals_are_arrays_and_vector_of_builds_a_vector() {
5834        assert_eq!(
5835            output_of("  console.println(\"{[1, 2].length()} {Vector.of(1, 2, 3).length()}\")?"),
5836            "2 3\n"
5837        );
5838    }
5839
5840    #[test]
5841    fn freeze_consumes_uniquely_owned_storage() {
5842        let source = r#"
5843use console.println
5844
5845export fn main() -> Result<Unit, Error> {
5846  var items = Vector.of(1)
5847  items.push(2)
5848  let frozen = items.freeze()
5849  console.println("{frozen.length()} {frozen}")?
5850  Ok(())
5851}
5852"#;
5853        assert_eq!(run_entry_of(source, "main", &[]).output, "2 [1, 2]\n");
5854    }
5855
5856    #[test]
5857    fn a_frozen_vector_is_no_longer_usable() {
5858        let source = r#"
5859export fn main() -> Result<Unit, Error> {
5860  var items = Vector.of(1)
5861  let frozen = items.freeze()
5862  items.push(2)
5863  Ok(())
5864}
5865"#;
5866        let error = run_entry_of(source, "main", &[]).error();
5867        assert!(
5868            error.message.contains("already consumed"),
5869            "{}",
5870            error.message
5871        );
5872    }
5873
5874    #[test]
5875    fn freeze_on_aliased_storage_points_at_to_array() {
5876        let source = r#"
5877export fn main() -> Result<Unit, Error> {
5878  var items = Vector.of(1)
5879  var alias = items
5880  let frozen = items.freeze()
5881  Ok(())
5882}
5883"#;
5884        let error = run_entry_of(source, "main", &[]).error();
5885        assert!(error.message.contains("freeze()"), "{}", error.message);
5886        assert!(
5887            error.help.unwrap().contains("toArray()"),
5888            "the diagnostic names the O(n) fallback"
5889        );
5890    }
5891
5892    #[test]
5893    fn to_array_produces_an_independent_array() {
5894        let source = r#"
5895use console.println
5896
5897export fn main() -> Result<Unit, Error> {
5898  var items = Vector.of(1)
5899  let snapshot = items.toArray()
5900  items.push(2)
5901  console.println("{snapshot.length()} {items.length()}")?
5902  Ok(())
5903}
5904"#;
5905        assert_eq!(run_entry_of(source, "main", &[]).output, "1 2\n");
5906    }
5907
5908    // ------------------------------------------------------- `is` and `Snapshot`
5909
5910    #[test]
5911    fn is_compares_vector_storage_identity() {
5912        assert_eq!(
5913            output_of(
5914                "  var a = Vector.of(1, 2)\n  var b = a\n  var c = Vector.of(1, 2)\n  \
5915                 println(\"{a is b} {a is c}\")?"
5916            ),
5917            "true false\n"
5918        );
5919    }
5920
5921    #[test]
5922    fn is_rejects_a_type_mismatch_at_runtime() {
5923        let error = error_of("  println(\"{Vector.of(1) is 1}\")?");
5924        assert!(
5925            error.message.contains("cannot compare the identity"),
5926            "{}",
5927            error.message
5928        );
5929    }
5930
5931    #[test]
5932    fn is_rejects_a_value_type_at_runtime() {
5933        let error = error_of("  println(\"{1 is 1}\")?");
5934        assert_eq!(error.message, "identity is not available for `Int`");
5935    }
5936
5937    #[test]
5938    fn snapshot_of_a_vector_allocates_independent_storage() {
5939        let source = r#"
5940use console.println
5941
5942export fn main() -> Result<Unit, Error> {
5943  var original = Vector.of(1, 2)
5944  var copy = original.snapshot()
5945  copy.push(3)
5946  console.println("{original.length()} {copy.length()}")?
5947  Ok(())
5948}
5949"#;
5950        assert_eq!(run_entry_of(source, "main", &[]).output, "2 3\n");
5951    }
5952
5953    #[test]
5954    fn snapshot_recurses_into_a_vector_s_own_vector_elements() {
5955        let source = r#"
5956use console.println
5957
5958export fn main() -> Result<Unit, Error> {
5959  var inner = Vector.of(1)
5960  var outer = Vector.of(inner)
5961  var copy = outer.snapshot()
5962  var innerCopy = copy.get(0).unwrapOr(Vector.of())
5963  innerCopy.push(2)
5964  console.println("{inner.length()} {innerCopy.length()}")?
5965  Ok(())
5966}
5967"#;
5968        assert_eq!(run_entry_of(source, "main", &[]).output, "1 2\n");
5969    }
5970
5971    #[test]
5972    fn snapshot_dispatches_to_a_struct_s_own_conformance() {
5973        let source = r#"
5974use console.println
5975
5976struct Booking(id: Int)
5977
5978impl Snapshot for Booking {
5979  fn snapshot(self) -> Booking { Booking(id: self.id) }
5980}
5981
5982export fn main() -> Result<Unit, Error> {
5983  let booking = Booking(id: 1)
5984  console.println("{booking.snapshot()}")?
5985  Ok(())
5986}
5987"#;
5988        assert_eq!(run_entry_of(source, "main", &[]).output, "Booking(id: 1)\n");
5989    }
5990
5991    #[test]
5992    fn snapshot_is_not_implemented_for_a_closure() {
5993        let error =
5994            error_of("  let handler = fn(x: Int) { x }\n  println(\"{handler.snapshot()}\")?");
5995        assert_eq!(error.message, "`fn` does not implement `Snapshot`");
5996        assert!(error.rule.unwrap().contains("Closures"));
5997    }
5998
5999    /// `push` on something that is no place at all has nowhere to push to,
6000    /// so this evaluator still refuses it — as a guard rather than as a
6001    /// diagnostic, since `cove-sema` reports it first.
6002    #[test]
6003    fn push_on_a_temporary_is_rejected() {
6004        let source = r#"
6005export fn main() -> Result<Unit, Error> {
6006  Vector.of(1).push(2)
6007  Ok(())
6008}
6009"#;
6010        let error = run_entry_of(source, "main", &[]).error();
6011        assert!(
6012            error.message.contains("is not a place"),
6013            "{}",
6014            error.message
6015        );
6016    }
6017
6018    /// Every `var self` method the table declares is refused on a temporary,
6019    /// and the guard asks the shared table rather than a list written here.
6020    #[test]
6021    fn every_var_self_method_on_a_temporary_is_rejected() {
6022        for call in ["push(2)", "set(0, 2)", "pop()", "remove(0)"] {
6023            let source = format!(
6024                "
6025export fn main() -> Result<Unit, Error> {{
6026  Vector.of(1).{call}
6027  Ok(())
6028}}
6029"
6030            );
6031            let error = run_entry_of(&source, "main", &[]).error();
6032            assert!(
6033                error.message.contains("is not a place"),
6034                "`{call}`: {}",
6035                error.message
6036            );
6037        }
6038    }
6039
6040    // ------------------------------------------------------------- rule 5
6041
6042    const TRY: &str = r#"
6043use console.println
6044
6045fn okValue() -> Result<Int, Error> {
6046  Ok(1)
6047}
6048
6049fn errValue() -> Result<Int, Error> {
6050  Err(Error("boom"))
6051}
6052
6053fn someValue() -> Option<Int> {
6054  Some(2)
6055}
6056
6057fn noneValue() -> Option<Int> {
6058  None
6059}
6060"#;
6061
6062    #[test]
6063    fn try_unwraps_ok_and_some() {
6064        let source = format!(
6065            "{TRY}
6066export fn main() -> Result<Unit, Error> {{
6067  let a = okValue()?
6068  let b = someValue()?
6069  console.println(\"{{a}} {{b}}\")?
6070  Ok(())
6071}}
6072"
6073        );
6074        assert_eq!(run_entry_of(&source, "main", &[]).output, "1 2\n");
6075    }
6076
6077    #[test]
6078    fn try_returns_the_error_from_the_current_function() {
6079        let source = format!(
6080            "{TRY}
6081export fn main() -> Result<Int, Error> {{
6082  let a = errValue()?
6083  console.println(\"unreachable\")?
6084  Ok(a)
6085}}
6086"
6087        );
6088        let run = run_entry_of(&source, "main", &[]);
6089        assert_eq!(run.output, "");
6090        assert_eq!(run.value().to_string(), "Err(boom)");
6091    }
6092
6093    #[test]
6094    fn try_returns_none_from_the_current_function() {
6095        let source = format!(
6096            "{TRY}
6097fn firstDigit() -> Option<Int> {{
6098  let value = noneValue()?
6099  Some(value)
6100}}
6101
6102export fn main() -> Option<Int> {{
6103  firstDigit()
6104}}
6105"
6106        );
6107        assert_eq!(
6108            run_entry_of(&source, "main", &[]).value().to_string(),
6109            "None"
6110        );
6111    }
6112
6113    #[test]
6114    fn try_on_a_plain_value_is_rejected() {
6115        let error = error_of("  let x = 1?");
6116        assert!(
6117            error
6118                .message
6119                .contains("`?` needs a `Result` or an `Option`"),
6120            "{}",
6121            error.message
6122        );
6123    }
6124
6125    // ------------------------------------------------------------- rule 6
6126
6127    #[test]
6128    fn arguments_are_evaluated_left_to_right() {
6129        let source = r#"
6130use console.println
6131
6132fn note(var log: Vector<String>, name: String) -> Int {
6133  log.push(name)
6134  0
6135}
6136
6137export fn main() -> Result<Unit, Error> {
6138  var log = Vector.of()
6139  let total = note(var log, "a") + note(var log, "b")
6140  console.println("{log}")?
6141  Ok(())
6142}
6143"#;
6144        assert_eq!(run_entry_of(source, "main", &[]).output, "[a, b]\n");
6145    }
6146
6147    // ------------------------------------------------------------- rule 7
6148
6149    #[test]
6150    fn integer_overflow_names_the_operation() {
6151        let error = error_of("  var big = 9223372036854775807\n  big = big + 1");
6152        assert_eq!(error.message, "`Int` addition overflowed");
6153    }
6154
6155    #[test]
6156    fn division_by_zero_is_a_runtime_error() {
6157        assert_eq!(
6158            error_of("  let x = 1 / 0").message,
6159            "`Int` division by zero"
6160        );
6161        assert_eq!(
6162            error_of("  let x = 1 % 0").message,
6163            "`Int` remainder by zero"
6164        );
6165    }
6166
6167    #[test]
6168    fn mixed_numeric_operands_are_rejected() {
6169        let error = error_of("  let x = 1 + 1.0");
6170        assert!(
6171            error.message.contains("not defined for `Int` and `Float`"),
6172            "{}",
6173            error.message
6174        );
6175    }
6176
6177    #[test]
6178    fn adding_a_string_to_an_int_is_rejected() {
6179        let error = error_of("  let x = \"a\" + 1");
6180        assert!(
6181            error.message.contains("not defined for `String` and `Int`"),
6182            "{}",
6183            error.message
6184        );
6185    }
6186
6187    #[test]
6188    fn adding_two_strings_points_at_interpolation() {
6189        let error = error_of("  let x = \"a\" + \"b\"");
6190        assert_eq!(error.message, "`+` is not defined for `String`");
6191        assert!(error.help.unwrap().contains("interpolation"));
6192    }
6193
6194    // ------------------------------------------------------------- rule 8
6195
6196    /// Static exhaustiveness abstains when the scrutinee's enum cannot be
6197    /// determined from the patterns, so every match it abstains on still
6198    /// needs this runtime guard. The fixture is deliberately opaque to that
6199    /// analysis: two enums declare a case named `Red`, so a bare `Red`
6200    /// pattern names neither of them unambiguously. Do not make it
6201    /// analysable -- that would delete the coverage this test exists for.
6202    #[test]
6203    fn a_match_with_no_matching_arm_is_a_runtime_error() {
6204        let source = r#"
6205enum Color {
6206  Red
6207  Green
6208}
6209
6210enum Wine {
6211  Red
6212  White
6213}
6214
6215export fn main() -> Result<Unit, Error> {
6216  let color = Color.Green
6217  let name = match color {
6218    Red => "red"
6219  }
6220  Ok(())
6221}
6222"#;
6223        let error = run_entry_of(source, "main", &[]).error();
6224        assert!(
6225            error.message.contains("no `match` arm covers"),
6226            "{}",
6227            error.message
6228        );
6229        assert_eq!(
6230            error.rule.as_deref(),
6231            Some("`match` must cover every enum case.")
6232        );
6233    }
6234
6235    #[test]
6236    fn match_binds_enum_payloads_and_literals() {
6237        let source = r#"
6238use console.println
6239
6240enum Shape {
6241  Dot
6242  Line(Int)
6243}
6244
6245fn describe(shape: Shape) -> String {
6246  match shape {
6247    Shape.Dot => "dot"
6248    Shape.Line(length) => "line {length}"
6249  }
6250}
6251
6252export fn main() -> Result<Unit, Error> {
6253  console.println(describe(Shape.Dot))?
6254  console.println(describe(Shape.Line(3)))?
6255  let word = match 2 {
6256    1 => "one"
6257    other => "many"
6258  }
6259  console.println(word)?
6260  Ok(())
6261}
6262"#;
6263        assert_eq!(
6264            run_entry_of(source, "main", &[]).output,
6265            "dot\nline 3\nmany\n"
6266        );
6267    }
6268
6269    // ------------------------------------------------------------- rule 9
6270
6271    #[test]
6272    fn equality_is_value_equality() {
6273        let source = r#"
6274use console.println
6275
6276struct Point {
6277  x: Int
6278}
6279
6280export fn main() -> Result<Unit, Error> {
6281  console.println("{Point(x: 1) == Point(x: 1)} {[1, 2] == [1, 3]}")?
6282  Ok(())
6283}
6284"#;
6285        assert_eq!(run_entry_of(source, "main", &[]).output, "true false\n");
6286    }
6287
6288    #[test]
6289    fn comparing_different_types_is_rejected() {
6290        let error = error_of("  let same = 1 == \"1\"");
6291        assert!(
6292            error.message.contains("cannot compare `Int` with `String`"),
6293            "{}",
6294            error.message
6295        );
6296    }
6297
6298    // ------------------------------------------------------------ rule 10
6299
6300    #[test]
6301    fn blocks_ifs_and_matches_are_expressions() {
6302        let source = r#"
6303use console.println
6304
6305fn classify(value: Int) -> String {
6306  if value > 0 {
6307    return "positive"
6308  }
6309  "other"
6310}
6311
6312export fn main() -> Result<Unit, Error> {
6313  let doubled = {
6314    let base = 3
6315    base * 2
6316  }
6317  let label = if doubled > 5 { "big" } else { "small" }
6318  console.println("{doubled} {label} {classify(1)} {classify(0)}")?
6319  Ok(())
6320}
6321"#;
6322        assert_eq!(
6323            run_entry_of(source, "main", &[]).output,
6324            "6 big positive other\n"
6325        );
6326    }
6327
6328    #[test]
6329    fn loops_run_to_completion() {
6330        let source = r#"
6331use console.println
6332
6333export fn main() -> Result<Unit, Error> {
6334  var total = 0
6335  for value in [1, 2, 3] {
6336    total += value
6337  }
6338  var count = 0
6339  while count < 2 {
6340    count += 1
6341  }
6342  console.println("{total} {count}")?
6343  Ok(())
6344}
6345"#;
6346        assert_eq!(run_entry_of(source, "main", &[]).output, "6 2\n");
6347    }
6348
6349    #[test]
6350    fn a_for_loop_is_unit_however_it_leaves() {
6351        // A `for` can reach its end without breaking, so `break` stops it
6352        // and supplies nothing: the operand is evaluated for its effects and
6353        // its value discarded, which is what the checker says the loop
6354        // produces too.
6355        let source = r#"
6356use console.println
6357
6358export fn main() -> Result<Unit, Error> {
6359  var seen = 0
6360  let found = for value in [1, 2, 3, 4] {
6361    seen = value
6362    if value == 3 {
6363      break value * 10
6364    }
6365  }
6366  console.println("{seen} {found}")?
6367  Ok(())
6368}
6369"#;
6370        assert_eq!(run_entry_of(source, "main", &[]).output, "3 ()\n");
6371    }
6372
6373    #[test]
6374    fn a_loop_that_never_breaks_evaluates_to_unit() {
6375        let source = r#"
6376use console.println
6377
6378export fn main() -> Result<Unit, Error> {
6379  let result = for value in [1, 2] {
6380    value
6381  }
6382  console.println("{result}")?
6383  Ok(())
6384}
6385"#;
6386        assert_eq!(run_entry_of(source, "main", &[]).output, "()\n");
6387    }
6388
6389    #[test]
6390    fn continue_skips_the_rest_of_an_iteration() {
6391        let source = r#"
6392use console.println
6393
6394export fn main() -> Result<Unit, Error> {
6395  var total = 0
6396  for value in [1, 2, 3, 4] {
6397    if value % 2 == 0 {
6398      continue
6399    }
6400    total += value
6401  }
6402  console.println("{total}")?
6403  Ok(())
6404}
6405"#;
6406        assert_eq!(run_entry_of(source, "main", &[]).output, "4\n");
6407    }
6408
6409    #[test]
6410    fn a_while_true_is_unit_like_every_other_loop() {
6411        // `while true` is an ordinary `while`: the `break` stops it and
6412        // supplies nothing, so the loop is `()` here exactly as the checker
6413        // says it is.
6414        let source = r#"
6415use console.println
6416
6417export fn main() -> Result<Unit, Error> {
6418  var count = 0
6419  let found = while true {
6420    count += 1
6421    if count == 3 {
6422      break count
6423    }
6424  }
6425  console.println("{count} {found}")?
6426  Ok(())
6427}
6428"#;
6429        assert_eq!(run_entry_of(source, "main", &[]).output, "3 ()\n");
6430    }
6431
6432    #[test]
6433    fn a_while_that_can_reach_its_end_is_unit_however_it_leaves() {
6434        let source = r#"
6435use console.println
6436
6437export fn main() -> Result<Unit, Error> {
6438  var count = 0
6439  let found = while count < 10 {
6440    count += 1
6441    if count == 3 {
6442      break count
6443    }
6444  }
6445  console.println("{count} {found}")?
6446  Ok(())
6447}
6448"#;
6449        assert_eq!(run_entry_of(source, "main", &[]).output, "3 ()\n");
6450    }
6451
6452    #[test]
6453    fn an_if_with_no_else_is_unit_even_when_its_branch_runs() {
6454        let source = r#"
6455use console.println
6456
6457export fn main() -> Result<Unit, Error> {
6458  var ran = false
6459  let taken = if true {
6460    ran = true
6461    1
6462  }
6463  let skipped = if false {
6464    2
6465  }
6466  console.println("{ran} {taken} {skipped}")?
6467  Ok(())
6468}
6469"#;
6470        assert_eq!(run_entry_of(source, "main", &[]).output, "true () ()\n");
6471    }
6472
6473    // ------------------------------------------------------------ rule 11
6474
6475    #[test]
6476    fn closures_capture_by_value_at_creation_time() {
6477        let source = r#"
6478use console.println
6479
6480export fn main() -> Result<Unit, Error> {
6481  var seen = 1
6482  let read = fn() {
6483    seen
6484  }
6485  seen = 2
6486  console.println("{read()} {seen}")?
6487  Ok(())
6488}
6489"#;
6490        assert_eq!(run_entry_of(source, "main", &[]).output, "1 2\n");
6491    }
6492
6493    // ------------------------------------------------------------ rule 12
6494
6495    #[test]
6496    fn an_unqualified_use_reaches_the_host_module() {
6497        let source = r#"
6498use console.println
6499
6500export fn main() -> Result<Unit, Error> {
6501  println("direct")?
6502  Ok(())
6503}
6504"#;
6505        assert_eq!(run_entry_of(source, "main", &[]).output, "direct\n");
6506    }
6507
6508    #[test]
6509    fn an_ungranted_capability_is_rejected_at_the_host_boundary() {
6510        let source = r#"
6511use console.println
6512
6513export fn main() -> Result<Unit, Error> {
6514  console.println("secret")?
6515  Ok(())
6516}
6517"#;
6518        let (sources, program) = program_of(source);
6519        let run = run_in(
6520            &program,
6521            &sources,
6522            "test",
6523            "main",
6524            &[],
6525            &[],
6526            BTreeMap::new(),
6527        );
6528        assert_eq!(run.output, "");
6529        let error = run.error();
6530        assert!(
6531            error.message.contains("requires the `console` capability"),
6532            "{}",
6533            error.message
6534        );
6535    }
6536
6537    #[test]
6538    fn the_env_host_reads_only_what_the_host_supplied() {
6539        let source = r#"
6540use env.get
6541use console.println
6542
6543export fn main() -> Result<Unit, Error> {
6544  console.println(env.get("PORT").unwrapOr("none"))?
6545  console.println(env.get("MISSING").unwrapOr("none"))?
6546  Ok(())
6547}
6548"#;
6549        let (sources, program) = program_of(source);
6550        let env = BTreeMap::from([("PORT".to_string(), "9000".to_string())]);
6551        let run = run_in(
6552            &program,
6553            &sources,
6554            "test",
6555            "main",
6556            &[],
6557            &["console", "env"],
6558            env,
6559        );
6560        assert_eq!(run.output, "9000\nnone\n");
6561    }
6562
6563    // --------------------------------------------------------- builtins
6564
6565    #[test]
6566    fn array_and_string_builtins() {
6567        let body = "  let items = [10, 20]\n  console.println(\"{items.get(0).unwrapOr(0)} {items.get(5).isNone()} {items.length()} {items.isEmpty()}\")?\n  console.println(\"{\"a bc  d\".words().length()} {\"abc\".length()} {\"\".isEmpty()}\")?";
6568        assert_eq!(output_of(body), "10 true 2 false\n3 3 true\n");
6569    }
6570
6571    #[test]
6572    fn int_parse_returns_a_result() {
6573        assert_eq!(
6574            output_of(
6575                "  console.println(\"{Int.parse(\"12\").isOk()} {Int.parse(\"x\").isError()}\")?"
6576            ),
6577            "true true\n"
6578        );
6579    }
6580
6581    /// `Result.unwrapOr` answers what an `Ok` carries and the fallback for an
6582    /// `Err`, which is `Option.unwrapOr` with `Ok` where it has `Some`. The
6583    /// error itself is dropped: a caller that wants to see it has `mapError`.
6584    #[test]
6585    fn result_unwrap_or_answers_the_ok_or_the_fallback() {
6586        let body = "  console.println(\"{Int.parse(\"12\").unwrapOr(0)} {Int.parse(\"x\").unwrapOr(0)}\")?";
6587        assert_eq!(output_of(body), "12 0\n");
6588    }
6589
6590    /// The fallback is the `Ok` type and the error type is not named at all,
6591    /// so a `Result` carrying a domain error unwraps exactly as one carrying
6592    /// the builtin `Error` does.
6593    #[test]
6594    fn result_unwrap_or_says_nothing_about_the_error_type() {
6595        let source = r#"
6596use console.println
6597
6598enum ConfigError {
6599  InvalidPort(String)
6600}
6601
6602fn port(text: String) -> Result<Int, ConfigError> {
6603  Int.parse(text).mapError(fn(error) { ConfigError.InvalidPort(text) })
6604}
6605
6606export fn main() -> Result<Unit, Error> {
6607  console.println("{port("7").unwrapOr(80)} {port("x").unwrapOr(80)}")?
6608  Ok(())
6609}
6610"#;
6611        assert_eq!(run_entry_of(source, "main", &[]).output, "7 80\n");
6612    }
6613
6614    /// A radix that exists reads the notation it names, and text that is not
6615    /// a number in that notation is the data's failure, so it answers `Err`
6616    /// exactly as `Int.parse` does. `Int.parse` itself stays decimal.
6617    #[test]
6618    fn int_parse_radix_reads_the_base_it_is_given() {
6619        let body = "  console.println(\"{Int.parseRadix(\"ff\", 16).unwrapOr(0)} {Int.parseRadix(\"1010\", 2).unwrapOr(0)} {Int.parseRadix(\"z\", 36).unwrapOr(0)}\")?";
6620        assert_eq!(output_of(body), "255 10 35\n");
6621        let signs = "  console.println(\"{Int.parseRadix(\"-ff\", 16).unwrapOr(0)} {Int.parseRadix(\"+10\", 8).unwrapOr(0)}\")?";
6622        assert_eq!(output_of(signs), "-255 8\n");
6623        let wrong = "  console.println(\"{Int.parseRadix(\"ff\", 10)}\")?";
6624        assert_eq!(output_of(wrong), "Err(`ff` is not an Int in radix 10)\n");
6625        // Radix ten is what `Int.parse` already reads, and the two agree.
6626        let decimal = "  console.println(\"{Int.parse(\"12\")} {Int.parseRadix(\"12\", 10)}\")?";
6627        assert_eq!(output_of(decimal), "Ok(12) Ok(12)\n");
6628    }
6629
6630    /// A radix outside `2..=36` is the call being wrong rather than the data,
6631    /// so it stops the run instead of answering `Err` — the line
6632    /// `String.split` draws at an empty separator.
6633    #[test]
6634    fn int_parse_radix_refuses_a_radix_that_names_no_notation() {
6635        for radix in ["1", "0", "37", "-16"] {
6636            let error = error_of(&format!("  let n = Int.parseRadix(\"1\", {radix})"));
6637            assert_eq!(
6638                error.message,
6639                format!("`Int.parseRadix` cannot read a number in radix `{radix}`")
6640            );
6641            assert!(error.rule.as_ref().unwrap().contains("2 through 36"));
6642        }
6643    }
6644
6645    /// `String.fromCodePoint` is `chars()` run backwards: it builds the
6646    /// one-character `String` a code point names, whatever plane it is in.
6647    #[test]
6648    fn string_from_code_point_builds_one_character() {
6649        let body = "  console.println(\"{String.fromCodePoint(65).unwrapOr(\"?\")}{String.fromCodePoint(12354).unwrapOr(\"?\")}{String.fromCodePoint(128512).unwrapOr(\"?\")}\")?";
6650        assert_eq!(output_of(body), "Aあ😀\n");
6651        // One character, so one element of `chars()` and a `length()` of 1,
6652        // even for the code point that takes four bytes to write.
6653        let counted =
6654            "  console.println(\"{String.fromCodePoint(128512).unwrapOr(\"\").length()}\")?";
6655        assert_eq!(output_of(counted), "1\n");
6656        let zero = "  console.println(\"{String.fromCodePoint(0).isOk()}\")?";
6657        assert_eq!(output_of(zero), "true\n");
6658    }
6659
6660    /// A number that names no character is an expected failure of the data,
6661    /// so it answers `Err` the way `Float.toInt` does, and the surrogates say
6662    /// which failure they are: a caller decoding UTF-16 has half a character
6663    /// rather than a bad one.
6664    #[test]
6665    fn string_from_code_point_says_which_way_a_number_names_no_character() {
6666        let out_of_range =
6667            "  console.println(\"{String.fromCodePoint(1114112)} {String.fromCodePoint(-1)}\")?";
6668        assert_eq!(
6669            output_of(out_of_range),
6670            "Err(`1114112` is not a Unicode code point) \
6671             Err(`-1` is not a Unicode code point)\n"
6672        );
6673        let surrogate = "  console.println(\"{String.fromCodePoint(55296)}\")?";
6674        assert_eq!(
6675            output_of(surrogate),
6676            "Err(`55296` is a surrogate half, which is not a character on its own)\n"
6677        );
6678        // The last code point there is, and the first one past it.
6679        let edges = "  console.println(\"{String.fromCodePoint(1114111).isOk()} {String.fromCodePoint(57343).isOk()} {String.fromCodePoint(57344).isOk()}\")?";
6680        assert_eq!(output_of(edges), "true false true\n");
6681    }
6682
6683    /// The two together are what issue #101 asked for: a `\u` escape read
6684    /// out of text, including the surrogate pair a format that writes code
6685    /// points in sixteen bits spells a supplementary character as.
6686    #[test]
6687    fn a_hex_escape_can_be_decoded_in_cove() {
6688        let source = r#"
6689use console.println
6690
6691/// The character a four-hex-digit escape names.
6692fn unescape(digits: String) -> Result<String, Error> {
6693  String.fromCodePoint(Int.parseRadix(digits, 16)?)
6694}
6695
6696/// The character a UTF-16 surrogate pair names.
6697fn unescapePair(high: String, low: String) -> Result<String, Error> {
6698  let lead = Int.parseRadix(high, 16)?
6699  let trail = Int.parseRadix(low, 16)?
6700  String.fromCodePoint(65536 + (lead - 55296) * 1024 + (trail - 56320))
6701}
6702
6703export fn main() -> Result<Unit, Error> {
6704  console.println("{unescape("0041")?}{unescape("3042")?}")?
6705  console.println("{unescapePair("D83D", "DE00")?}")?
6706  console.println("{unescape("D83D")}")?
6707  Ok(())
6708}
6709"#;
6710        assert_eq!(
6711            run_entry_of(source, "main", &[]).output,
6712            "Aあ\n😀\nErr(`55357` is a surrogate half, which is not a character on its own)\n"
6713        );
6714    }
6715
6716    /// ADR 0044: a trailing closure can never declare a parameter, and
6717    /// `mapError`'s callback declares one (`fn(E) -> F`) with no exception
6718    /// any more — so a program that writes the callback as a trailing
6719    /// closure is rejected rather than silently handed nothing. This test
6720    /// used to be `map_error_accepts_a_trailing_closure` and assert the
6721    /// opposite.
6722    #[test]
6723    fn map_error_rejects_a_trailing_closure() {
6724        let source = r#"
6725use console.println
6726
6727enum ConfigError {
6728  InvalidPort(String)
6729}
6730
6731export fn main() -> Result<Unit, Error> {
6732  let failed = Int.parse("x").mapError { ConfigError.InvalidPort("x") }
6733  let kept = Int.parse("7").mapError { ConfigError.InvalidPort("7") }
6734  console.println("{failed} {kept}")?
6735  Ok(())
6736}
6737"#;
6738        let errors = check_errors_of(source);
6739        assert!(
6740            errors.iter().any(|error| error.code == "cove::type::arity"
6741                && error.message == "this function takes 0 parameter(s), but 1 were expected here"),
6742            "{errors:?}"
6743        );
6744    }
6745
6746    /// `pop` is a `Vector`'s mutating method and no method of an `Array` at
6747    /// all, so the receiver's type answers this rather than the name: an
6748    /// `Array` asked for one is told it has no such method, not told to find
6749    /// a place for a `var self` receiver it would never need.
6750    #[test]
6751    fn a_method_that_does_not_exist_names_the_receiver_type() {
6752        let error = error_of("  let x = [1].pop()");
6753        assert_eq!(error.message, "`Array` has no method `pop`");
6754    }
6755
6756    // ------------------------------------ walking a sequence with a closure
6757
6758    /// The four higher-order methods answer an `Array` whichever sequence
6759    /// they were called on, and neither writes through the receiver.
6760    #[test]
6761    fn a_sequence_walks_the_same_whichever_sequence_it_is() {
6762        let output = output_of(
6763            r#"  let fixed = [3, 1, 2]
6764  var growable = Vector.of(3, 1, 2)
6765  console.println("{fixed.map(fn(n) { n * 2 })} {growable.map(fn(n) { n * 2 })}")?
6766  console.println("{fixed.filter(fn(n) { n > 1 })} {growable.filter(fn(n) { n > 1 })}")?
6767  console.println("{fixed.fold(0, fn(t, n) { t + n })} {growable.fold(0, fn(t, n) { t + n })}")?
6768  console.println("{fixed.sorted(by: fn(a, b) { a < b })} {growable.sorted(by: fn(a, b) { a < b })}")?
6769  console.println("{fixed} {growable}")?"#,
6770        );
6771        assert_eq!(
6772            output,
6773            "[6, 2, 4] [6, 2, 4]\n[3, 2] [3, 2]\n6 6\n[1, 2, 3] [1, 2, 3]\n[3, 1, 2] [3, 1, 2]\n"
6774        );
6775    }
6776
6777    /// An empty sequence answers without calling anything, and `fold` answers
6778    /// the initial value it was handed.
6779    #[test]
6780    fn an_empty_sequence_answers_without_calling_its_callback() {
6781        let output = output_of(
6782            r#"  let empty: Array<Int> = []
6783  console.println("{empty.map(fn(n) { n / 0 })}")?
6784  console.println("{empty.filter(fn(n) { n / 0 > 0 })}")?
6785  console.println("{empty.sorted(by: fn(a, b) { a / 0 < b })}")?
6786  console.println("{empty.fold(7, fn(t, n) { t / 0 })}")?"#,
6787        );
6788        assert_eq!(output, "[]\n[]\n[]\n7\n");
6789    }
6790
6791    /// `sorted` is stable: two elements neither of which comes before the
6792    /// other keep the order they were written in.
6793    ///
6794    /// The comparison answers `false` for every pair, so every element ties
6795    /// with every other and only stability decides the answer.
6796    #[test]
6797    fn sorted_is_stable() {
6798        let output = output_of(
6799            r#"  let items = [5, 4, 3, 2, 1, 0]
6800  console.println("{items.sorted(by: fn(a, b) { false })}")?"#,
6801        );
6802        assert_eq!(output, "[5, 4, 3, 2, 1, 0]\n");
6803    }
6804
6805    /// An ordering that contradicts itself gets a permutation and not a
6806    /// stopped run: the comparison is the program's to get right, and the
6807    /// merge has no invariant of its own to break.
6808    #[test]
6809    fn an_inconsistent_ordering_answers_a_permutation() {
6810        let output = output_of(
6811            r#"  let items = [1, 2, 3, 4]
6812  let sorted = items.sorted(by: fn(a, b) { true })
6813  console.println("{sorted.length()} {sorted.fold(0, fn(t, n) { t + n })}")?"#,
6814        );
6815        assert_eq!(output, "4 10\n");
6816    }
6817
6818    /// A callback that fails takes the whole call with it, so no half-built
6819    /// answer is ever reachable.
6820    #[test]
6821    fn a_failing_callback_answers_nothing() {
6822        for body in [
6823            "  let x = [1, 2].map(fn(n) { n / 0 })",
6824            "  let x = [1, 2].filter(fn(n) { n / 0 > 1 })",
6825            "  let x = [1, 2].fold(0, fn(t, n) { n / 0 })",
6826            "  let x = [2, 1].sorted(by: fn(a, b) { a / 0 < b })",
6827        ] {
6828            assert_eq!(
6829                error_of(body).message,
6830                "`Int` division by zero",
6831                "for `{body}`"
6832            );
6833        }
6834    }
6835
6836    /// The elements come out of a `Vector` before the first callback, so a
6837    /// callback may reach the very vector being walked.
6838    ///
6839    /// Reading it is as far as a callback can go — a capture is a read-only
6840    /// place, so nothing it can write to is the receiver — but the walk holds
6841    /// no borrow on the storage either way, which is what this pins.
6842    #[test]
6843    fn a_callback_may_read_the_vector_it_is_walking() {
6844        let output = output_of(
6845            r#"  var items = Vector.of(2, 1, 3)
6846  console.println("{items.map(fn(n) { n + items.length() })}")?
6847  console.println("{items.sorted(by: fn(a, b) { a + items.length() < b + items.length() })}")?"#,
6848        );
6849        assert_eq!(output, "[5, 4, 6]\n[1, 2, 3]\n");
6850    }
6851
6852    // -------------------------------------------------------- ranges
6853
6854    #[test]
6855    fn a_range_is_an_ordinary_value() {
6856        let output = output_of(
6857            r#"  let exclusive = 0..<3
6858  let inclusive = 0..3
6859  console.println("{exclusive} {inclusive}")?"#,
6860        );
6861        assert_eq!(output, "0..<3 0..3\n");
6862    }
6863
6864    #[test]
6865    fn a_range_value_iterates_like_a_range_literal() {
6866        let output = output_of(
6867            r#"  let bounds = 0..<3
6868  var total = 0
6869  for value in bounds {
6870    total += value
6871  }
6872  for value in 1..3 {
6873    total += value
6874  }
6875  console.println("{total}")?"#,
6876        );
6877        assert_eq!(output, "9\n");
6878    }
6879
6880    #[test]
6881    fn a_range_has_the_sequence_methods() {
6882        let output = output_of(
6883            r#"  let exclusive = 0..<3
6884  let inclusive = 0..3
6885  console.println("{exclusive.length()} {inclusive.length()}")?
6886  console.println("{exclusive.isEmpty()} {exclusive.contains(2)} {exclusive.contains(3)}")?
6887  console.println("{inclusive.contains(3)} {inclusive.contains(-1)}")?"#,
6888        );
6889        assert_eq!(output, "3 4\nfalse true false\ntrue false\n");
6890    }
6891
6892    #[test]
6893    fn a_reversed_range_is_empty_and_iterates_zero_times() {
6894        let output = output_of(
6895            r#"  let reversed = 3..<0
6896  var rounds = 0
6897  for _value in reversed {
6898    rounds += 1
6899  }
6900  console.println("{reversed} {reversed.length()} {reversed.isEmpty()} {rounds}")?"#,
6901        );
6902        assert_eq!(output, "3..<0 0 true 0\n");
6903    }
6904
6905    #[test]
6906    fn ranges_compare_by_value() {
6907        let output =
6908            output_of(r#"  console.println("{0..<3 == 0..<3} {0..<3 == 0..3} {0..<3 == 1..<3}")?"#);
6909        assert_eq!(output, "true false false\n");
6910    }
6911
6912    #[test]
6913    fn a_range_bound_must_be_an_int() {
6914        let error = error_of("  let bad = 0..<\"3\"");
6915        assert!(
6916            error.message.contains("a range bound must be an `Int`"),
6917            "{}",
6918            error.message
6919        );
6920    }
6921
6922    #[test]
6923    fn a_range_has_no_method_it_does_not_declare() {
6924        let error = error_of("  let bounds = 0..<3\n  let bad = bounds.reverse()");
6925        assert_eq!(error.message, "`Range` has no method `reverse`");
6926    }
6927
6928    // ---------------------------------------------- one spelling: length
6929
6930    #[test]
6931    fn count_is_rejected_and_names_the_length_spelling() {
6932        let bodies = [
6933            "  let n = [1, 2].count()",
6934            "  let n = Vector.of(1, 2).count()",
6935            "  let n = \"a b\".count()",
6936            "  let n = (0..<3).count()",
6937            "  let n = Map.of().count()",
6938            "  let n = Set.of().count()",
6939        ];
6940        for body in bodies {
6941            let error = error_of(body);
6942            assert!(
6943                error
6944                    .message
6945                    .contains("Cove spells the number of elements `length()`"),
6946                "{body}: {}",
6947                error.message
6948            );
6949            assert_eq!(
6950                error.help.as_deref(),
6951                Some("write `length()` instead of `count()`"),
6952                "{body}"
6953            );
6954        }
6955    }
6956
6957    #[test]
6958    fn length_is_the_one_spelling_every_sequence_answers() {
6959        let output = output_of(
6960            r#"  console.println("{[1, 2].length()} {Vector.of(1).length()} {"ab".length()} {(0..<4).length()}")?"#,
6961        );
6962        assert_eq!(output, "2 1 2 4\n");
6963    }
6964
6965    // ------------------------------------------------------- map and set
6966
6967    #[test]
6968    fn map_of_builds_a_map_and_answers_its_methods() {
6969        let output = output_of(
6970            r#"  let ages = Map.of(
6971    MapEntry(key: "Alice", value: 30),
6972    MapEntry(key: "Bob", value: 25)
6973  )
6974  console.println("{ages}")?
6975  console.println("{ages.length()} {ages.isEmpty()}")?
6976  console.println("{ages.get("Alice")} {ages.get("Zoe")}")?
6977  console.println("{ages.contains("Bob")} {ages.contains("Zoe")}")?
6978  console.println("{ages.keys()} {ages.values()}")?"#,
6979        );
6980        assert_eq!(
6981            output,
6982            "{Alice: 30, Bob: 25}\n2 false\nSome(30) None\ntrue false\n[Alice, Bob] [30, 25]\n"
6983        );
6984    }
6985
6986    #[test]
6987    fn an_empty_map_is_empty() {
6988        let output = output_of(r#"  console.println("{Map.of()} {Map.of().isEmpty()}")?"#);
6989        assert_eq!(output, "{} true\n");
6990    }
6991
6992    #[test]
6993    fn map_of_rejects_a_duplicate_key() {
6994        let error = error_of(
6995            r#"  let bad = Map.of(
6996    MapEntry(key: "x", value: 1),
6997    MapEntry(key: "x", value: 2)
6998  )"#,
6999        );
7000        assert_eq!(
7001            error.message,
7002            "`Map.of` was given the key `x` more than once"
7003        );
7004    }
7005
7006    #[test]
7007    fn map_of_rejects_an_argument_that_is_not_a_map_entry() {
7008        let error = error_of("  let bad = Map.of(1)");
7009        assert!(
7010            error.message.contains("`Map.of` expects `MapEntry` values"),
7011            "{}",
7012            error.message
7013        );
7014    }
7015
7016    #[test]
7017    fn map_entry_labels_are_key_then_value_in_declaration_order() {
7018        let error = error_of(r#"  let bad = MapEntry(value: 1, key: "x")"#);
7019        assert!(
7020            error.message.contains("out of declaration order"),
7021            "{}",
7022            error.message
7023        );
7024    }
7025
7026    #[test]
7027    fn map_get_and_contains_reject_an_invalid_key_type() {
7028        let error = error_of(
7029            r#"  let m = Map.of()
7030  let bad = m.get(Vector.of(1))"#,
7031        );
7032        assert_eq!(
7033            error.message,
7034            "`Map.get` cannot use a `Vector` as a map key"
7035        );
7036        assert!(
7037            error
7038                .rule
7039                .as_deref()
7040                .unwrap_or_default()
7041                .contains("Mutable handles and structs containing them are not valid map keys"),
7042            "{:?}",
7043            error.rule
7044        );
7045    }
7046
7047    #[test]
7048    fn map_inserted_and_removed_return_a_new_map_and_do_not_mutate_the_original() {
7049        let output = output_of(
7050            r#"  let original = Map.of(MapEntry(key: "a", value: 1))
7051  let inserted = original.inserted("b", 2)
7052  let removed = inserted.removed("a")
7053  console.println("{original} {inserted} {removed}")?"#,
7054        );
7055        assert_eq!(output, "{a: 1} {a: 1, b: 2} {b: 2}\n");
7056    }
7057
7058    #[test]
7059    fn maps_compare_by_structural_equality() {
7060        let output = output_of(
7061            r#"  let a = Map.of(MapEntry(key: "x", value: 1))
7062  let b = Map.of(MapEntry(key: "x", value: 1))
7063  let c = Map.of(MapEntry(key: "x", value: 2))
7064  console.println("{a == b} {a == c}")?"#,
7065        );
7066        assert_eq!(output, "true false\n");
7067    }
7068
7069    #[test]
7070    fn map_iterates_map_entries_in_ascending_key_order() {
7071        let output = output_of(
7072            r#"  let scores = Map.of(
7073    MapEntry(key: "b", value: 2),
7074    MapEntry(key: "a", value: 1)
7075  )
7076  for entry in scores {
7077    console.println("{entry.key} {entry.value}")?
7078  }"#,
7079        );
7080        assert_eq!(output, "a 1\nb 2\n");
7081    }
7082
7083    #[test]
7084    fn set_of_builds_a_set_and_answers_its_methods() {
7085        let output = output_of(
7086            r#"  let names = Set.of("b", "a", "c")
7087  console.println("{names}")?
7088  console.println("{names.length()} {names.isEmpty()}")?
7089  console.println("{names.contains("a")} {names.contains("z")}")?
7090  console.println("{names.toArray()}")?"#,
7091        );
7092        assert_eq!(output, "{a, b, c}\n3 false\ntrue false\n[a, b, c]\n");
7093    }
7094
7095    #[test]
7096    fn set_of_rejects_a_duplicate_element() {
7097        let error = error_of("  let bad = Set.of(1, 1)");
7098        assert_eq!(
7099            error.message,
7100            "`Set.of` was given the element `1` more than once"
7101        );
7102    }
7103
7104    #[test]
7105    fn set_of_rejects_an_invalid_element_type() {
7106        let error = error_of("  let bad = Set.of(Vector.of(1))");
7107        assert_eq!(
7108            error.message,
7109            "`Set.of` cannot use a `Vector` as a set element"
7110        );
7111    }
7112
7113    #[test]
7114    fn set_inserted_and_removed_return_a_new_set_and_do_not_mutate_the_original() {
7115        let output = output_of(
7116            r#"  let original = Set.of(1, 2)
7117  let inserted = original.inserted(3)
7118  let removed = inserted.removed(1)
7119  console.println("{original} {inserted} {removed}")?"#,
7120        );
7121        assert_eq!(output, "{1, 2} {1, 2, 3} {2, 3}\n");
7122    }
7123
7124    #[test]
7125    fn sets_compare_by_structural_equality() {
7126        let output = output_of(
7127            r#"  let a = Set.of(1, 2)
7128  let b = Set.of(2, 1)
7129  let c = Set.of(1)
7130  console.println("{a == b} {a == c}")?"#,
7131        );
7132        assert_eq!(output, "true false\n");
7133    }
7134
7135    #[test]
7136    fn set_iterates_in_ascending_order() {
7137        let output = output_of(
7138            r#"  var total = 0
7139  for item in Set.of(3, 1, 2) {
7140    total = total * 10 + item
7141  }
7142  console.println("{total}")?"#,
7143        );
7144        assert_eq!(output, "123\n");
7145    }
7146
7147    #[test]
7148    fn a_payload_free_enum_case_is_a_valid_map_key() {
7149        let run = colour_body(
7150            r#"  let byColour = Map.of(MapEntry(key: Colour.Red, value: "stop"))
7151  console.println("{byColour.get(Colour.Red)}")?"#,
7152        );
7153        assert_eq!(run.output, "Some(stop)\n");
7154    }
7155
7156    #[test]
7157    fn an_enum_case_with_a_payload_is_a_valid_set_element() {
7158        let run = colour_body(
7159            r#"  let colours = Set.of(Colour.Red, Colour.Named("teal"))
7160  console.println("{colours.contains(Colour.Named("teal"))} {colours.contains(Colour.Named("blue"))}")?"#,
7161        );
7162        assert_eq!(run.output, "true false\n");
7163    }
7164
7165    /// A source module declaring an opaque type, together with a plain
7166    /// struct of the same shape to render beside it.
7167    const OPAQUE: &str = r#"
7168use console.println
7169
7170/// A token.
7171export opaque struct Token {
7172  raw: String
7173  count: Int
7174}
7175
7176/// A token with nothing to hide.
7177export struct Label {
7178  raw: String
7179  count: Int
7180}
7181"#;
7182
7183    /// An opaque type renders as its name and nothing else — in the module
7184    /// that declares it as much as in any other, because a rendered string
7185    /// goes wherever it is passed and carries no module with it. A module
7186    /// that wants a readable form exports a method that returns one. See
7187    /// ADR 0014.
7188    #[test]
7189    fn an_opaque_value_renders_as_its_name_alone() {
7190        let source = format!(
7191            "{OPAQUE}{}",
7192            r#"
7193/// Entry point.
7194export fn main() -> Result<Unit, Error> {
7195  let token = Token(raw: "secret", count: 1)
7196  let label = Label(raw: "secret", count: 1)
7197  println("{token}")?
7198  println("{label}")?
7199  Ok(())
7200}
7201"#
7202        );
7203        let run = run_entry_of(&source, "main", &[]);
7204        assert_eq!(run.output, "Token\nLabel(raw: secret, count: 1)\n");
7205    }
7206
7207    /// The rendering rule holds wherever a value is rebuilt from its parts:
7208    /// a key taken back out of a `Set` is the value that went in, opacity
7209    /// included.
7210    #[test]
7211    fn an_opaque_value_taken_out_of_a_set_still_renders_as_its_name() {
7212        let source = format!(
7213            "{OPAQUE}{}",
7214            r#"
7215/// Entry point.
7216export fn main() -> Result<Unit, Error> {
7217  for token in Set.of(Token(raw: "secret", count: 1)) {
7218    println("{token}")?
7219  }
7220  Ok(())
7221}
7222"#
7223        );
7224        let run = run_entry_of(&source, "main", &[]);
7225        assert_eq!(run.output, "Token\n");
7226    }
7227
7228    /// Opacity and the `dyn Trait` wrapper compose: keying looks through the
7229    /// wrapper, so two trait objects over the same opaque value are one key,
7230    /// and what comes back out is still opaque, so it renders as its name
7231    /// alone. Neither rule undoes the other — the wrapper is a representation
7232    /// and opacity is a property of the value inside it.
7233    #[test]
7234    fn a_trait_object_over_an_opaque_value_keys_and_renders_as_that_value() {
7235        let source = format!(
7236            "{OPAQUE}{}",
7237            r#"
7238/// Something that can describe itself.
7239trait Described {
7240  fn describe(self) -> String
7241}
7242
7243impl Described for Token {
7244  fn describe(self) -> String { "token {self.count}" }
7245}
7246
7247impl Described for Label {
7248  fn describe(self) -> String { "label {self.count}" }
7249}
7250
7251/// Entry point.
7252export fn main() -> Result<Unit, Error> {
7253  let token: dyn Described = Token(raw: "secret", count: 1)
7254  let label: dyn Described = Label(raw: "secret", count: 1)
7255  let keys = Set.of(token, label)
7256  println("{keys.contains(Token(raw: "secret", count: 1))}")?
7257  println("{token}")?
7258  for key in keys {
7259    println("{key}")?
7260  }
7261  Ok(())
7262}
7263"#
7264        );
7265        let run = run_entry_of(&source, "main", &[]);
7266        // `contains` answers `true` for the bare value: keying looked through
7267        // the wrapper, which is what `==` already says about the two of them.
7268        // Every rendering of the opaque one is its bare name — held in the
7269        // wrapper, and rebuilt from the key on the way back out of the set —
7270        // while the ordinary struct beside it still shows its fields.
7271        assert_eq!(
7272            run.output,
7273            "true\nToken\nLabel(raw: secret, count: 1)\nToken\n"
7274        );
7275    }
7276
7277    #[test]
7278    fn a_struct_built_only_from_ints_is_a_valid_set_element() {
7279        let run = point_body(
7280            r#"  let points = Set.of(Point(x: 1, y: 2), Point(x: 3, y: 4))
7281  console.println("{points.contains(Point(x: 1, y: 2))} {points.contains(Point(x: 9, y: 9))}")?"#,
7282        );
7283        assert_eq!(run.output, "true false\n");
7284    }
7285
7286    #[test]
7287    fn a_struct_nested_inside_a_struct_is_a_valid_set_element() {
7288        let source = r#"
7289use console.println
7290
7291struct Address {
7292  city: String
7293}
7294
7295struct Person {
7296  name: String
7297  address: Address
7298}
7299
7300export fn main() -> Result<Unit, Error> {
7301  let people = Set.of(
7302    Person(name: "Ada", address: Address(city: "London")),
7303    Person(name: "Grace", address: Address(city: "New York"))
7304  )
7305  console.println("{people.contains(Person(name: "Ada", address: Address(city: "London")))}")?
7306  console.println("{people.contains(Person(name: "Ada", address: Address(city: "Paris")))}")?
7307  Ok(())
7308}
7309"#;
7310        assert_eq!(run_entry_of(source, "main", &[]).output, "true\nfalse\n");
7311    }
7312
7313    #[test]
7314    fn an_array_built_only_from_ints_is_a_valid_set_element() {
7315        let output = output_of(
7316            r#"  let pairs = Set.of([1, 2], [3, 4])
7317  console.println("{pairs.contains([1, 2])} {pairs.contains([9])}")?"#,
7318        );
7319        assert_eq!(output, "true false\n");
7320    }
7321
7322    #[test]
7323    fn a_struct_containing_a_vector_is_rejected_naming_the_nested_field() {
7324        let source = r#"
7325use console.println
7326
7327struct Point {
7328  tags: Vector<Int>
7329}
7330
7331export fn main() -> Result<Unit, Error> {
7332  let bad = Set.of(Point(tags: Vector.of(1)))
7333  Ok(())
7334}
7335"#;
7336        let error = run_entry_of(source, "main", &[]).error();
7337        assert_eq!(
7338            error.message,
7339            "`Set.of` cannot use a `Vector` inside `Point.tags` as a set element"
7340        );
7341    }
7342
7343    #[test]
7344    fn a_float_is_rejected_as_a_key_for_a_reason_distinct_from_mutability() {
7345        let error = error_of("  let bad = Set.of(1.5)");
7346        assert_eq!(
7347            error.message,
7348            "`Set.of` cannot use a `Float` as a set element"
7349        );
7350        assert!(
7351            error.rule.as_deref().unwrap_or_default().contains("NaN"),
7352            "{:?}",
7353            error.rule
7354        );
7355    }
7356
7357    // --------------------------------- associated functions on an enum
7358
7359    const COLOUR: &str = r#"
7360use console.println
7361
7362enum Colour {
7363  Red
7364  Named(String)
7365}
7366
7367impl Colour {
7368  /// Returns the colour used when nothing was chosen.
7369  fn fallback() -> Colour {
7370    Colour.Red
7371  }
7372
7373  /// Names this colour.
7374  fn describe(self) -> String {
7375    match self {
7376      Colour.Red => "red"
7377      Colour.Named(name) => name
7378    }
7379  }
7380}
7381"#;
7382
7383    fn colour_body(body: &str) -> Run {
7384        run_entry_of(
7385            &format!(
7386                "{COLOUR}\nexport fn main() -> Result<Unit, Error> {{\n{body}\n  Ok(())\n}}\n"
7387            ),
7388            "main",
7389            &[],
7390        )
7391    }
7392
7393    #[test]
7394    fn an_enum_can_declare_an_associated_function() {
7395        let run = colour_body("  console.println(\"{Colour.fallback()}\")?");
7396        assert_eq!(run.output, "Red\n");
7397    }
7398
7399    #[test]
7400    fn an_enum_value_answers_its_methods() {
7401        let run = colour_body(
7402            "  console.println(\"{Colour.Red.describe()} {Colour.Named(\"teal\").describe()}\")?",
7403        );
7404        assert_eq!(run.output, "red teal\n");
7405    }
7406
7407    #[test]
7408    fn a_case_wins_over_an_associated_function_of_the_same_name() {
7409        let source = r#"
7410use console.println
7411
7412enum Signal {
7413  Ready
7414}
7415
7416impl Signal {
7417  /// Shadowed by the case of the same name, which keeps naming the case.
7418  fn Ready() -> String {
7419    "the function"
7420  }
7421}
7422
7423export fn main() -> Result<Unit, Error> {
7424  console.println("{Signal.Ready()}")?
7425  Ok(())
7426}
7427"#;
7428        assert_eq!(run_entry_of(source, "main", &[]).output, "Ready\n");
7429    }
7430
7431    #[test]
7432    fn an_unknown_enum_member_names_both_possibilities() {
7433        let error = colour_body("  let missing = Colour.missing()").error();
7434        assert_eq!(
7435            error.message,
7436            "enum `Colour` has no case or associated function `missing`"
7437        );
7438        let help = error.help.unwrap();
7439        assert!(help.contains("known cases: Red, Named"), "{help}");
7440        assert!(
7441            help.contains("known functions: describe, fallback"),
7442            "{help}"
7443        );
7444    }
7445
7446    // --------------------------------------------- struct initialization
7447
7448    const POINT: &str = r#"
7449use console.println
7450
7451struct Point {
7452  x: Int
7453  y: Int
7454}
7455"#;
7456
7457    fn point_body(body: &str) -> Run {
7458        run_entry_of(
7459            &format!("{POINT}\nexport fn main() -> Result<Unit, Error> {{\n{body}\n  Ok(())\n}}\n"),
7460            "main",
7461            &[],
7462        )
7463    }
7464
7465    #[test]
7466    fn positional_arguments_may_precede_labels() {
7467        let run = point_body("  console.println(\"{Point(1, y: 2)}\")?");
7468        assert_eq!(run.output, "Point(x: 1, y: 2)\n");
7469    }
7470
7471    #[test]
7472    fn struct_initialization_reports_missing_unknown_and_duplicate_labels() {
7473        let missing = point_body("  let p = Point(x: 1)").error();
7474        assert!(missing.message.contains("field `y`"), "{}", missing.message);
7475
7476        let unknown = point_body("  let p = Point(x: 1, z: 2)").error();
7477        assert!(
7478            unknown.message.contains("no parameter labeled `z`"),
7479            "{}",
7480            unknown.message
7481        );
7482
7483        let duplicate = point_body("  let p = Point(x: 1, x: 2)").error();
7484        assert!(
7485            duplicate.message.contains("`x` more than once"),
7486            "{}",
7487            duplicate.message
7488        );
7489    }
7490
7491    #[test]
7492    fn struct_initializer_labels_must_be_in_declaration_order() {
7493        let error = point_body("  let p = Point(y: 2, x: 1)").error();
7494        assert_eq!(
7495            error.message,
7496            "`Point` was given the label `x` out of declaration order"
7497        );
7498        assert_eq!(
7499            error.help.as_deref(),
7500            Some("write the arguments in this order: x, y")
7501        );
7502    }
7503
7504    #[test]
7505    fn call_labels_must_be_in_declaration_order() {
7506        let source = r#"
7507use console.println
7508
7509fn between(low: Int, high: Int) -> String {
7510  "[{low}, {high}]"
7511}
7512
7513export fn main() -> Result<Unit, Error> {
7514  console.println(between(high: 6, low: 5))?
7515  Ok(())
7516}
7517"#;
7518        let error = run_entry_of(source, "main", &[]).error();
7519        assert_eq!(
7520            error.message,
7521            "`between` was given the label `low` out of declaration order"
7522        );
7523        assert_eq!(
7524            error.rule.as_deref(),
7525            Some(
7526                "Labeled arguments appear in declaration order, so argument order matches parameter order."
7527            )
7528        );
7529        assert_eq!(
7530            error.help.as_deref(),
7531            Some("write the arguments in this order: low, high")
7532        );
7533    }
7534
7535    #[test]
7536    fn labels_in_declaration_order_are_accepted_after_positional_arguments() {
7537        let source = r#"
7538use console.println
7539
7540fn measure(value: Int, unit: String = "m", prefix: String = "length") -> String {
7541  "{prefix} {value}{unit}"
7542}
7543
7544export fn main() -> Result<Unit, Error> {
7545  console.println(measure(3, unit: "cm", prefix: "width"))?
7546  console.println(measure(3, prefix: "width"))?
7547  console.println(measure(value: 4, unit: "cm"))?
7548  Ok(())
7549}
7550"#;
7551        assert_eq!(
7552            run_entry_of(source, "main", &[]).output,
7553            "width 3cm
7554width 3m
7555length 4cm
7556"
7557        );
7558    }
7559
7560    // --------------------------------------------------------- the entry
7561
7562    #[test]
7563    fn an_entry_takes_no_parameters_or_one_array_of_strings() {
7564        let source = r#"
7565export fn main(first: String, second: String) -> Result<Unit, Error> {
7566  Ok(())
7567}
7568"#;
7569        let error = run_entry_of(source, "main", &[]).error();
7570        assert!(
7571            error
7572                .rule
7573                .unwrap()
7574                .contains("either no parameters or one `Array<String>`"),
7575            "{}",
7576            error.message
7577        );
7578    }
7579
7580    // ------------------------------------------------------------- tasks
7581    //
7582    // Tasks run on threads of their own, so these tests assert what `await`
7583    // and scope exit produce, and never the order in which two independent
7584    // tasks happen to run. A test that depended on that order would be
7585    // pinning a race rather than the language.
7586
7587    const TASKS: &str = r#"
7588use console.println
7589
7590async fn answer() -> Int {
7591  7
7592}
7593
7594async fn load(ok: Bool) -> Result<Int, Error> {
7595  if ok {
7596    Ok(1)
7597  } else {
7598    Err(Error("boom"))
7599  }
7600}
7601"#;
7602
7603    /// A task body that cannot finish before a cancellation reaches it, and
7604    /// prints only if it does.
7605    ///
7606    /// With ADR 0008 a spawned task starts at once on a thread of its own, so
7607    /// a test that asserts a cancelled task "never ran" has to give it work
7608    /// to be stopped in the middle of. The loop stops at its next back-edge
7609    /// safepoint once the task is cancelled; the bound is there only so that
7610    /// a runtime which never delivers the cancellation fails the test instead
7611    /// of hanging.
7612    const SPINNING_TASK: &str =
7613        "      var i = 0\n      while i < 1000000000 {\n        i += 1\n      }\n      println(\"this must not run\")?";
7614
7615    /// Runs `body` inside a `main` that returns `Result<Unit, Error>`, with
7616    /// the `async fn` helpers of [`TASKS`] in scope.
7617    fn run_task_body(body: &str) -> Run {
7618        run_entry_of(
7619            &format!("{TASKS}\nexport fn main() -> Result<Unit, Error> {{\n{body}\n  Ok(())\n}}\n"),
7620            "main",
7621            &[],
7622        )
7623    }
7624
7625    #[test]
7626    fn an_async_fn_is_called_like_any_other_function_and_awaited() {
7627        let run = run_task_body("  let value = await answer()\n  println(\"{value}\")?");
7628        assert_eq!(run.output, "7\n");
7629    }
7630
7631    /// An `async fn` runs its body at the call site, so a call that is never
7632    /// awaited has still run by the time the call returns. ADR 0008 gives a
7633    /// thread to `spawn` rather than to every `async fn`, so the assertion
7634    /// here is that the effect happened, not when.
7635    #[test]
7636    fn an_async_fn_that_is_never_awaited_still_runs() {
7637        let source = r#"
7638use console.println
7639
7640async fn announce() -> Result<Unit, Error> {
7641  println("announced")?
7642  Ok(())
7643}
7644
7645export fn main() -> Result<Unit, Error> {
7646  let ignored = announce()
7647  Ok(())
7648}
7649"#;
7650        let run = run_entry_of(source, "main", &[]);
7651        assert!(run.output.contains("announced"), "{:?}", run.output);
7652    }
7653
7654    #[test]
7655    fn awaiting_a_result_propagates_with_a_question_mark() {
7656        let source = format!(
7657            "{TASKS}
7658export fn main() -> Result<Int, Error> {{
7659  let good = load(true).await()?
7660  println(\"{{good}}\")?
7661  let bad = load(false).await()?
7662  println(\"unreachable\")?
7663  Ok(bad)
7664}}
7665"
7666        );
7667        let run = run_entry_of(&source, "main", &[]);
7668        assert_eq!(run.output, "1\n");
7669        assert_eq!(run.value().to_string(), "Err(boom)");
7670    }
7671
7672    /// `await` binds looser than `?`, so `await load()?` applies `?` to the
7673    /// handle rather than to the value inside it. The diagnostic names the
7674    /// spelling that works.
7675    #[test]
7676    fn a_question_mark_on_a_task_points_at_await() {
7677        let error = run_task_body("  let task = load(true)\n  let value = task?").error();
7678        assert_eq!(
7679            error.message,
7680            "`?` needs a `Result` or an `Option`, but found `Task`"
7681        );
7682        assert!(
7683            error.help.unwrap().contains("task.await()?"),
7684            "the diagnostic shows the correction"
7685        );
7686    }
7687
7688    /// `await` binds tighter than `?`, so `await task()?` awaits and then
7689    /// propagates. The `?` applies to the `Result` the task produced, never
7690    /// to the task handle itself.
7691    #[test]
7692    fn a_question_mark_after_await_propagates_the_awaited_error() {
7693        let run = run_task_body("  let value = await load(true)?\n  println(\"{value}\")?");
7694        assert_eq!(run.output, "1\n");
7695
7696        let error = run_task_body("  let value = await load(false)?").value;
7697        match error {
7698            Ok(Value(Repr::Enum(result))) => {
7699                assert_eq!(&*result.case, "Err");
7700                assert_eq!(result.payload[0].to_string(), "boom");
7701            }
7702            other => panic!("expected the awaited `Err` to propagate, found {other:?}"),
7703        }
7704    }
7705
7706    #[test]
7707    fn both_await_spellings_settle_the_same_task() {
7708        let run = run_task_body(
7709            "  let prefix = await answer()\n  let postfix = answer().await()\n  println(\"{prefix} {postfix}\")?",
7710        );
7711        assert_eq!(run.output, "7 7\n");
7712    }
7713
7714    #[test]
7715    fn a_scope_awaits_the_tasks_it_spawned() {
7716        let run = run_task_body(
7717            "  scope tasks {\n    let first = tasks.spawn { 1 }\n    let second = tasks.spawn { 2 }\n    let a = await first\n    let b = second.await()\n    println(\"{a} {b}\")?\n  }",
7718        );
7719        assert_eq!(run.output, "1 2\n");
7720    }
7721
7722    #[test]
7723    fn leaving_a_scope_settles_a_task_the_body_never_awaited() {
7724        let run = run_task_body(
7725            "  scope tasks {\n    let ignored = tasks.spawn { println(\"the task ran\")? }\n  }\n  println(\"after the scope\")?",
7726        );
7727        assert_eq!(run.output, "the task ran\nafter the scope\n");
7728    }
7729
7730    #[test]
7731    fn returning_from_a_scope_cancels_a_task_that_is_still_running() {
7732        let source = format!(
7733            "{TASKS}
7734export fn main() -> Result<Unit, Error> {{
7735  scope tasks {{
7736    let ignored = tasks.spawn {{
7737{SPINNING_TASK}
7738    }}
7739    return Ok(())
7740  }}
7741}}
7742"
7743        );
7744        let run = run_entry_of(&source, "main", &[]);
7745        assert_eq!(run.output, "");
7746        assert_eq!(run.value().to_string(), "Ok(())");
7747    }
7748
7749    #[test]
7750    fn an_error_inside_a_scope_cancels_a_task_that_is_still_running() {
7751        let source = format!(
7752            "{TASKS}
7753export fn main() -> Result<Int, Error> {{
7754  scope tasks {{
7755    let ignored = tasks.spawn {{
7756{SPINNING_TASK}
7757    }}
7758    let value = load(false).await()?
7759    Ok(value)
7760  }}
7761}}
7762"
7763        );
7764        let run = run_entry_of(&source, "main", &[]);
7765        assert_eq!(run.output, "");
7766        assert_eq!(run.value().to_string(), "Err(boom)");
7767    }
7768
7769    #[test]
7770    fn a_task_that_fails_propagates_its_error_out_of_the_scope() {
7771        let source = format!(
7772            "{TASKS}
7773export fn main() -> Result<Unit, Error> {{
7774  scope tasks {{
7775    let failing = tasks.spawn {{ Err(Error(\"the task failed\")) }}
7776    println(\"the body finished\")?
7777  }}
7778  println(\"unreachable\")?
7779  Ok(())
7780}}
7781"
7782        );
7783        let run = run_entry_of(&source, "main", &[]);
7784        assert_eq!(run.output, "the body finished\n");
7785        assert_eq!(run.value().to_string(), "Err(the task failed)");
7786    }
7787
7788    #[test]
7789    fn awaiting_a_cancelled_task_is_rejected() {
7790        let run = run_task_body(&format!(
7791            "  scope tasks {{\n    let timer = tasks.spawn {{\n{SPINNING_TASK}\n    }}\n    timer.cancel()\n    let value = await timer\n  }}"
7792        ));
7793        assert_eq!(run.output, "");
7794        let error = run.error();
7795        assert!(error.message.contains("was cancelled"), "{}", error.message);
7796        assert!(error.rule.unwrap().contains("waits for or cancels"));
7797    }
7798
7799    #[test]
7800    fn awaiting_the_same_handle_twice_runs_the_body_once() {
7801        let run = run_task_body(
7802            "  scope tasks {\n    let once = tasks.spawn {\n      println(\"the body ran\")?\n      7\n    }\n    let first = await once\n    let second = await once\n    println(\"{first} {second}\")?\n  }",
7803        );
7804        assert_eq!(run.output, "the body ran\n7 7\n");
7805    }
7806
7807    #[test]
7808    fn awaiting_a_value_that_is_not_a_task_is_rejected() {
7809        let error = run_task_body("  let value = await 1").error();
7810        assert_eq!(error.message, "`await` needs a task, but found `Int`");
7811        assert!(error.rule.unwrap().contains("`await` settles a task"));
7812    }
7813
7814    // ------------------------------------------------------- task safety
7815
7816    #[test]
7817    fn spawning_a_closure_that_captures_a_vector_is_rejected() {
7818        let source = r#"
7819export fn main() -> Result<Unit, Error> {
7820  var items = Vector.of(1, 2)
7821  scope tasks {
7822    let counting = tasks.spawn { items.length() }
7823  }
7824  Ok(())
7825}
7826"#;
7827        let error = run_entry_of(source, "main", &[]).error();
7828        assert_eq!(
7829            error.message,
7830            "`spawn` cannot capture `items`, which is a `Vector`"
7831        );
7832        assert!(error
7833            .rule
7834            .unwrap()
7835            .contains("A vector cannot cross, even through `let`"));
7836        let help = error.help.unwrap();
7837        assert!(
7838            help.contains("freeze()") && help.contains("toArray()"),
7839            "{help}"
7840        );
7841    }
7842
7843    #[test]
7844    fn spawning_a_closure_that_captures_the_frozen_array_is_accepted() {
7845        let source = r#"
7846use console.println
7847
7848export fn main() -> Result<Unit, Error> {
7849  var items = Vector.of(1, 2)
7850  let frozen = items.freeze()
7851  scope tasks {
7852    let counting = tasks.spawn { frozen.length() }
7853    let total = await counting
7854    println("{total}")?
7855  }
7856  Ok(())
7857}
7858"#;
7859        assert_eq!(run_entry_of(source, "main", &[]).output, "2\n");
7860    }
7861
7862    #[test]
7863    fn task_safety_names_the_field_that_cannot_cross() {
7864        let source = r#"
7865struct Draft {
7866  guests: Vector<String>
7867}
7868
7869export fn main() -> Result<Unit, Error> {
7870  let draft = Draft(guests: Vector.of("Alice"))
7871  scope tasks {
7872    let counting = tasks.spawn { draft.guests.length() }
7873  }
7874  Ok(())
7875}
7876"#;
7877        let error = run_entry_of(source, "main", &[]).error();
7878        assert_eq!(
7879            error.message,
7880            "`spawn` cannot capture `draft.guests`, which is a `Vector`"
7881        );
7882    }
7883
7884    #[test]
7885    fn a_closure_is_task_safe_only_when_every_capture_is() {
7886        let source = r#"
7887export fn main() -> Result<Unit, Error> {
7888  var seen = Vector.of(1)
7889  let count = fn() {
7890    seen.length()
7891  }
7892  scope tasks {
7893    let counting = tasks.spawn { count() }
7894  }
7895  Ok(())
7896}
7897"#;
7898        let error = run_entry_of(source, "main", &[]).error();
7899        assert_eq!(
7900            error.message,
7901            "`spawn` cannot capture `count -> seen`, which is a `Vector`"
7902        );
7903    }
7904
7905    /// A vector reached through an array element and then a struct field. The
7906    /// path a diagnostic reports is how the value was reached, so a
7907    /// programmer looking for what to change reads the way in rather than the
7908    /// name of the whole capture.
7909    #[test]
7910    fn task_safety_names_the_array_element_that_cannot_cross() {
7911        let source = r#"
7912struct Draft {
7913  guests: Vector<String>
7914}
7915
7916export fn main() -> Result<Unit, Error> {
7917  let drafts = [Draft(guests: Vector.of("Alice"))]
7918  scope tasks {
7919    let counting = tasks.spawn { drafts.length() }
7920  }
7921  Ok(())
7922}
7923"#;
7924        let error = run_entry_of(source, "main", &[]).error();
7925        assert_eq!(
7926            error.message,
7927            "`spawn` cannot capture `drafts[0].guests`, which is a `Vector`"
7928        );
7929    }
7930
7931    /// An enum is a tagged union, so what a case carries is reached through
7932    /// the case that carries it and the position it sits in.
7933    #[test]
7934    fn task_safety_names_the_enum_payload_that_cannot_cross() {
7935        let source = r#"
7936enum Draft {
7937  Empty
7938  Guests(Vector<String>)
7939}
7940
7941export fn main() -> Result<Unit, Error> {
7942  let draft = Draft.Guests(Vector.of("Alice"))
7943  scope tasks {
7944    let counting = tasks.spawn { draft }
7945  }
7946  Ok(())
7947}
7948"#;
7949        let error = run_entry_of(source, "main", &[]).error();
7950        assert_eq!(
7951            error.message,
7952            "`spawn` cannot capture `draft.Guests(0)`, which is a `Vector`"
7953        );
7954    }
7955
7956    /// A payload-free case of the same enum crosses: what decides is the
7957    /// value a case carries, never the type it belongs to.
7958    #[test]
7959    fn an_enum_case_that_carries_nothing_crosses_a_task_boundary() {
7960        let source = r#"
7961use console.println
7962
7963enum Draft {
7964  Empty
7965  Guests(Vector<String>)
7966}
7967
7968export fn main() -> Result<Unit, Error> {
7969  let draft = Draft.Empty
7970  scope tasks {
7971    let crossing = tasks.spawn { draft }
7972    println("{await crossing}")?
7973  }
7974  Ok(())
7975}
7976"#;
7977        assert_eq!(run_entry_of(source, "main", &[]).output, "Empty\n");
7978    }
7979
7980    /// A trait object is task-safe exactly when the value it holds is: the
7981    /// wrapper adds a trait name, which is not state. So the diagnostic names
7982    /// the field inside, and says nothing about the trait.
7983    #[test]
7984    fn task_safety_looks_through_a_trait_object_to_the_value_it_holds() {
7985        let source = r#"
7986trait Summary {
7987  fn summarize(self) -> String
7988}
7989
7990struct Draft {
7991  guests: Vector<String>
7992}
7993
7994impl Summary for Draft {
7995  fn summarize(self) -> String {
7996    "a draft"
7997  }
7998}
7999
8000export fn main() -> Result<Unit, Error> {
8001  let entry: dyn Summary = Draft(guests: Vector.of("Alice"))
8002  scope tasks {
8003    let describing = tasks.spawn { entry.summarize() }
8004  }
8005  Ok(())
8006}
8007"#;
8008        let error = run_entry_of(source, "main", &[]).error();
8009        assert_eq!(
8010            error.message,
8011            "`spawn` cannot capture `entry.guests`, which is a `Vector`"
8012        );
8013    }
8014
8015    /// The same trait object over a value that may cross does cross, and
8016    /// dispatch on the far side still reaches the implementation the value
8017    /// carried with it.
8018    #[test]
8019    fn a_trait_object_over_a_task_safe_value_crosses_and_still_dispatches() {
8020        let source = r#"
8021use console.println
8022
8023trait Summary {
8024  fn summarize(self) -> String
8025}
8026
8027struct Draft {
8028  guests: Array<String>
8029}
8030
8031impl Summary for Draft {
8032  fn summarize(self) -> String {
8033    "a draft of {self.guests.length()}"
8034  }
8035}
8036
8037export fn main() -> Result<Unit, Error> {
8038  let entry: dyn Summary = Draft(guests: ["Alice"])
8039  scope tasks {
8040    let describing = tasks.spawn { entry.summarize() }
8041    println("{await describing}")?
8042  }
8043  Ok(())
8044}
8045"#;
8046        assert_eq!(run_entry_of(source, "main", &[]).output, "a draft of 1\n");
8047    }
8048
8049    // --------------------------------------------------- real concurrency
8050
8051    /// Runs `source`'s `main` with `console` and a real `clock` granted, and
8052    /// reports how long the whole run took.
8053    fn run_timed(source: &str) -> (Run, Duration) {
8054        let (sources, program) = program_of(source);
8055        let buffer = Buffer::default();
8056        let mut hosts = HostRegistry::new(Grants::new(["console", "clock"]));
8057        hosts.register(Box::new(Console::new(buffer.clone(), Buffer::default())));
8058        hosts.register(Box::new(crate::clock::Clock::real()));
8059        let runtime = Runtime::new(program, sources, Arc::new(hosts));
8060        let started = Instant::now();
8061        let value = Interpreter::new(&runtime).run_entry("test", "main", Vec::new());
8062        let elapsed = started.elapsed();
8063        (
8064            Run {
8065                value,
8066                output: buffer.text(),
8067            },
8068            elapsed,
8069        )
8070    }
8071
8072    /// Collects every event a run traced, for assertions.
8073    #[derive(Clone, Default)]
8074    struct RecordingSink(Arc<Mutex<Vec<TraceEvent>>>);
8075
8076    impl RecordingSink {
8077        fn events(&self) -> Vec<TraceEvent> {
8078            self.0.lock().expect("no test panics while tracing").clone()
8079        }
8080    }
8081
8082    impl crate::trace::TraceSink for RecordingSink {
8083        fn record(&self, event: TraceEvent) {
8084            self.0
8085                .lock()
8086                .expect("no test panics while tracing")
8087                .push(event);
8088        }
8089    }
8090
8091    /// Runs `source`'s `main` with `console` and a real `clock` granted,
8092    /// reporting what it traced and how long it took.
8093    fn run_traced(source: &str) -> (Run, Vec<TraceEvent>, Duration) {
8094        run_traced_under(source, Limits::default())
8095    }
8096
8097    /// The same, under `limits`, for the tests that are about what stops a
8098    /// run rather than about what it computes.
8099    fn run_traced_under(source: &str, limits: Limits) -> (Run, Vec<TraceEvent>, Duration) {
8100        let (sources, program) = program_of(source);
8101        let buffer = Buffer::default();
8102        let sink = RecordingSink::default();
8103        let mut hosts = HostRegistry::new(Grants::new(["console", "clock"]));
8104        hosts.register(Box::new(Console::new(buffer.clone(), Buffer::default())));
8105        hosts.register(Box::new(crate::clock::Clock::real()));
8106        hosts.set_budget(Budget::new(limits));
8107        hosts.set_trace(Arc::new(sink.clone()));
8108        let runtime =
8109            Runtime::new(program, sources, Arc::new(hosts)).with_trace(Arc::new(sink.clone()));
8110        let started = Instant::now();
8111        let value = Interpreter::new(&runtime).run_entry("test", "main", Vec::new());
8112        let elapsed = started.elapsed();
8113        (
8114            Run {
8115                value,
8116                output: buffer.text(),
8117            },
8118            sink.events(),
8119            elapsed,
8120        )
8121    }
8122
8123    /// How a run of `source`'s `main` ended, under `limits`, granting
8124    /// `grants`, and with `cancellation` as the run's own stop flag.
8125    ///
8126    /// Every classification a trace can carry is produced by a real run
8127    /// here rather than by building the event by hand: the point of the
8128    /// terminal event is that the runtime can tell these cases apart, and a
8129    /// test that constructed the answer itself would not test that.
8130    fn run_ended(
8131        source: &str,
8132        limits: Limits,
8133        grants: &[&str],
8134        cancellation: Cancellation,
8135    ) -> (RunOutcome, Option<String>) {
8136        let (sources, program) = program_of(source);
8137        let sink = RecordingSink::default();
8138        let mut hosts = HostRegistry::new(Grants::new(grants.iter().copied()));
8139        hosts.register(Box::new(Console::new(Buffer::default(), Buffer::default())));
8140        hosts.set_budget(Budget::with_cancellation(limits, cancellation));
8141        hosts.set_trace(Arc::new(sink.clone()));
8142        let runtime =
8143            Runtime::new(program, sources, Arc::new(hosts)).with_trace(Arc::new(sink.clone()));
8144        let _ = Interpreter::new(&runtime).run_entry("test", "main", Vec::new());
8145        let events = sink.events();
8146        // The terminal event is terminal: nothing a run traces comes after
8147        // it, whatever the run did.
8148        match events.last() {
8149            Some(TraceEvent::RunEnded { outcome, message }) => (*outcome, message.clone()),
8150            other => panic!("a run's last event must be `run_ended`, found {other:?}"),
8151        }
8152    }
8153
8154    /// A run of `source`'s `main` that needs nothing but `console`.
8155    fn ended(source: &str) -> (RunOutcome, Option<String>) {
8156        run_ended(source, Limits::default(), &["console"], Cancellation::new())
8157    }
8158
8159    /// A `main` around `body`, for the terminal-event tests.
8160    fn main_of(body: &str) -> String {
8161        format!("use console.println\n\nexport fn main() -> Result<Unit, Error> {{\n{body}\n}}\n")
8162    }
8163
8164    #[test]
8165    fn a_run_that_finished_ends_with_success_and_says_nothing_more() {
8166        assert_eq!(
8167            ended(&main_of("  println(\"hi\")?\n  Ok(())")),
8168            (RunOutcome::Success, None)
8169        );
8170    }
8171
8172    /// A returned `Err` is the program saying what it was written to say, so
8173    /// it is its own outcome rather than one more kind of failure — and the
8174    /// message it carries is the one the program wrote.
8175    #[test]
8176    fn a_run_whose_entry_returned_an_error_ends_with_that_error_and_its_message() {
8177        assert_eq!(
8178            ended(&main_of("  Err(Error(message: \"no report\"))")),
8179            (RunOutcome::Error, Some("no report".to_string()))
8180        );
8181    }
8182
8183    #[test]
8184    fn a_run_that_broke_an_invariant_ends_with_that() {
8185        let (outcome, message) = ended(&main_of("  let n = 1 / 0\n  Ok(())"));
8186        assert_eq!(outcome, RunOutcome::Invariant);
8187        assert_eq!(message.as_deref(), Some("`Int` division by zero"));
8188    }
8189
8190    /// A capability the run was not granted is the boundary refusing, which
8191    /// is neither the program's own failure nor a limit the run passed.
8192    #[test]
8193    fn a_run_the_host_boundary_refused_ends_with_that() {
8194        let (outcome, message) = run_ended(
8195            &main_of("  println(\"hi\")?\n  Ok(())"),
8196            Limits::default(),
8197            &[],
8198            Cancellation::new(),
8199        );
8200        assert_eq!(outcome, RunOutcome::HostBoundary);
8201        assert!(
8202            message.is_some_and(|message| message.contains("requires the `console` capability")),
8203            "the message names what was refused"
8204        );
8205    }
8206
8207    /// Each runtime control is its own classification: a reader deciding what
8208    /// to do about a stopped run wants to know which control stopped it.
8209    #[test]
8210    fn each_limit_that_stops_a_run_ends_it_with_that_limit_s_own_name() {
8211        let looping = main_of("  var i = 0\n  while true {\n    i = i + 1\n  }\n  Ok(())");
8212        let stopped = |limits: Limits, source: &str| {
8213            run_ended(source, limits, &["console"], Cancellation::new()).0
8214        };
8215        assert_eq!(
8216            stopped(
8217                Limits {
8218                    fuel: Some(100),
8219                    ..Limits::default()
8220                },
8221                &looping
8222            ),
8223            RunOutcome::Fuel
8224        );
8225        assert_eq!(
8226            stopped(
8227                Limits {
8228                    deadline: Some(Duration::from_millis(1)),
8229                    ..Limits::default()
8230                },
8231                &looping
8232            ),
8233            RunOutcome::Deadline
8234        );
8235        assert_eq!(
8236            stopped(
8237                Limits {
8238                    max_host_calls: Some(0),
8239                    ..Limits::default()
8240                },
8241                &main_of("  println(\"hi\")?\n  Ok(())")
8242            ),
8243            RunOutcome::HostCalls
8244        );
8245        assert_eq!(
8246            stopped(
8247                Limits {
8248                    max_call_depth: Some(2),
8249                    ..Limits::default()
8250                },
8251                &format!(
8252                    "fn down(n: Int) -> Int {{\n  if n == 0 {{ 0 }} else {{ down(n - 1) }}\n}}\n\n{}",
8253                    main_of("  let n = down(8)\n  Ok(())")
8254                )
8255            ),
8256            RunOutcome::CallDepth
8257        );
8258        assert_eq!(
8259            stopped(
8260                Limits {
8261                    max_tasks: Some(1),
8262                    ..Limits::default()
8263                },
8264                &main_of(
8265                    "  scope many {\n    let a = many.spawn { 1 }\n    let b = many.spawn { 2 }\n    let total = await a + await b\n  }\n  Ok(())"
8266                )
8267            ),
8268            RunOutcome::Concurrency
8269        );
8270    }
8271
8272    /// The one stop `cove run` cannot itself raise, and the reason the
8273    /// classification exists: a host embedding the runtime cancels a run
8274    /// through the flag it kept, and the trace says that is what happened.
8275    #[test]
8276    fn a_run_cancelled_from_outside_ends_with_that() {
8277        let cancellation = Cancellation::new();
8278        cancellation.cancel();
8279        assert_eq!(
8280            run_ended(
8281                &main_of("  println(\"hi\")?\n  Ok(())"),
8282                Limits::default(),
8283                &["console"],
8284                cancellation,
8285            )
8286            .0,
8287            RunOutcome::Cancelled
8288        );
8289    }
8290
8291    /// A run that never reached its entry still ended, and a trace that said
8292    /// nothing about it would be a trace with no ending at all.
8293    #[test]
8294    fn a_run_that_could_not_find_its_entry_still_ends_with_an_event() {
8295        let (sources, program) = program_of(&main_of("  Ok(())"));
8296        let sink = RecordingSink::default();
8297        let runtime = Runtime::new(
8298            program,
8299            sources,
8300            Arc::new(HostRegistry::new(Grants::new(["console"]))),
8301        )
8302        .with_trace(Arc::new(sink.clone()));
8303        let outcome = Interpreter::new(&runtime).run_entry("test", "absent", Vec::new());
8304        assert!(outcome.is_err());
8305        let events = sink.events();
8306        assert!(
8307            matches!(
8308                events.as_slice(),
8309                [TraceEvent::RunEnded {
8310                    outcome: RunOutcome::Invariant,
8311                    ..
8312                }]
8313            ),
8314            "{events:?}"
8315        );
8316    }
8317
8318    /// The acceptance criterion of issue #61: a trace of concurrent tasks can
8319    /// be grouped by which task did the I/O, unambiguously.
8320    ///
8321    /// Three tasks each make two host calls around a wait, so their calls
8322    /// genuinely interleave and no order is fixed. What is fixed is whose
8323    /// each one was: every call a task made carries that task's id, so
8324    /// grouping by it recovers exactly the three pairs the program wrote,
8325    /// with the entry's own call under its own id and mixed into none of
8326    /// them.
8327    #[test]
8328    fn every_host_call_names_the_task_that_made_it() {
8329        let source = r#"
8330use clock.sleep
8331use console.println
8332
8333fn work(label: String) -> Result<Unit, Error> {
8334  println("{label} started")?
8335  sleep(1ms)
8336  println("{label} finished")
8337}
8338
8339export fn main() -> Result<Unit, Error> {
8340  println("entry")?
8341  scope workers {
8342    let a = workers.spawn { work("a") }
8343    let b = workers.spawn { work("b") }
8344    let c = workers.spawn { work("c") }
8345    await a?
8346    await b?
8347    await c?
8348  }
8349  Ok(())
8350}
8351"#;
8352        let (run, events, _) = run_traced(source);
8353        run.value();
8354
8355        let mut said: std::collections::BTreeMap<u64, Vec<String>> =
8356            std::collections::BTreeMap::new();
8357        for event in &events {
8358            let TraceEvent::HostCall { task, op, args, .. } = event else {
8359                continue;
8360            };
8361            if op != "println" {
8362                continue;
8363            }
8364            let crate::trace::RecordedValue::Carried(transfer) = &args[0] else {
8365                panic!("a printed line is a string, which crosses a boundary whole");
8366            };
8367            said.entry(*task)
8368                .or_default()
8369                .push(transfer.clone().into_value().to_string());
8370        }
8371
8372        // The entry called a host too, under the id a call outside any
8373        // spawned task is made with, and nothing a task said is under it.
8374        assert_eq!(said.remove(&ENTRY_TASK), Some(vec!["entry".to_string()]));
8375
8376        // Three tasks, three ids, and each id's calls are one label's — a
8377        // grouping that mixed two tasks would show up as a group with two
8378        // labels in it.
8379        assert_eq!(said.len(), 3, "{said:?}");
8380        let mut labels: Vec<String> = Vec::new();
8381        for (task, lines) in &said {
8382            assert_ne!(*task, ENTRY_TASK);
8383            let label = lines[0]
8384                .split_once(' ')
8385                .expect("a line is `<label> <what>`")
8386                .0
8387                .to_string();
8388            assert_eq!(
8389                *lines,
8390                vec![format!("{label} started"), format!("{label} finished")],
8391                "task {task} said something another task said"
8392            );
8393            labels.push(label);
8394        }
8395        labels.sort();
8396        assert_eq!(labels, ["a", "b", "c"]);
8397    }
8398
8399    #[test]
8400    fn a_task_can_spawn_tasks_of_its_own() {
8401        let run = run_task_body(
8402            "  scope outer {\n    let parent = outer.spawn {\n      scope inner {\n        let a = inner.spawn { 1 }\n        let b = inner.spawn { 2 }\n        await a + await b\n      }\n    }\n    println(\"{await parent}\")?\n  }",
8403        );
8404        assert_eq!(run.output, "3\n");
8405    }
8406
8407    /// A value leaving a task crosses the same boundary its body crossed to
8408    /// get there, so it answers to the same rule: a vector cannot come back
8409    /// out of a task any more than it could go in.
8410    #[test]
8411    fn a_task_cannot_produce_a_value_that_may_not_cross() {
8412        let source = r#"
8413export fn main() -> Result<Unit, Error> {
8414  scope tasks {
8415    let building = tasks.spawn { Vector.of(1, 2) }
8416    let items = await building
8417  }
8418  Ok(())
8419}
8420"#;
8421        let error = run_entry_of(source, "main", &[]).error();
8422        assert_eq!(
8423            error.message,
8424            "this task produced a `Vector`, which cannot leave a task"
8425        );
8426    }
8427
8428    /// The success criterion itself, read off a trace: each task's wait is
8429    /// attributed to that task, and the waits add up to more than the run
8430    /// took, which is only possible if they happened at the same time.
8431    #[test]
8432    fn a_trace_attributes_each_task_s_wait_to_that_task() {
8433        let source = r#"
8434use clock.sleep
8435
8436export fn main() -> Result<Unit, Error> {
8437  scope waits {
8438    let first = waits.spawn { sleep(300ms) }
8439    let second = waits.spawn { sleep(300ms) }
8440    await first
8441    await second
8442  }
8443  Ok(())
8444}
8445"#;
8446        let (run, events, elapsed) = run_traced(source);
8447        run.value();
8448
8449        // Every one of these events was produced on a task's own thread and
8450        // written by the sink the run shares, so reading them back is also
8451        // the evidence that an event, and the values it carries, may cross a
8452        // task boundary.
8453        let sleeps: Vec<&TraceEvent> = events
8454            .iter()
8455            .filter(|event| matches!(event, TraceEvent::HostCall { op, .. } if op == "sleep"))
8456            .collect();
8457        assert_eq!(sleeps.len(), 2);
8458        for event in &sleeps {
8459            let TraceEvent::HostCall { args, .. } = event else {
8460                unreachable!("filtered to host calls")
8461            };
8462            assert_eq!(args.len(), 1);
8463            assert_eq!(
8464                crate::trace::value_to_json(
8465                    &match &args[0] {
8466                        crate::trace::RecordedValue::Carried(transfer) =>
8467                            transfer.clone().into_value(),
8468                        other => panic!("expected a carried duration, found {other:?}"),
8469                    },
8470                    crate::trace::ValueCapture::Full
8471                ),
8472                r#"{"type":"duration","ns":300000000}"#
8473            );
8474        }
8475        let waited: Duration = sleeps
8476            .iter()
8477            .filter_map(|event| match event {
8478                TraceEvent::HostCall { wait, .. } => Some(*wait),
8479                _ => None,
8480            })
8481            .sum();
8482        assert!(
8483            waited > elapsed,
8484            "the two waits total {waited:?}, which is not more than the {elapsed:?} the run took"
8485        );
8486        assert_eq!(
8487            events
8488                .iter()
8489                .filter(|event| matches!(event, TraceEvent::TaskCompleted { .. }))
8490                .count(),
8491            2
8492        );
8493    }
8494
8495    /// Cancellation reaches a task that is already running: it stops at its
8496    /// next safepoint, and the trace says it was cancelled rather than that
8497    /// it completed.
8498    #[test]
8499    fn cancelling_a_running_task_stops_it_and_traces_it() {
8500        let source = format!(
8501            "{TASKS}
8502export fn main() -> Result<Unit, Error> {{
8503  scope tasks {{
8504    let ignored = tasks.spawn {{
8505{SPINNING_TASK}
8506    }}
8507    return Ok(())
8508  }}
8509}}
8510"
8511        );
8512        let (run, events, _) = run_traced(&source);
8513        assert_eq!(run.output, "");
8514        assert!(events
8515            .iter()
8516            .any(|event| matches!(event, TraceEvent::TaskCancelled { id: 1 })));
8517        assert!(!events
8518            .iter()
8519            .any(|event| matches!(event, TraceEvent::TaskCompleted { .. })));
8520    }
8521
8522    /// ADR 0001 lists "CPU time and I/O wait are accurately attributable in
8523    /// traces" as a success criterion, and ADR 0003 records that phase 1
8524    /// could not validate it, because with one task running at a time nothing
8525    /// overlapped. This is the smallest observation that it now does: two
8526    /// tasks that each wait 300ms finish in about 300ms, not 600ms.
8527    #[test]
8528    fn two_tasks_wait_at_the_same_time() {
8529        let source = r#"
8530use clock.sleep
8531
8532export fn main() -> Result<Unit, Error> {
8533  scope waits {
8534    let first = waits.spawn { sleep(300ms) }
8535    let second = waits.spawn { sleep(300ms) }
8536    await first
8537    await second
8538  }
8539  Ok(())
8540}
8541"#;
8542        let (run, elapsed) = run_timed(source);
8543        run.value();
8544        assert!(
8545            elapsed >= Duration::from_millis(250),
8546            "both tasks really waited, but the run took {elapsed:?}"
8547        );
8548        assert!(
8549            elapsed < Duration::from_millis(550),
8550            "the waits overlapped, but the run took {elapsed:?}, which is closer to their sum"
8551        );
8552    }
8553
8554    #[test]
8555    fn a_scope_with_two_tasks_produces_both_values() {
8556        let run = run_task_body(
8557            "  scope tasks {\n    let first = tasks.spawn { 1 }\n    let second = tasks.spawn { 2 }\n    println(\"{await first} {await second}\")?\n  }",
8558        );
8559        assert_eq!(run.output, "1 2\n");
8560    }
8561
8562    /// A task draws its fuel from the run's budget, so exhausting it inside a
8563    /// task stops the run exactly as exhausting it in the entry would.
8564    #[test]
8565    fn a_budget_exhausted_inside_a_task_stops_the_run() {
8566        let source = r#"
8567export fn main() -> Result<Unit, Error> {
8568  scope tasks {
8569    let spinning = tasks.spawn {
8570      var i = 0
8571      while i < 1000000000 {
8572        i += 1
8573      }
8574      i
8575    }
8576    await spinning
8577  }
8578  Ok(())
8579}
8580"#;
8581        let (sources, program) = program_of(source);
8582        let mut hosts = HostRegistry::new(Grants::new(["console"]));
8583        hosts.register(Box::new(Console::new(Buffer::default(), Buffer::default())));
8584        hosts.set_budget(Budget::new(Limits {
8585            fuel: Some(10_000),
8586            ..Limits::default()
8587        }));
8588        let runtime = Runtime::new(program, sources, Arc::new(hosts));
8589        let error = Interpreter::new(&runtime)
8590            .run_entry("test", "main", Vec::new())
8591            .expect_err("the fuel budget stops the run");
8592        assert!(error.message.contains("fuel budget"), "{}", error.message);
8593        assert!(
8594            runtime
8595                .hosts()
8596                .with_budget(|budget| budget.fuel_spent())
8597                .unwrap_or_default()
8598                >= 10_000
8599        );
8600    }
8601
8602    // -------------------------------------------------- leaving a scope
8603    //
8604    // "Concurrent work belongs to a task scope. Leaving the scope waits for
8605    // or cancels its child tasks." That rule is about every child and about
8606    // every way out, so this section takes each exit a scope has — running
8607    // off the end of its body, `return`, a propagated `Err`, a cancellation
8608    // the program asked for, and a broken invariant — and reads the trace
8609    // for what became of every task that was spawned. The outcome a test
8610    // must never find is a child that was neither joined nor cancelled,
8611    // because that is a thread the scope outlived.
8612
8613    /// What the trace says became of each task the run spawned, in spawn
8614    /// order, as `(id, joined, cancelled)`.
8615    ///
8616    /// The children are read from the events rather than from the source, so
8617    /// a test asserts on the ones the run actually had and not on the ones
8618    /// its author remembered writing.
8619    fn children_of(events: &[TraceEvent]) -> Vec<(u64, bool, bool)> {
8620        let mut children: Vec<(u64, bool, bool)> = Vec::new();
8621        for event in events {
8622            match event {
8623                // Spawning is traced before the thread starts, so a child is
8624                // always in the list before anything can settle it.
8625                TraceEvent::TaskSpawned { id, .. } => children.push((*id, false, false)),
8626                TraceEvent::TaskCompleted { id, .. } => {
8627                    if let Some(child) = children.iter_mut().find(|child| child.0 == *id) {
8628                        child.1 = true;
8629                    }
8630                }
8631                TraceEvent::TaskCancelled { id } => {
8632                    if let Some(child) = children.iter_mut().find(|child| child.0 == *id) {
8633                        child.2 = true;
8634                    }
8635                }
8636                _ => {}
8637            }
8638        }
8639        children
8640    }
8641
8642    /// The rule itself, in the form every exit has to satisfy.
8643    fn assert_every_child_settled(events: &[TraceEvent]) {
8644        let children = children_of(events);
8645        assert!(
8646            !children.is_empty(),
8647            "the run spawned no task, so it cannot show what a scope does with one"
8648        );
8649        for (id, joined, cancelled) in &children {
8650            assert!(
8651                *joined || *cancelled,
8652                "task {id} was neither joined nor cancelled: {children:?}"
8653            );
8654        }
8655    }
8656
8657    /// The scope runs off the end of its body. It waits for the child the
8658    /// body never awaited — which is why that child's line comes before the
8659    /// line after the scope — and cancels nothing.
8660    #[test]
8661    fn a_scope_that_completes_normally_joins_the_child_it_never_awaited() {
8662        let source = r#"
8663use console.println
8664
8665export fn main() -> Result<Unit, Error> {
8666  scope tasks {
8667    let awaited = tasks.spawn { 1 }
8668    let ignored = tasks.spawn { println("the child the body never awaited ran")? }
8669    await awaited
8670  }
8671  println("the scope was left")?
8672  Ok(())
8673}
8674"#;
8675        let (run, events, _) = run_traced(source);
8676        assert_eq!(
8677            run.output,
8678            "the child the body never awaited ran\nthe scope was left\n"
8679        );
8680        run.value();
8681        assert_every_child_settled(&events);
8682        assert_eq!(
8683            children_of(&events),
8684            vec![(1, true, false), (2, true, false)]
8685        );
8686    }
8687
8688    /// `return` leaves the scope early. The child that had already been
8689    /// awaited keeps what it did, and the one still running is cancelled:
8690    /// cancellation stops work that has not happened, it does not undo work
8691    /// that has.
8692    #[test]
8693    fn a_scope_left_by_return_cancels_only_the_child_still_running() {
8694        let source = format!(
8695            "use console.println
8696
8697export fn main() -> Result<Unit, Error> {{
8698  scope tasks {{
8699    let quick = tasks.spawn {{ println(\"the quick child ran\")? }}
8700    let spinning = tasks.spawn {{
8701{SPINNING_TASK}
8702    }}
8703    await quick
8704    return Ok(())
8705  }}
8706}}
8707"
8708        );
8709        let (run, events, _) = run_traced(&source);
8710        assert_eq!(run.output, "the quick child ran\n");
8711        run.value();
8712        assert_every_child_settled(&events);
8713        assert_eq!(
8714            children_of(&events),
8715            vec![(1, true, false), (2, false, true)]
8716        );
8717    }
8718
8719    /// An `Err` propagated out of the scope's body with `?` leaves it the way
8720    /// `return` does: the error is the scope's value, and the child still
8721    /// running is cancelled rather than waited for.
8722    #[test]
8723    fn a_scope_left_by_a_propagated_err_cancels_the_child_still_running() {
8724        let source = format!(
8725            "{TASKS}
8726export fn main() -> Result<Unit, Error> {{
8727  scope tasks {{
8728    let quick = tasks.spawn {{ println(\"the quick child ran\")? }}
8729    let spinning = tasks.spawn {{
8730{SPINNING_TASK}
8731    }}
8732    await quick
8733    await load(false)?
8734    println(\"never printed\")?
8735  }}
8736  Ok(())
8737}}
8738"
8739        );
8740        let (run, events, _) = run_traced(&source);
8741        assert_eq!(run.output, "the quick child ran\n");
8742        assert_eq!(run.value().to_string(), "Err(boom)");
8743        assert_every_child_settled(&events);
8744        assert_eq!(
8745            children_of(&events),
8746            vec![(1, true, false), (2, false, true)]
8747        );
8748    }
8749
8750    /// The program cancels a child itself and then leaves the scope normally.
8751    /// Asking is all `cancel` does, so the trace records the cancellation
8752    /// where the scope waits and learns that the child really stopped.
8753    #[test]
8754    fn a_child_the_program_cancelled_is_still_waited_for_at_scope_exit() {
8755        let source = format!(
8756            "use console.println
8757
8758export fn main() -> Result<Unit, Error> {{
8759  scope tasks {{
8760    let quick = tasks.spawn {{ println(\"the quick child ran\")? }}
8761    let spinning = tasks.spawn {{
8762{SPINNING_TASK}
8763    }}
8764    await quick
8765    spinning.cancel()
8766  }}
8767  println(\"the scope was left\")?
8768  Ok(())
8769}}
8770"
8771        );
8772        let (run, events, _) = run_traced(&source);
8773        assert_eq!(run.output, "the quick child ran\nthe scope was left\n");
8774        run.value();
8775        assert_every_child_settled(&events);
8776        assert_eq!(
8777            children_of(&events),
8778            vec![(1, true, false), (2, false, true)]
8779        );
8780    }
8781
8782    /// A broken invariant in the scope's own body. "Integer overflow is a
8783    /// broken invariant, not a wrapped result", so this is not an `Err` the
8784    /// body chose to propagate — and the scope still cancels its child, since
8785    /// leaving a scope is leaving it however it happened.
8786    #[test]
8787    fn a_scope_left_by_a_broken_invariant_cancels_the_child_still_running() {
8788        let source = format!(
8789            "use console.println
8790
8791export fn main() -> Result<Unit, Error> {{
8792  scope tasks {{
8793    let spinning = tasks.spawn {{
8794{SPINNING_TASK}
8795    }}
8796    let largest = 9223372036854775807
8797    println(\"never printed {{largest + 1}}\")?
8798  }}
8799  Ok(())
8800}}
8801"
8802        );
8803        let (run, events, _) = run_traced(&source);
8804        assert_eq!(run.output, "");
8805        let error = run.error();
8806        assert_eq!(error.message, "`Int` addition overflowed");
8807        assert_eq!(
8808            error.rule.as_deref(),
8809            Some("Integer overflow is a broken invariant, not a wrapped result.")
8810        );
8811        assert_every_child_settled(&events);
8812        assert_eq!(children_of(&events), vec![(1, false, true)]);
8813    }
8814
8815    /// A broken invariant inside a child rather than in the scope's body. The
8816    /// overflow reaches the scope through `await`, and the sibling that was
8817    /// still running is cancelled on the way out.
8818    ///
8819    /// The child that broke is traced as completed rather than as cancelled.
8820    /// ADR 0003 asks for "trace events for task spawn, completion, and
8821    /// cancellation", so the distinction the trace draws is between a thread
8822    /// that finished and one that was stopped, and a thread that finished by
8823    /// raising is on the first side of it.
8824    #[test]
8825    fn a_broken_invariant_in_a_child_leaves_the_scope_and_stops_its_sibling() {
8826        let source = format!(
8827            "use console.println
8828
8829export fn main() -> Result<Unit, Error> {{
8830  scope tasks {{
8831    let spinning = tasks.spawn {{
8832{SPINNING_TASK}
8833    }}
8834    let broken = tasks.spawn {{
8835      let largest = 9223372036854775807
8836      largest + 1
8837    }}
8838    await broken
8839  }}
8840  Ok(())
8841}}
8842"
8843        );
8844        let (run, events, _) = run_traced(&source);
8845        assert_eq!(run.output, "");
8846        assert_eq!(run.error().message, "`Int` addition overflowed");
8847        assert_every_child_settled(&events);
8848        assert_eq!(
8849            children_of(&events),
8850            vec![(1, false, true), (2, true, false)]
8851        );
8852    }
8853
8854    /// The Language Card lists concurrency beside the limits that do exist:
8855    /// "CPU, memory, time, concurrency, and Host-call limits are runtime
8856    /// controls, not termination proofs", and ADR 0001 lists "concurrency
8857    /// limits" among what the runtime should be able to impose.
8858    ///
8859    /// One is imposed here, and imposed before the thread exists: the run
8860    /// holds the eight tasks it was allowed, the ninth `spawn` stops it, and
8861    /// nothing was started to be stopped — the trace carries eight task
8862    /// spawns and not nine. See issue #37.
8863    #[test]
8864    fn spawning_past_a_concurrency_limit_is_refused_before_a_thread_exists() {
8865        let source = r#"
8866export fn main() -> Result<Unit, Error> {
8867  scope tasks {
8868    var i = 0
8869    while i < 64 {
8870      let ignored = tasks.spawn { 1 }
8871      i += 1
8872    }
8873  }
8874  Ok(())
8875}
8876"#;
8877        let (run, events, _) = run_traced_under(
8878            source,
8879            Limits {
8880                max_tasks: Some(8),
8881                ..Limits::default()
8882            },
8883        );
8884        let error = run.error();
8885        assert!(
8886            error
8887                .message
8888                .contains("concurrency limit of 8 task(s) exceeded"),
8889            "{}",
8890            error.message
8891        );
8892        assert!(error.span.is_some(), "the stop points at the `spawn`");
8893        assert!(error.rule.is_some());
8894        assert_eq!(
8895            events
8896                .iter()
8897                .filter(|event| matches!(event, TraceEvent::TaskSpawned { .. }))
8898                .count(),
8899            8,
8900            "the refused `spawn` was never given a thread, so it was never traced"
8901        );
8902    }
8903
8904    /// The limit bounds the tasks alive at once, not the tasks a run spawns
8905    /// over its life, so a task's place goes back when its end is observed.
8906    /// A task ends by finishing, by producing an `Err`, by being cancelled,
8907    /// or by breaking an invariant in its own thread, and a join is where all
8908    /// four are seen — so this run of four tasks, each ended before the next
8909    /// begins, is not stopped by a limit of one.
8910    #[test]
8911    fn a_task_whose_end_was_observed_gives_its_place_back() {
8912        let source = r#"
8913use console.println
8914
8915export fn main() -> Result<Unit, Error> {
8916  scope finishing {
8917    let one = finishing.spawn { 1 }
8918    let value = await one
8919  }
8920  scope failing {
8921    let two = failing.spawn { Err(Error("this task produced an error")) }
8922    let outcome = await two
8923  }
8924  scope cancelling {
8925    let three = cancelling.spawn { 3 }
8926    three.cancel()
8927  }
8928  scope last {
8929    let four = last.spawn { 4 }
8930    println("{await four}")?
8931  }
8932  Ok(())
8933}
8934"#;
8935        let (run, _, _) = run_traced_under(
8936            source,
8937            Limits {
8938                max_tasks: Some(1),
8939                ..Limits::default()
8940            },
8941        );
8942        assert_eq!(run.output, "4\n");
8943        run.value();
8944    }
8945
8946    /// Concurrency bounds the run and not one scope, the way memory bounds
8947    /// the run and not one task: a nested scope's `spawn` counts the tasks
8948    /// its parent is still holding, so a program cannot stay under the limit
8949    /// by spreading its tasks over more scopes.
8950    #[test]
8951    fn the_concurrency_limit_is_the_run_s_and_not_one_scope_s() {
8952        let source = r#"
8953export fn main() -> Result<Unit, Error> {
8954  scope outer {
8955    let one = outer.spawn { 1 }
8956    scope inner {
8957      let two = inner.spawn { 2 }
8958      let three = inner.spawn { 3 }
8959      let ignored = await two + await three
8960    }
8961    let value = await one
8962  }
8963  Ok(())
8964}
8965"#;
8966        let (run, _, _) = run_traced_under(
8967            source,
8968            Limits {
8969                max_tasks: Some(2),
8970                ..Limits::default()
8971            },
8972        );
8973        let error = run.error();
8974        assert!(
8975            error
8976                .message
8977                .contains("concurrency limit of 2 task(s) exceeded"),
8978            "{}",
8979            error.message
8980        );
8981    }
8982
8983    /// A run that stays inside the limit is not stopped by it, however many
8984    /// tasks it spawns in all.
8985    #[test]
8986    fn a_run_within_the_concurrency_limit_is_not_stopped() {
8987        let source = r#"
8988use console.println
8989
8990export fn main() -> Result<Unit, Error> {
8991  var total = 0
8992  var i = 0
8993  while i < 8 {
8994    scope tasks {
8995      let one = tasks.spawn { 1 }
8996      let two = tasks.spawn { 2 }
8997      total += await one + await two
8998    }
8999    i += 1
9000  }
9001  println("{total}")?
9002  Ok(())
9003}
9004"#;
9005        let (run, _, _) = run_traced_under(
9006            source,
9007            Limits {
9008                max_tasks: Some(2),
9009                ..Limits::default()
9010            },
9011        );
9012        assert_eq!(run.output, "24\n");
9013        run.value();
9014    }
9015
9016    // --------------------------------------------------------- deadlines
9017
9018    /// A run that finishes inside its deadline is not stopped, however
9019    /// generous the bound: a limit that fired early would be a limit on the
9020    /// machine rather than on the run.
9021    #[test]
9022    fn a_run_that_finishes_inside_its_deadline_is_not_stopped() {
9023        let source = r#"
9024use console.println
9025
9026export fn main() -> Result<Unit, Error> {
9027  println("inside the deadline")?
9028  Ok(())
9029}
9030"#;
9031        let (run, _, _) = run_traced_under(
9032            source,
9033            Limits {
9034                deadline: Some(Duration::from_secs(30)),
9035                ..Limits::default()
9036            },
9037        );
9038        assert_eq!(run.output, "inside the deadline\n");
9039        run.value();
9040    }
9041
9042    /// A deadline that expires while Cove code runs stops it at the next
9043    /// safepoint, and the diagnostic names the bound that was configured. The
9044    /// loop is bounded only so that a runtime which never observes the
9045    /// deadline fails the test instead of hanging.
9046    #[test]
9047    fn a_deadline_that_expires_while_cove_code_runs_stops_it_at_a_safepoint() {
9048        let source = r#"
9049use console.println
9050
9051export fn main() -> Result<Unit, Error> {
9052  var i = 0
9053  while i < 1000000000 {
9054    i += 1
9055  }
9056  println("never printed")?
9057  Ok(())
9058}
9059"#;
9060        let (run, _, _) = run_traced_under(
9061            source,
9062            Limits {
9063                deadline: Some(Duration::from_millis(50)),
9064                ..Limits::default()
9065            },
9066        );
9067        assert_eq!(run.output, "");
9068        let error = run.error();
9069        assert_eq!(
9070            error.message,
9071            "execution stopped: wall-clock deadline of 50ms exceeded"
9072        );
9073        assert!(error.rule.is_some(), "the stop cites the rule it enforces");
9074        assert!(error.span.is_some(), "the stop points at the loop");
9075    }
9076
9077    /// A deadline that expires while a Host call blocks stops the run only
9078    /// once that call has returned.
9079    ///
9080    /// That is the documented behaviour rather than a shortfall of it. The
9081    /// `clock` schema says of `sleep` that "a cancelled task stops at its
9082    /// next safepoint, which is after the wait it is already inside
9083    /// returns", and a safepoint is a place in Cove code. So the evidence is
9084    /// in the trace: the host call is recorded whole, having waited for as
9085    /// long as it was asked to, and the run stops afterwards.
9086    #[test]
9087    fn a_deadline_that_expires_while_a_host_call_blocks_stops_the_run_when_it_returns() {
9088        let source = r#"
9089use clock.sleep
9090use console.println
9091
9092export fn main() -> Result<Unit, Error> {
9093  sleep(750ms)?
9094  println("never printed")?
9095  Ok(())
9096}
9097"#;
9098        let (run, events, elapsed) = run_traced_under(
9099            source,
9100            Limits {
9101                deadline: Some(Duration::from_millis(250)),
9102                ..Limits::default()
9103            },
9104        );
9105        assert_eq!(run.output, "");
9106        assert_eq!(
9107            run.error().message,
9108            "execution stopped: wall-clock deadline of 250ms exceeded"
9109        );
9110        let waits: Vec<Duration> = events
9111            .iter()
9112            .filter_map(|event| match event {
9113                TraceEvent::HostCall { op, wait, .. } if op == "sleep" => Some(*wait),
9114                _ => None,
9115            })
9116            .collect();
9117        assert_eq!(waits.len(), 1, "the sleep was recorded once: {waits:?}");
9118        assert!(
9119            waits[0] >= Duration::from_millis(500),
9120            "the sleep ran to its end rather than being cut short, but waited {:?}",
9121            waits[0]
9122        );
9123        assert!(
9124            elapsed >= Duration::from_millis(500),
9125            "the run outlived its deadline for as long as the call held it, but took {elapsed:?}"
9126        );
9127    }
9128
9129    /// `clock.timeout` bounds work the program hands the host, which the host
9130    /// runs back on this task through `Reentry`. Work that finishes inside
9131    /// the bound answers `Ok` with its value.
9132    #[test]
9133    fn a_timeout_answers_ok_when_the_bounded_work_finishes_inside_it() {
9134        let source = r#"
9135use clock.timeout
9136use console.println
9137
9138export fn main() -> Result<Unit, Error> {
9139  let answer = clock.timeout(30s) {
9140    7
9141  }?
9142  println("the bounded work answered {answer}")?
9143  Ok(())
9144}
9145"#;
9146        let (run, events, _) = run_traced(source);
9147        assert_eq!(run.output, "the bounded work answered 7\n");
9148        run.value();
9149        assert!(
9150            events.iter().any(|event| {
9151                matches!(event, TraceEvent::HostCall { op, granted, .. } if op == "timeout" && *granted)
9152            }),
9153            "the bound is a granted host call, and the trace says so: {events:?}"
9154        );
9155    }
9156
9157    /// The other half: a bound that expires stops the Cove code it was given
9158    /// at that code's next safepoint, and the answer names the bound rather
9159    /// than whatever the stopped body was doing. The loop is bounded only so
9160    /// that a runtime which never delivers the stop fails instead of hanging.
9161    #[test]
9162    fn a_timeout_stops_cove_code_that_runs_past_its_bound() {
9163        let source = r#"
9164use clock.timeout
9165use console.println
9166
9167export fn main() -> Result<Unit, Error> {
9168  let outcome = clock.timeout(50ms) {
9169    var i = 0
9170    while i < 1000000000 {
9171      i += 1
9172    }
9173    i
9174  }
9175  println("{outcome}")?
9176  Ok(())
9177}
9178"#;
9179        let (run, events, _) = run_traced(source);
9180        assert_eq!(run.output, "Err(clock: timed out after 50ms)\n");
9181        run.value();
9182        assert!(
9183            events
9184                .iter()
9185                .any(|event| matches!(event, TraceEvent::HostCall { op, .. } if op == "timeout")),
9186            "the bound is a granted host call, and the trace says so: {events:?}"
9187        );
9188    }
9189
9190    // ------------------------------------------------- the reentry contract
9191
9192    /// Nests `clock.timeout` `levels` deep: every level is a host call whose
9193    /// callback calls a host that is handed work of its own, which is the
9194    /// Host → Cove → Host → Cove shape the reentry bound exists for.
9195    fn nested_reentry(levels: usize) -> Run {
9196        let source = format!(
9197            r#"
9198use clock.timeout
9199use console.println
9200
9201fn nest(n: Int) -> Int {{
9202  if n <= 0 {{
9203    0
9204  }} else {{
9205    let inner = clock.timeout(60s) {{ nest(n - 1) }}
9206    match inner {{
9207      Ok(deeper) => deeper + 1,
9208      Err(stopped) => 0 - 1,
9209    }}
9210  }}
9211}}
9212
9213export fn main() -> Result<Unit, Error> {{
9214  println("{{nest({levels})}}")?
9215  Ok(())
9216}}
9217"#
9218        );
9219        run_traced(&source).0
9220    }
9221
9222    /// Nesting is supported: the inner callback runs on the same task, the
9223    /// same heap, and the same budget as the outer one, and the values come
9224    /// back out through the host calls that were standing in for them.
9225    #[test]
9226    fn a_callback_may_call_a_host_that_runs_a_callback_of_its_own() {
9227        let run = nested_reentry(MAX_REENTRY_DEPTH);
9228        assert_eq!(run.output, format!("{MAX_REENTRY_DEPTH}\n"));
9229        run.value();
9230    }
9231
9232    /// And it is bounded. A native stack is what a reentry level spends, and
9233    /// how much of it a host spends per level is the host's business, so the
9234    /// count is what the runtime can hold: past it the run stops with an
9235    /// error naming the limit. Without this the same program aborts the
9236    /// process, which is the one failure a sandbox may not have.
9237    #[test]
9238    fn nested_reentry_past_the_bound_stops_the_run_rather_than_the_process() {
9239        let error = nested_reentry(MAX_REENTRY_DEPTH + 1).error();
9240        assert_eq!(
9241            error.message,
9242            format!(
9243                "reentry depth limit of {MAX_REENTRY_DEPTH} reached while a host ran a Cove callback"
9244            )
9245        );
9246        assert!(error.span.is_some(), "the stop points at the host call");
9247        assert!(error.rule.is_some());
9248    }
9249
9250    /// The depth limit is a promise about the native stack, so it holds only
9251    /// on a stack big enough for the frames it allows. Every thread the
9252    /// runtime runs Cove on is one it sized, and a spawned task's thread is
9253    /// the one that used to be a platform default: the same recursion that
9254    /// the entry reported a limit for overflowed a task's 2 MiB and ended the
9255    /// process, taking every sibling task with it.
9256    ///
9257    /// Both halves run inside `on_cove_stack`, which is what a host outside
9258    /// this crate does too, because the test harness's threads are not the
9259    /// runtime's to size. Only the message comes back: a `Value` is `Rc`-based
9260    /// and cannot cross a thread boundary.
9261    #[test]
9262    fn the_depth_limit_stops_a_spawned_task_the_way_it_stops_the_entry() {
9263        let recursing = r#"
9264fn nest(n: Int) -> Int {
9265  if n <= 0 {
9266    0
9267  } else {
9268    nest(n - 1) + 1
9269  }
9270}
9271"#;
9272        let depth = MAX_CALL_DEPTH + 16;
9273        let stop = |source: String| {
9274            crate::on_cove_stack(move || run_entry_of(&source, "main", &[]).error().message)
9275                .expect("a thread to run Cove on")
9276        };
9277
9278        let on_the_entry = format!(
9279            r#"{recursing}
9280export fn main() -> Result<Unit, Error> {{
9281  let answer = nest({depth})
9282  Ok(())
9283}}
9284"#
9285        );
9286        let in_a_task = format!(
9287            r#"{recursing}
9288export fn main() -> Result<Unit, Error> {{
9289  scope tasks {{
9290    let task = tasks.spawn {{ nest({depth}) }}
9291    let answer = task.await()
9292    Ok(())
9293  }}
9294}}
9295"#
9296        );
9297
9298        let expected = format!("call depth limit of {MAX_CALL_DEPTH} reached while calling `nest`");
9299        assert_eq!(stop(on_the_entry), expected);
9300        assert_eq!(stop(in_a_task), expected);
9301    }
9302
9303    /// Fuel is the run's, and a callback is the run's work: the interpreter
9304    /// that charges a safepoint inside a callback is the one that charged the
9305    /// statement that made the host call, so a body handed to a host cannot
9306    /// buy a program more of anything.
9307    #[test]
9308    fn work_a_callback_does_is_charged_to_the_budget_that_made_the_host_call() {
9309        let source = r#"
9310use clock.timeout
9311
9312export fn main() -> Result<Unit, Error> {
9313  let outcome = clock.timeout(60s) {
9314    var i = 0
9315    while i < 1000000000 {
9316      i += 1
9317    }
9318    i
9319  }
9320  Ok(())
9321}
9322"#;
9323        let (run, _, _) = run_traced_under(
9324            source,
9325            Limits {
9326                fuel: Some(10_000),
9327                ..Limits::default()
9328            },
9329        );
9330        assert_eq!(
9331            run.error().message,
9332            "execution stopped: fuel budget of 10000 exhausted"
9333        );
9334    }
9335
9336    /// A host may run its callback as many times as its operation means, and
9337    /// every one of them is a round the run pays for: fuel is charged inside
9338    /// the body exactly as it is charged outside, so a timer cannot outlive
9339    /// the budget by hiding its work behind a host call. The output shows the
9340    /// rounds that were affordable, and the stop names the limit.
9341    #[test]
9342    fn every_round_of_a_repeated_callback_is_charged_to_the_run() {
9343        let source = r#"
9344use clock.every
9345use console.println
9346
9347export fn main() -> Result<Unit, Error> {
9348  let outcome = clock.every(1ms, async fn() {
9349    println("round")?
9350    Ok(())
9351  })
9352  Ok(())
9353}
9354"#;
9355        let (run, _, _) = run_traced_under(
9356            source,
9357            Limits {
9358                fuel: Some(500),
9359                ..Limits::default()
9360            },
9361        );
9362        let rounds = run.output.lines().count();
9363        assert_eq!(
9364            run.error().message,
9365            "execution stopped: fuel budget of 500 exhausted"
9366        );
9367        assert!(
9368            rounds > 1,
9369            "the timer ran more than one round before the budget ran out, but ran {rounds}"
9370        );
9371    }
9372
9373    /// A callback's frames are ordinary Cove frames and count as ordinary
9374    /// Cove frames. The recursion here is the same depth in both runs and the
9375    /// limit is the same; the only difference is the one frame the callback
9376    /// itself adds, and that frame is enough to cross the limit.
9377    #[test]
9378    fn a_callback_s_own_frame_counts_against_the_run_s_call_depth() {
9379        let recursing = r#"
9380fn nest(n: Int) -> Int {
9381  if n <= 0 {
9382    0
9383  } else {
9384    nest(n - 1) + 1
9385  }
9386}
9387"#;
9388        let limits = || Limits {
9389            max_call_depth: Some(6),
9390            ..Limits::default()
9391        };
9392        let direct = format!(
9393            r#"{recursing}
9394export fn main() -> Result<Unit, Error> {{
9395  let answer = nest(4)
9396  Ok(())
9397}}
9398"#
9399        );
9400        run_traced_under(&direct, limits()).0.value();
9401
9402        let through_a_callback = format!(
9403            r#"
9404use clock.timeout
9405{recursing}
9406export fn main() -> Result<Unit, Error> {{
9407  let answer = clock.timeout(60s) {{ nest(4) }}
9408  Ok(())
9409}}
9410"#
9411        );
9412        assert_eq!(
9413            run_traced_under(&through_a_callback, limits())
9414                .0
9415                .error()
9416                .message,
9417            "execution stopped: call-depth limit of 6 exceeded"
9418        );
9419    }
9420
9421    /// A host call made from inside a callback passes the same choke point as
9422    /// any other, so it is charged again. A run allowed one host call is
9423    /// stopped by the `clock.now` its own bounded body makes; a run allowed
9424    /// two is not.
9425    #[test]
9426    fn a_host_call_a_callback_makes_is_charged_against_the_run_again() {
9427        let source = r#"
9428use clock.timeout
9429
9430export fn main() -> Result<Unit, Error> {
9431  let outcome = clock.timeout(60s) { clock.now() }
9432  Ok(())
9433}
9434"#;
9435        let limited = |max_host_calls| Limits {
9436            max_host_calls: Some(max_host_calls),
9437            ..Limits::default()
9438        };
9439        assert_eq!(
9440            run_traced_under(source, limited(1)).0.error().message,
9441            "execution stopped: host-call limit of 1 exceeded"
9442        );
9443        run_traced_under(source, limited(2)).0.value();
9444    }
9445
9446    /// The deadline reaches a callback the way it reaches anything else: the
9447    /// body was inside the deadline when it started and is stopped at its own
9448    /// next safepoint once the deadline has passed. The bound the host itself
9449    /// applies is far longer, so what stops this is the run's deadline and
9450    /// the message says so.
9451    #[test]
9452    fn a_deadline_that_passes_while_a_callback_runs_stops_the_callback() {
9453        let source = r#"
9454use clock.timeout
9455
9456export fn main() -> Result<Unit, Error> {
9457  let outcome = clock.timeout(60s) {
9458    var i = 0
9459    while i < 1000000000 {
9460      i += 1
9461    }
9462    i
9463  }
9464  Ok(())
9465}
9466"#;
9467        let (run, _, elapsed) = run_traced_under(
9468            source,
9469            Limits {
9470                deadline: Some(Duration::from_millis(150)),
9471                ..Limits::default()
9472            },
9473        );
9474        assert_eq!(
9475            run.error().message,
9476            "execution stopped: wall-clock deadline of 150ms exceeded"
9477        );
9478        assert!(
9479            elapsed < Duration::from_secs(60),
9480            "the callback stopped at its own safepoint rather than running to the host's bound, but took {elapsed:?}"
9481        );
9482    }
9483
9484    /// Cancelling the task stops the callback its host call is running, at
9485    /// the callback's own next safepoint. The flag belongs to the task, not
9486    /// to the host call, so the host neither knows nor has to.
9487    #[test]
9488    fn cancelling_a_task_stops_the_callback_it_is_running() {
9489        let source = r#"
9490use clock.timeout
9491use console.println
9492
9493export fn main() -> Result<Unit, Error> {
9494  scope tasks {
9495    let bounded = tasks.spawn {
9496      clock.timeout(60s) {
9497        var i = 0
9498        while i < 1000000000 {
9499          i += 1
9500        }
9501        i
9502      }
9503    }
9504    println("the parent is not waiting")?
9505    bounded.cancel()
9506  }
9507  println("the scope was left")?
9508  Ok(())
9509}
9510"#;
9511        let (run, _, elapsed) = run_traced(source);
9512        assert_eq!(
9513            run.output,
9514            "the parent is not waiting\nthe scope was left\n"
9515        );
9516        run.value();
9517        assert!(
9518            elapsed < Duration::from_secs(60),
9519            "the cancelled callback stopped rather than running to the host's bound, but took {elapsed:?}"
9520        );
9521    }
9522
9523    /// What a trace says about a host call whose callback made another host
9524    /// call, which is worth writing down because it is less than a reader
9525    /// might assume. The two are recorded as siblings, in the order they
9526    /// finished, so the inner one comes first; nothing on either event says
9527    /// one happened inside the other. All that connects them is the outer
9528    /// call's `wait`, which contains the inner call's.
9529    #[test]
9530    fn a_host_call_made_inside_a_callback_is_traced_beside_the_one_that_ran_it() {
9531        let source = r#"
9532use clock.timeout
9533
9534export fn main() -> Result<Unit, Error> {
9535  let outcome = clock.timeout(60s) {
9536    clock.sleep(20ms)
9537    clock.now()
9538  }
9539  Ok(())
9540}
9541"#;
9542        let (run, events, _) = run_traced(source);
9543        run.value();
9544        let calls: Vec<(&str, Duration)> = events
9545            .iter()
9546            .filter_map(|event| match event {
9547                TraceEvent::HostCall { op, wait, .. } => Some((op.as_str(), *wait)),
9548                _ => None,
9549            })
9550            .collect();
9551        assert_eq!(
9552            calls.iter().map(|(op, _)| *op).collect::<Vec<_>>(),
9553            vec!["sleep", "now", "timeout"],
9554            "the calls a callback made are recorded before the call that ran it: {events:?}"
9555        );
9556        let sleep = calls[0].1;
9557        let timeout = calls[2].1;
9558        assert!(
9559            timeout >= sleep,
9560            "the outer call's wait contains the inner call's, but {timeout:?} < {sleep:?}"
9561        );
9562    }
9563
9564    // ---------------------------------------------------------- `Shared`
9565
9566    /// Mutable state of the kind the Language Card says belongs in a
9567    /// `Shared`.
9568    const METRICS: &str = r#"
9569use console.println
9570
9571struct Metrics {
9572  requests: Int
9573  failures: Int
9574}
9575
9576impl Metrics {
9577  /// Records one completed request.
9578  fn record(var self, failed: Bool) {
9579    self.requests += 1
9580    if failed {
9581      self.failures += 1
9582    }
9583  }
9584}
9585"#;
9586
9587    /// Runs `body` inside a `main` with [`METRICS`] in scope.
9588    fn run_shared_body(body: &str) -> Run {
9589        run_entry_of(
9590            &format!(
9591                "{METRICS}\nexport fn main() -> Result<Unit, Error> {{\n{body}\n  Ok(())\n}}\n"
9592            ),
9593            "main",
9594            &[],
9595        )
9596    }
9597
9598    #[test]
9599    fn a_lock_gives_a_var_alias_to_the_wrapped_value() {
9600        let run = run_shared_body(
9601            "  let metrics = Shared(Metrics(requests: 0, failures: 0))\n  metrics.lock(fn(var value) {\n    value.record(true)\n    value.record(false)\n  })\n  metrics.lock(fn(value) {\n    println(\"{value.requests} {value.failures}\")\n  })?",
9602        );
9603        assert_eq!(run.output, "2 1\n");
9604    }
9605
9606    #[test]
9607    fn a_lock_produces_the_value_its_closure_produces() {
9608        let run = run_shared_body(
9609            "  let metrics = Shared(Metrics(requests: 4, failures: 1))\n  let doubled = metrics.lock(fn(var value) {\n    value.requests = value.requests * 2\n    value.requests\n  })\n  println(\"{doubled}\")?",
9610        );
9611        assert_eq!(run.output, "8\n");
9612    }
9613
9614    /// A closure that does not declare `var` receives a copy, exactly as an
9615    /// ordinary parameter does anywhere else in the language: it can read the
9616    /// wrapped value, and the `var self` method that would change it is
9617    /// refused, because a copy is not the place the value lives in.
9618    ///
9619    /// That refusal is `cove check`'s since ADR 0021, so what is left here
9620    /// is that the copy can be read.
9621    #[test]
9622    fn a_lock_closure_without_var_receives_a_read_only_copy() {
9623        let run = run_shared_body(
9624            "  let metrics = Shared(Metrics(requests: 1, failures: 0))\n  metrics.lock(fn(value) {\n    println(\"{value.requests}\")\n  })?",
9625        );
9626        assert_eq!(run.output, "1\n");
9627    }
9628
9629    /// The whole reason the type exists: a `Shared` crosses a task boundary
9630    /// by sharing rather than by copying, so every task sees one value, and
9631    /// `lock` is what keeps their read-modify-writes from racing.
9632    #[test]
9633    fn tasks_share_one_value_through_a_shared() {
9634        let source = format!(
9635            "{METRICS}
9636export fn main() -> Result<Unit, Error> {{
9637  let metrics = Shared(Metrics(requests: 0, failures: 0))
9638  scope requests {{
9639    let first = requests.spawn {{
9640      for i in 0..<100 {{
9641        metrics.lock(fn(var value) {{ value.record(false) }})
9642      }}
9643    }}
9644    let second = requests.spawn {{
9645      for i in 0..<100 {{
9646        metrics.lock(fn(var value) {{ value.record(true) }})
9647      }}
9648    }}
9649    await first
9650    await second
9651  }}
9652  metrics.lock(fn(value) {{
9653    println(\"{{value.requests}} {{value.failures}}\")
9654  }})?
9655  Ok(())
9656}}
9657"
9658        );
9659        let run = run_entry_of(&source, "main", &[]);
9660        assert_eq!(run.output, "200 100\n");
9661    }
9662
9663    #[test]
9664    fn a_shared_refuses_a_payload_that_cannot_cross_a_task_boundary() {
9665        let error = run_shared_body("  let counts = Shared(Vector.of(1, 2))").error();
9666        assert_eq!(
9667            error.message,
9668            "`Shared` cannot wrap a `Vector`, which cannot cross a task boundary"
9669        );
9670        assert!(error
9671            .rule
9672            .unwrap()
9673            .contains("A vector cannot cross, even through `let`"));
9674    }
9675
9676    #[test]
9677    fn a_shared_refuses_a_struct_holding_a_vector() {
9678        let source = r#"
9679struct Draft {
9680  guests: Vector<String>
9681}
9682
9683export fn main() -> Result<Unit, Error> {
9684  let draft = Shared(Draft(guests: Vector.of("Alice")))
9685  Ok(())
9686}
9687"#;
9688        let error = run_entry_of(source, "main", &[]).error();
9689        assert_eq!(
9690            error.message,
9691            "`Shared` cannot wrap a `Vector` in `guests`, which cannot cross a task boundary"
9692        );
9693    }
9694
9695    /// A `lock` inside a `lock` on the same value can never be granted, so
9696    /// the runtime says so rather than waiting for itself for ever.
9697    #[test]
9698    fn a_reentrant_lock_is_reported_rather_than_deadlocking() {
9699        let error = run_shared_body(
9700            "  let metrics = Shared(Metrics(requests: 0, failures: 0))\n  metrics.lock(fn(var value) {\n    metrics.lock(fn(var inner) {\n      inner.record(false)\n    })\n  })",
9701        )
9702        .error();
9703        assert_eq!(
9704            error.message,
9705            "this task already holds this `Shared`, so `lock` would wait for itself"
9706        );
9707        assert!(error.help.unwrap().contains("one `lock`"));
9708    }
9709
9710    /// Two different `Shared` values are two different locks, so holding one
9711    /// while taking the other is ordinary nesting rather than a deadlock.
9712    #[test]
9713    fn a_lock_inside_a_lock_on_another_shared_is_allowed() {
9714        let run = run_shared_body(
9715            "  let left = Shared(Metrics(requests: 1, failures: 0))\n  let right = Shared(Metrics(requests: 2, failures: 0))\n  let total = left.lock(fn(value) {\n    right.lock(fn(other) {\n      value.requests + other.requests\n    })\n  })\n  println(\"{total}\")?",
9716        );
9717        assert_eq!(run.output, "3\n");
9718    }
9719
9720    /// ADR 0011's amendment: nothing reclaims an `Arc` cycle among `Shared`
9721    /// cells, so `lock` rejects the one shape of that cycle it can see for
9722    /// free — a cell ending up holding a handle to itself — rather than
9723    /// leaving it to leak silently. This is the ADR's own example.
9724    #[test]
9725    fn a_lock_refuses_a_closure_that_stores_a_handle_to_its_own_cell() {
9726        let source = r#"
9727struct Node {
9728  cell: Option<Shared<Node>>
9729}
9730
9731export fn main() -> Result<Unit, Error> {
9732  let n = Shared(Node(cell: None))
9733  n.lock(fn(var value) {
9734    value = Node(cell: Some(n))
9735  })
9736  Ok(())
9737}
9738"#;
9739        let error = run_entry_of(source, "main", &[]).error();
9740        assert_eq!(
9741            error.message,
9742            "this `lock` would leave the cell holding a handle to itself, and no collector reclaims that cycle"
9743        );
9744        assert!(error
9745            .rule
9746            .unwrap()
9747            .contains("`Shared` ownership must stay acyclic"));
9748    }
9749
9750    /// The check only catches a cell reaching *itself*: a cell that ends up
9751    /// holding a handle to a *different* cell is an ordinary, permitted
9752    /// `Shared` graph, not the direct cycle `lock` refuses.
9753    #[test]
9754    fn a_lock_allows_a_closure_that_stores_a_handle_to_a_different_cell() {
9755        let source = r#"
9756struct Node {
9757  cell: Option<Shared<Node>>
9758}
9759
9760export fn main() -> Result<Unit, Error> {
9761  let a = Shared(Node(cell: None))
9762  let b = Shared(Node(cell: None))
9763  b.lock(fn(var value) {
9764    value = Node(cell: Some(a))
9765  })
9766  Ok(())
9767}
9768"#;
9769        let run = run_entry_of(source, "main", &[]);
9770        assert!(run.value.is_ok());
9771    }
9772
9773    #[test]
9774    fn a_shared_has_no_operation_but_lock() {
9775        let error =
9776            run_shared_body("  let metrics = Shared(Metrics(requests: 0, failures: 0))\n  let value = metrics.get()")
9777                .error();
9778        assert_eq!(error.message, "`Shared` has no method `get`");
9779        assert!(error
9780            .rule
9781            .unwrap()
9782            .contains("there is no `get` and no `set`"));
9783    }
9784
9785    // ------------------------------------------------- acceptance tests
9786
9787    fn examples_root() -> PathBuf {
9788        Path::new(env!("CARGO_MANIFEST_DIR")).join("../../examples")
9789    }
9790
9791    /// Loads the repository's real `examples/` package.
9792    fn examples_program() -> (Arc<SourceMap>, Arc<Program>) {
9793        let root = examples_root();
9794        let mut sources = SourceMap::new();
9795        let package = cove_sema::package::load(&root, &mut sources).expect("examples load");
9796        let program = cove_sema::resolve::resolve(&package).expect("examples resolve");
9797        (Arc::new(sources), Arc::new(program))
9798    }
9799
9800    #[test]
9801    fn runs_the_hello_example() {
9802        let (sources, program) = examples_program();
9803        let default = run_in(
9804            &program,
9805            &sources,
9806            "hello",
9807            "main",
9808            &[],
9809            &["console"],
9810            BTreeMap::new(),
9811        );
9812        assert_eq!(default.output, "Hello, world!\n");
9813        assert_eq!(default.value().to_string(), "Ok(())");
9814
9815        let named = run_in(
9816            &program,
9817            &sources,
9818            "hello",
9819            "main",
9820            &["Cove"],
9821            &["console"],
9822            BTreeMap::new(),
9823        );
9824        assert_eq!(named.output, "Hello, Cove!\n");
9825    }
9826
9827    #[test]
9828    fn runs_the_values_example() {
9829        let (sources, program) = examples_program();
9830        let run = run_in(
9831            &program,
9832            &sources,
9833            "values",
9834            "main",
9835            &[],
9836            &["console"],
9837            BTreeMap::new(),
9838        );
9839        assert_eq!(run.output, "Pending\nConfirmed\n2\n2\n2\n1\n");
9840        assert_eq!(run.value().to_string(), "Ok(())");
9841    }
9842
9843    #[test]
9844    fn runs_the_config_example() {
9845        let (sources, program) = examples_program();
9846
9847        let loaded = run_in(
9848            &program,
9849            &sources,
9850            "config",
9851            "loadConfig",
9852            &[],
9853            &["env"],
9854            BTreeMap::from([
9855                ("PORT".to_string(), "9000".to_string()),
9856                ("LOG_LEVEL".to_string(), "debug".to_string()),
9857            ]),
9858        );
9859        assert_eq!(
9860            loaded.value().to_string(),
9861            "Ok(Config(port: 9000, logLevel: Debug))"
9862        );
9863
9864        let defaulted = run_in(
9865            &program,
9866            &sources,
9867            "config",
9868            "loadConfig",
9869            &[],
9870            &["env"],
9871            BTreeMap::new(),
9872        );
9873        assert_eq!(
9874            defaulted.value().to_string(),
9875            "Ok(Config(port: 8080, logLevel: Info))"
9876        );
9877
9878        let rejected = run_in(
9879            &program,
9880            &sources,
9881            "config",
9882            "loadConfig",
9883            &[],
9884            &["env"],
9885            BTreeMap::from([("LOG_LEVEL".to_string(), "verbose".to_string())]),
9886        );
9887        assert_eq!(
9888            rejected.value().to_string(),
9889            "Err(InvalidLogLevel(verbose))"
9890        );
9891
9892        let invalid_port = run_in(
9893            &program,
9894            &sources,
9895            "config",
9896            "loadConfig",
9897            &[],
9898            &["env"],
9899            BTreeMap::from([("PORT".to_string(), "eighty".to_string())]),
9900        );
9901        assert_eq!(invalid_port.value().to_string(), "Err(InvalidPort(eighty))");
9902    }
9903
9904    #[test]
9905    fn runs_the_restricted_example() {
9906        let (sources, program) = examples_program();
9907
9908        let buffer = Buffer::default();
9909        let mut hosts = HostRegistry::new(Grants::new(["documents", "console"]));
9910        hosts.register(Box::new(Console::new(buffer.clone(), Buffer::default())));
9911        hosts.register(Box::new(Documents::rooted(
9912            examples_root().join("documents"),
9913        )));
9914        let runtime = Runtime::new(program, sources, Arc::new(hosts));
9915        let value = Interpreter::new(&runtime)
9916            .run_entry("restricted", "main", Vec::new())
9917            .expect("the program ran without a runtime error");
9918
9919        assert_eq!(buffer.text(), "5 words\n");
9920        assert_eq!(value.to_string(), "Ok(())");
9921    }
9922
9923    // ------------------------------------------------- garbage collection
9924
9925    /// A sink that keeps every event, so a test can assert on what a run
9926    /// recorded rather than on how it was formatted.
9927    ///
9928    /// Task threads record through the same sink as the entry, so this is
9929    /// shared and locked exactly as the real ones are.
9930    #[derive(Clone, Default)]
9931    struct Recorder(Arc<Mutex<Vec<TraceEvent>>>);
9932
9933    impl TraceSink for Recorder {
9934        fn record(&self, event: TraceEvent) {
9935            self.0
9936                .lock()
9937                .expect("no test panics while tracing")
9938                .push(event);
9939        }
9940    }
9941
9942    impl Recorder {
9943        fn events(&self) -> Vec<TraceEvent> {
9944            self.0.lock().expect("no test panics while tracing").clone()
9945        }
9946    }
9947
9948    /// One run, together with what its heaps did.
9949    struct HeapRun {
9950        value: Result<Value, RuntimeError>,
9951        output: String,
9952        events: Vec<TraceEvent>,
9953        stats: HeapStats,
9954    }
9955
9956    impl HeapRun {
9957        /// Every collection the run recorded, as `(task, allocated, freed)`.
9958        fn collections(&self) -> Vec<(u64, u64, u64)> {
9959            self.events
9960                .iter()
9961                .filter_map(|event| match event {
9962                    TraceEvent::HeapCollected {
9963                        task,
9964                        allocated,
9965                        freed,
9966                        ..
9967                    } => Some((*task, *allocated, *freed)),
9968                    _ => None,
9969                })
9970                .collect()
9971        }
9972
9973        /// The run's `heap_summary`, which is always its last heap event.
9974        fn summary(&self) -> HeapStats {
9975            self.events
9976                .iter()
9977                .rev()
9978                .find_map(|event| match event {
9979                    // Every object figure is `Some` here because this is the
9980                    // interpreter's own summary and the interpreter counts
9981                    // objects; a `None` would be a machine that does not, and
9982                    // this test would rather fail than read it as a zero.
9983                    TraceEvent::HeapSummary {
9984                        collections,
9985                        object_count,
9986                        allocated_bytes,
9987                        live_bytes,
9988                        peak_bytes,
9989                        pause,
9990                        ..
9991                    } => Some(HeapStats {
9992                        allocated_objects: object_count.expect("the interpreter counts objects"),
9993                        allocated_bytes: allocated_bytes.expect("the interpreter counts bytes"),
9994                        collections: *collections,
9995                        freed_objects: 0,
9996                        live_bytes: live_bytes.expect("the interpreter counts bytes"),
9997                        live_objects: 0,
9998                        peak_bytes: peak_bytes.expect("the interpreter counts bytes"),
9999                        pause: pause.expect("the interpreter times its collections"),
10000                    }),
10001                    _ => None,
10002                })
10003                .expect("a run ends with a heap summary")
10004        }
10005    }
10006
10007    /// Runs `source`'s `test.main` under `limits`, watching every heap.
10008    fn run_watching_the_heap(source: &str, limits: crate::budget::Limits) -> HeapRun {
10009        let (sources, program) = program_of(source);
10010        let buffer = Buffer::default();
10011        let mut hosts = HostRegistry::new(Grants::new(["console"]));
10012        hosts.register(Box::new(Console::new(buffer.clone(), Buffer::default())));
10013        hosts.set_budget(crate::budget::Budget::new(limits));
10014        let recorder = Recorder::default();
10015        let runtime =
10016            Runtime::new(program, sources, Arc::new(hosts)).with_trace(Arc::new(recorder.clone()));
10017        let mut interpreter = Interpreter::new(&runtime);
10018        let value = interpreter.run_entry("test", "main", Vec::new());
10019        let stats = interpreter.heap_stats();
10020        HeapRun {
10021            value,
10022            output: buffer.text(),
10023            events: recorder.events(),
10024            stats,
10025        }
10026    }
10027
10028    /// Runs `body` inside `test.main`, watching every heap.
10029    fn run_collecting(body: &str) -> HeapRun {
10030        let source = format!(
10031            "use console.println\n\nexport fn main() -> Result<Unit, Error> {{\n{body}\n  Ok(())\n}}\n"
10032        );
10033        run_watching_the_heap(&source, crate::budget::Limits::default())
10034    }
10035
10036    /// Enough abandoned objects for a heap to have collected several times.
10037    const CHURN: usize = 200;
10038
10039    /// A loop body that builds one cycle and abandons it.
10040    fn churn(count: usize) -> String {
10041        format!(
10042            "  var i = 0\n  while i < {count} {{\n    var v = Vector.of()\n    v.push(v)\n    i += 1\n  }}\n"
10043        )
10044    }
10045
10046    /// The whole reason for the collector. `Rc` cannot free a vector that
10047    /// holds itself, so without a mark and a sweep every one of these would
10048    /// still be live at the end of the run.
10049    #[test]
10050    fn a_cycle_through_a_vector_element_is_reclaimed() {
10051        let run = run_collecting(&churn(CHURN));
10052        run.value.as_ref().expect("the program ran");
10053        assert!(
10054            run.summary().allocated_objects >= CHURN as u64,
10055            "{:?}",
10056            run.summary()
10057        );
10058        assert!(
10059            run.collections().iter().any(|(_, _, freed)| *freed > 0),
10060            "nothing was reclaimed: {:?}",
10061            run.collections()
10062        );
10063        assert_eq!(run.stats.live_objects, 0, "{:?}", run.stats);
10064    }
10065
10066    #[test]
10067    fn a_cycle_through_a_struct_field_is_reclaimed() {
10068        let run = run_watching_the_heap(
10069            &format!(
10070                "struct Node(next: Vector<Node>)\n\nexport fn main() -> Result<Unit, Error> {{\n  var i = 0\n  while i < {CHURN} {{\n    var v: Vector<Node> = Vector.of()\n    v.push(Node(next: v))\n    i += 1\n  }}\n  Ok(())\n}}\n"
10071            ),
10072            crate::budget::Limits::default(),
10073        );
10074        run.value.as_ref().expect("the program ran");
10075        assert!(
10076            run.collections().iter().any(|(_, _, freed)| *freed > 0),
10077            "{:?}",
10078            run.collections()
10079        );
10080        assert_eq!(run.stats.live_objects, 0, "{:?}", run.stats);
10081    }
10082
10083    /// A closure captures by value, so a vector holding a closure that
10084    /// captured that vector is a cycle whose back edge is a capture.
10085    #[test]
10086    fn a_cycle_through_a_closure_capture_is_reclaimed() {
10087        let run = run_collecting(&format!(
10088            "  var i = 0\n  while i < {CHURN} {{\n    var v: Vector<fn() -> Int> = Vector.of()\n    let f = fn() {{\n      v.length()\n    }}\n    v.push(f)\n    i += 1\n  }}\n"
10089        ));
10090        run.value.as_ref().expect("the program ran");
10091        assert!(
10092            run.collections().iter().any(|(_, _, freed)| *freed > 0),
10093            "{:?}",
10094            run.collections()
10095        );
10096        assert_eq!(run.stats.live_objects, 0, "{:?}", run.stats);
10097    }
10098
10099    /// The roots are the environment chain, so a binding's value survives
10100    /// however many collections run while it is in scope — including the
10101    /// elements it was holding.
10102    #[test]
10103    fn a_value_the_environment_chain_holds_is_not_collected() {
10104        let run = run_collecting(&format!(
10105            "  var kept = Vector.of(1, 2, 3)\n{}  println(\"kept {{kept.length()}} {{kept}}\")?\n",
10106            churn(CHURN)
10107        ));
10108        run.value.as_ref().expect("the program ran");
10109        assert_eq!(run.output, "kept 3 [1, 2, 3]\n");
10110        assert!(run.collections().iter().any(|(_, _, freed)| *freed > 0));
10111    }
10112
10113    /// A binding is a root only while it is in scope: the same vector that
10114    /// survived above is reclaimed once the block that named it is left.
10115    #[test]
10116    fn a_value_whose_binding_has_gone_out_of_scope_is_collected() {
10117        let run = run_collecting(&format!(
10118            "  {{\n    var doomed = Vector.of()\n    doomed.push(doomed)\n  }}\n{}",
10119            churn(CHURN)
10120        ));
10121        run.value.as_ref().expect("the program ran");
10122        assert_eq!(
10123            run.stats.live_objects, 0,
10124            "the block's vector outlived its block: {:?}",
10125            run.stats
10126        );
10127    }
10128
10129    /// A task collects the heap of its own thread, and the event says whose
10130    /// it was.
10131    #[test]
10132    fn a_task_collects_its_own_heap() {
10133        let run = run_watching_the_heap(
10134            &format!(
10135                "fn work() -> Int {{\n{}  i\n}}\n\nexport fn main() -> Result<Unit, Error> {{\n  scope tasks {{\n    let one = tasks.spawn {{ work() }}\n    let done = one.await()\n    done\n  }}\n  Ok(())\n}}\n",
10136                churn(CHURN)
10137            ),
10138            crate::budget::Limits::default(),
10139        );
10140        run.value.as_ref().expect("the program ran");
10141        assert!(
10142            run.collections()
10143                .iter()
10144                .any(|(task, _, freed)| *task != ENTRY_TASK && *freed > 0),
10145            "no collection ran inside the task: {:?}",
10146            run.collections()
10147        );
10148    }
10149
10150    /// ADR 0011's per-task heap, on ADR 0008's threads: two tasks running at
10151    /// the same time each collect their own objects, and neither disturbs
10152    /// what the other is holding.
10153    ///
10154    /// The two are held at a barrier until both have arrived, so they are
10155    /// provably churning at the same time rather than merely both having run.
10156    /// Each then builds a vector only it can reach, churns through enough
10157    /// cycles to be collected several times, and reads its own vector back. A
10158    /// collection that reached across the boundary would empty one of them.
10159    ///
10160    /// The barrier's spin is bounded so that a runtime which never lets both
10161    /// tasks arrive fails this test rather than hanging it.
10162    #[test]
10163    fn two_tasks_collect_at_the_same_time_without_disturbing_each_other() {
10164        let run = run_watching_the_heap(
10165            &format!(
10166                "use console.println\n\nfn work(gate: Shared<Int>, mark: Int) -> Int {{\n  var kept = Vector.of(mark, mark, mark)\n  gate.lock(fn(var arrived) {{\n    arrived += 1\n  }})\n  var both = 0\n  var spins = 0\n  while both < 2 && spins < 100000000 {{\n    both = gate.lock(fn(arrived) {{\n      arrived\n    }})\n    spins += 1\n  }}\n{}  kept.length() * 1000 + both * 100 + mark\n}}\n\nexport fn main() -> Result<Unit, Error> {{\n  let gate = Shared(0)\n  scope tasks {{\n    let one = tasks.spawn {{ work(gate, 1) }}\n    let two = tasks.spawn {{ work(gate, 2) }}\n    println(\"{{one.await()}} {{two.await()}}\")?\n  }}\n  Ok(())\n}}\n",
10167                churn(CHURN)
10168            ),
10169            crate::budget::Limits::default(),
10170        );
10171        run.value.as_ref().expect("the program ran");
10172        // `3201` and `3202`: each task kept its own three-element vector, both
10173        // saw the barrier open, and each saw the mark it was given. A
10174        // collection that reached across the boundary would have emptied one
10175        // of those vectors.
10176        assert_eq!(run.output, "3201 3202\n");
10177
10178        let collected: BTreeSet<u64> = run
10179            .collections()
10180            .into_iter()
10181            .filter(|(_, _, freed)| *freed > 0)
10182            .map(|(task, _, _)| task)
10183            .collect();
10184        assert!(
10185            collected.contains(&1) && collected.contains(&2),
10186            "both tasks should have collected: {collected:?}"
10187        );
10188    }
10189
10190    /// A heap dies with the thread that owns it, and dropping a table of
10191    /// `Weak`s takes nothing with it — so a task that ends while a cycle it
10192    /// built is still in scope would leave that cycle behind. Retiring a heap
10193    /// sweeps it one last time, which is what makes a task's memory a task's
10194    /// to give back.
10195    #[test]
10196    fn a_task_that_ends_still_naming_a_cycle_leaves_nothing_behind() {
10197        let run = run_watching_the_heap(
10198            &format!(
10199                "struct Node(next: Vector<Node>)\n\nfn holds() -> Int {{\n  var kept: Vector<Node> = Vector.of()\n  kept.push(Node(next: kept))\n{}  kept.length()\n}}\n\nexport fn main() -> Result<Unit, Error> {{\n  scope tasks {{\n    let one = tasks.spawn {{ holds() }}\n    let done = one.await()\n    done\n  }}\n  Ok(())\n}}\n",
10200                churn(CHURN)
10201            ),
10202            crate::budget::Limits::default(),
10203        );
10204        run.value.as_ref().expect("the program ran");
10205        let summary = run.summary();
10206        // Every object the task allocated, including the cycle it was still
10207        // naming when it ended, was reclaimed.
10208        let freed: u64 = run.collections().iter().map(|(_, _, freed)| freed).sum();
10209        assert_eq!(
10210            freed, summary.allocated_objects,
10211            "a cycle outlived the task that built it: {summary:?}"
10212        );
10213    }
10214
10215    /// A value crossing a task boundary is copied, so the copy the task runs
10216    /// on is its own and the original stays behind — where the sending task's
10217    /// heap reclaims it like anything else it stopped naming.
10218    #[test]
10219    fn a_value_transferred_into_a_task_leaves_the_original_to_the_sender() {
10220        let run = run_watching_the_heap(
10221            &format!(
10222                "use console.println\n\nfn sum(items: Array<Int>) -> Int {{\n  var total = 0\n  for item in items {{\n    total += item\n  }}\n  total\n}}\n\nexport fn main() -> Result<Unit, Error> {{\n  let crossed = {{\n    var building = Vector.of(1, 2, 3)\n    building.toArray()\n  }}\n  scope tasks {{\n    let one = tasks.spawn {{ sum(crossed) }}\n    println(\"{{one.await()}}\")?\n  }}\n{}  Ok(())\n}}\n",
10223                churn(CHURN)
10224            ),
10225            crate::budget::Limits::default(),
10226        );
10227        run.value.as_ref().expect("the program ran");
10228        assert_eq!(run.output, "6\n");
10229        // The vector the array was built from was named only inside the block
10230        // it was built in; nothing crossed but the array's copy, so the
10231        // entry's heap has nothing left.
10232        assert_eq!(run.stats.live_objects, 0, "{:?}", run.stats);
10233    }
10234
10235    /// A `Shared`'s contents belong to the cell, not to any task's heap, and
10236    /// a collection never takes the cell's lock — which it could not, since
10237    /// `lock` holds it for the whole of a closure that reaches safepoints.
10238    #[test]
10239    fn a_collection_inside_a_lock_neither_waits_nor_loses_the_cell_s_contents() {
10240        let run = run_watching_the_heap(
10241            &format!(
10242                "use console.println\n\nexport fn main() -> Result<Unit, Error> {{\n  let total = Shared(0)\n  scope tasks {{\n    let one = tasks.spawn {{ bump(total) }}\n    let two = tasks.spawn {{ bump(total) }}\n    let first = one.await()\n    let second = two.await()\n    first + second\n  }}\n  total.lock(fn(value) {{\n    println(\"total {{value}}\")\n  }})?\n  Ok(())\n}}\n\nfn bump(total: Shared<Int>) -> Int {{\n  total.lock(fn(var value) {{\n{}    value += 1\n    value\n  }})\n}}\n",
10243                churn(CHURN)
10244            ),
10245            crate::budget::Limits::default(),
10246        );
10247        run.value.as_ref().expect("the program ran");
10248        assert_eq!(run.output, "total 2\n");
10249        // The churn happened while each task held the lock, so collections ran
10250        // inside `lock` without waiting for it.
10251        assert!(
10252            run.collections()
10253                .iter()
10254                .any(|(task, _, freed)| *task != ENTRY_TASK && *freed > 0),
10255            "{:?}",
10256            run.collections()
10257        );
10258    }
10259
10260    /// ADR 0011 asks allocation, live heap size, collection count, and pause
10261    /// time to be trace events. This is the run that produces all four.
10262    #[test]
10263    fn the_trace_carries_allocation_the_live_heap_collections_and_pause() {
10264        let run = run_collecting(&format!("  var kept = Vector.of(1)\n{}", churn(CHURN)));
10265        run.value.as_ref().expect("the program ran");
10266
10267        let collections = run.collections();
10268        assert!(!collections.is_empty(), "no collection was recorded");
10269        for (_, allocated, _) in &collections {
10270            assert!(*allocated > 0, "a collection recorded no allocation");
10271        }
10272
10273        let summary = run.summary();
10274        assert_eq!(summary.allocated_objects, CHURN as u64 + 1);
10275        assert!(summary.allocated_bytes > 0);
10276        assert_eq!(summary.collections, collections.len() as u64);
10277        // The summary's live figure is what the run ended holding, which is
10278        // nothing: the entry's own bindings went with it, and retiring its
10279        // heap swept them. What `kept` was worth shows in the peak, and in the
10280        // collections that ran while it was still named.
10281        assert_eq!(summary.live_bytes, 0);
10282        assert!(summary.peak_bytes > 0, "the kept vector was live");
10283        let live_while_running: Vec<u64> = run
10284            .events
10285            .iter()
10286            .filter_map(|event| match event {
10287                TraceEvent::HeapCollected { live_bytes, .. } => Some(*live_bytes),
10288                _ => None,
10289            })
10290            .collect();
10291        assert!(
10292            live_while_running.iter().any(|bytes| *bytes > 0),
10293            "no collection saw the kept vector: {live_while_running:?}"
10294        );
10295        assert!(
10296            summary.pause > Duration::ZERO,
10297            "a collection took no time at all"
10298        );
10299    }
10300
10301    /// A program that allocates nothing collectable pays for no collection at
10302    /// all: the heap a task starts with is a table and two counters.
10303    #[test]
10304    fn a_program_that_allocates_nothing_is_never_collected() {
10305        let run = run_collecting("  println(\"{1 + 1}\")?\n");
10306        run.value.as_ref().expect("the program ran");
10307        assert_eq!(run.output, "2\n");
10308        assert_eq!(run.summary().collections, 0);
10309        assert_eq!(run.summary().allocated_objects, 0);
10310        assert!(run.collections().is_empty());
10311    }
10312}