Skip to main content

cove_runtime/
builtins.rs

1//! Builtin methods, associated functions, and constructors.
2//!
3//! Everything here is dispatched dynamically on a receiver value, a type name,
4//! or a constructor name. The MVP has no method table derived from types yet,
5//! so an arity or type mismatch is an ordinary [`RuntimeError`] that names the
6//! method it came from.
7//!
8//! What each builtin *is* — its parameters, its result, and whether its
9//! receiver is `var self` — is [`cove_schema::builtins`], two tables below
10//! both this crate and the compiler: one for what is called on a receiver and
11//! one for the constructors and assertions, which are called on nothing. This
12//! module is the other half: the bodies, which have to be here because a body
13//! reaches into a [`Value`] and `cove-schema` has no values. Every question a
14//! name alone can answer is asked of the schema rather than answered twice —
15//! which names are namespaces, which methods mutate, which names construct,
16//! which assert, how many arguments each takes, and which receivers report a
17//! `length` — and `tests/builtin_schema.rs` drives every entry in both tables
18//! through a real interpreter, so a signature declared with no body behind it
19//! fails a test rather than a program.
20
21use std::collections::{BTreeMap, BTreeSet};
22use std::rc::Rc;
23
24use cove_diag::Span;
25use cove_schema::builtins::{FreeBuiltinKind, FreeBuiltinSchema, MAP_ENTRY};
26
27use crate::error::RuntimeError;
28use crate::shared::SharedCell;
29use crate::value::{InvalidKey, MapKey, RangeBounds, Repr, Value, VectorStorage};
30
31/// Type names a program may write as a namespace, such as `Vector.of`.
32///
33/// This is [`cove_schema::builtins::is_builtin_type`], re-exported so that
34/// the interpreter still asks one module about builtins.
35pub use cove_schema::builtins::is_builtin_type;
36
37/// The methods that take a `var self` receiver and therefore need a mutable
38/// place at the call site.
39///
40/// This is [`cove_schema::builtins::is_mutating_method`]: `push`, `set`,
41/// `pop`, `remove` and `freeze` declare `mutating` in the shared table, and
42/// nothing here restates them.
43pub use cove_schema::builtins::is_mutating_method;
44
45/// How the builtins call back into the evaluator.
46///
47/// Higher-order builtins such as `Result.mapError` invoke a Cove callback, so
48/// they need the interpreter that owns the call stack.
49pub trait Callable {
50    /// Allocates growable vector storage in the running task's heap.
51    ///
52    /// Every `Vector` a program can reach is created through this, so the
53    /// collector's table of objects is the complete set of values that can
54    /// form a cycle. A builtin that makes one asks its caller rather than
55    /// calling [`VectorStorage::new`] directly.
56    fn allocate_vector(&mut self, elements: Vec<Value>) -> Value;
57
58    /// Calls a closure value with already evaluated arguments.
59    ///
60    /// **The arguments are taken out of `args`, which is left empty.** The
61    /// vector belongs to the caller and comes back to it, capacity and all,
62    /// which is what lets a per-element callback be invoked without
63    /// allocating a vector per element: `walk_with` hands the same one
64    /// down for the whole walk. Issue #193 is the cost that made that worth
65    /// arranging — `map`, `filter`, `fold` and `sorted` built and dropped a
66    /// `Vec<Value>` for every element they visited, which is the same shape
67    /// the predecessor's own argument vectors had before #184 and on the
68    /// one path that scheme could not reach.
69    ///
70    /// A caller that fails partway is still handed back a vector it may
71    /// reuse: an implementation drains what it was given before it runs
72    /// anything, so `args` is empty whether the call answered or raised.
73    fn call_value(
74        &mut self,
75        callee: &Value,
76        args: &mut Vec<Value>,
77        span: Span,
78    ) -> Result<Value, RuntimeError>;
79
80    /// The number of parameters `callee` declares, when it is a closure.
81    fn arity(&self, callee: &Value) -> Option<usize>;
82
83    /// The independent copy `Snapshot` makes of one value.
84    ///
85    /// A hook on this trait, whose one implementor is `Interpreter`, because
86    /// a struct and an enum answer their own `impl Snapshot for Type`
87    /// through a declaration that only the interpreter reaches this way. The
88    /// linear-memory backend puts the same recursion in the lowering
89    /// instead, exactly because a builtin never calls back into Cove —
90    /// `docs/LINEAR_VM.md` says why. [`snapshot`] recurses through here so
91    /// that a `Vector` of structs reaches the interpreter's own answer for
92    /// each one.
93    fn snapshot(&mut self, value: &Value, span: Span) -> Result<Value, RuntimeError>;
94}
95
96/// The independent copy `Snapshot` makes of a value that no declared
97/// conformance answers for.
98///
99/// The Language Reference makes an independent copy an explicit `impl
100/// Snapshot for Type`, and this is everything that decision leaves over: a
101/// value with nothing mutable inside it returns itself, because a copy of it
102/// is not observable, and a `Vector` — the one thing a copy is observable of
103/// — allocates storage of its own and snapshots what it held.
104///
105/// An `Array`, a `Map` and a `Set` are cloned rather than walked, which is
106/// `Interpreter::snapshot`'s own answer and not a shortcut taken here: each
107/// is immutable, so an element that shares storage with something else went
108/// on sharing it before this was called and there is nothing for a copy to
109/// separate.
110///
111/// A struct, an enum and a `dyn` are not here. They are what the caller
112/// answers, through [`Callable::snapshot`].
113pub fn snapshot(
114    callable: &mut dyn Callable,
115    value: &Value,
116    span: Span,
117) -> Result<Value, RuntimeError> {
118    match value {
119        Value(Repr::Unit)
120        | Value(Repr::Bool(_))
121        | Value(Repr::Int(_))
122        | Value(Repr::Float(_))
123        | Value(Repr::Duration(_))
124        | Value(Repr::Str(_))
125        | Value(Repr::Array(_))
126        | Value(Repr::Map(_))
127        | Value(Repr::Set(_))
128        | Value(Repr::Range { .. }) => Ok(value.clone()),
129        Value(Repr::Vector(storage)) => {
130            check_live(storage, "snapshot", span)?;
131            let elements = storage.elements.borrow().clone();
132            let mut snapshotted = Vec::with_capacity(elements.len());
133            for item in &elements {
134                snapshotted.push(callable.snapshot(item, span)?);
135            }
136            Ok(callable.allocate_vector(snapshotted))
137        }
138        other => Err(no_snapshot_conformance(other, span)),
139    }
140}
141
142/// What a `...` argument that is neither an `Array` nor a `Vector` is
143/// refused with.
144///
145/// A spread passes an existing sequence where a variadic parameter's
146/// elements would go, so the two sequences are what it reads; `bind_params`
147/// reports anything else, and the linear-memory backend reports it from the
148/// instruction that does the appending. One wording, because it is one
149/// failure.
150pub fn spread_needs_a_sequence(span: Span) -> RuntimeError {
151    RuntimeError::new("`...` spreads an `Array` or a `Vector`").at(span)
152}
153
154/// What a value that implements no `Snapshot` conformance is refused with.
155///
156/// Only the interpreter reaches it: a struct or an enum whose type wrote
157/// none, met while walking a `Vector` whose element type is not known until
158/// the value is. The linear-memory backend has no runtime version of this
159/// question — a `Vector`'s element type is part of its layout, so whether it
160/// snapshots itself or calls a conformance is decided when the walk is
161/// lowered, not when it runs.
162pub fn no_snapshot_conformance(value: &Value, span: Span) -> RuntimeError {
163    RuntimeError::new(format!(
164        "`{}` does not implement `Snapshot`",
165        value.type_name()
166    ))
167    .at(span)
168    .with_rule(
169        "Closures, synchronized values, and Host resources do not implement `Snapshot` by default; a struct or enum conforms explicitly with `impl Snapshot for Type`.",
170    )
171}
172
173/// The builtins that are called on nothing: the constructors `Ok`, `Err`,
174/// `Some`, `Error`, and `Shared`, and the assertions `assert` and
175/// `assertEqual`.
176///
177/// This is [`cove_schema::builtins::free_builtin`], re-exported so that the
178/// interpreter still asks one module about builtins. Which of the two kinds
179/// an entry is decides which path a call is dispatched through, and how many
180/// arguments it declares is what that call is held to.
181pub use cove_schema::builtins::free_builtin;
182
183/// `assert(condition: Bool) -> Result<Unit, Error>` and
184/// `assertEqual(actual: T, expected: T) -> Result<Unit, Error>`.
185///
186/// `sources` holds the source text of each argument expression, in order.
187/// That text is the whole reason these are builtins rather than a library:
188/// a failure message says which condition failed in the words the test was
189/// written in, and only the compiler has them.
190///
191/// A failing assertion is an expected failure, so it is an `Err` rather than
192/// a panic — panics stay reserved for broken invariants. `assertEqual`
193/// reports both values, since knowing only that they differ rarely explains
194/// why.
195///
196/// How many arguments each takes and what each one is called are the shared
197/// table's, so the arity this enforces is the arity `cove check` reported on.
198pub fn call_assertion(
199    name: &str,
200    args: &mut Vec<Value>,
201    sources: &[&str],
202    span: Span,
203) -> Result<Value, RuntimeError> {
204    let Some(schema) =
205        free_builtin(name).filter(|schema| schema.kind == FreeBuiltinKind::Assertion)
206    else {
207        return Err(RuntimeError::new(format!("unknown assertion `{name}`")).at(span));
208    };
209    let args = expect_args(name, args, schema.arity(), span)?;
210    match name {
211        "assert" => {
212            let Value(Repr::Bool(holds)) = &args[0] else {
213                return Err(declared_type_error(schema, 0, &args[0], span));
214            };
215            if *holds {
216                return Ok(Value::ok(Value(Repr::Unit)));
217            }
218            Ok(assertion_failure(format!(
219                "assertion failed: `{}`",
220                source_of(sources, 0)
221            )))
222        }
223        "assertEqual" => {
224            // `assertEqual` compares the way `==` does, so it refuses the
225            // same comparison `==` refuses. The shared table says as much by
226            // naming one type parameter twice.
227            if args[0].type_name() != args[1].type_name() {
228                return Err(RuntimeError::new(format!(
229                    "`assertEqual` cannot compare `{}` with `{}`",
230                    args[0].type_name(),
231                    args[1].type_name()
232                ))
233                .at(span)
234                .with_rule("`==` means value equality between values of the same type."));
235            }
236            if args[0].eq_value(&args[1]) {
237                return Ok(Value::ok(Value(Repr::Unit)));
238            }
239            Ok(assertion_failure(format!(
240                "assertion failed: `{}` is `{}`, expected `{}`",
241                source_of(sources, 0),
242                args[0],
243                args[1]
244            )))
245        }
246        // The table admitted the name, so this is a table entry with no body
247        // behind it, which `tests/builtin_schema.rs` is what catches.
248        _ => Err(RuntimeError::new(format!("unknown assertion `{name}`")).at(span)),
249    }
250}
251
252/// A free builtin was given an argument its declared parameter does not
253/// admit.
254///
255/// The parameter's name and type are read out of the shared table, so
256/// `Error("boom")` and `assert(1)` are refused in the words the table
257/// declares them in.
258fn declared_type_error(
259    schema: &FreeBuiltinSchema,
260    index: usize,
261    found: &Value,
262    span: Span,
263) -> RuntimeError {
264    let param = &schema.params[index];
265    type_error(schema.name, param.name, &param.ty.to_string(), found, span)
266}
267
268/// The `Err` a failed assertion produces.
269fn assertion_failure(message: String) -> Value {
270    Value::err(Value::error(message))
271}
272
273/// The source text of argument `index`, or a placeholder when the caller
274/// could not supply it.
275fn source_of<'a>(sources: &[&'a str], index: usize) -> &'a str {
276    sources.get(index).copied().unwrap_or("?")
277}
278
279/// `Ok(v)`, `Err(e)`, `Some(v)`, `Error("message")`, `Shared(value)`.
280///
281/// Which names these are and how many arguments each carries come from the
282/// shared table; what each one builds is here, because building one needs a
283/// [`Value`].
284pub fn call_constructor(
285    name: &str,
286    args: &mut Vec<Value>,
287    span: Span,
288) -> Result<Value, RuntimeError> {
289    let Some(schema) =
290        free_builtin(name).filter(|schema| schema.kind == FreeBuiltinKind::Constructor)
291    else {
292        return Err(RuntimeError::new(format!("unknown constructor `{name}`")).at(span));
293    };
294    let args = expect_args(name, args, schema.arity(), span)?;
295    let value = args.remove(0);
296    Ok(match name {
297        "Ok" => Value::ok(value),
298        "Err" => Value::err(value),
299        "Some" => Value::some(value),
300        // `Shared` is the one constructor that can refuse its payload: what
301        // it wraps must be task-safe, since a `Shared` is reachable from
302        // every task it was given to.
303        "Shared" => Value(Repr::Shared(SharedCell::wrap(&value, span)?)),
304        "Error" => match value {
305            Value(Repr::Str(message)) => Value::error(message.to_string()),
306            other => {
307                return Err(declared_type_error(schema, 0, &other, span));
308            }
309        },
310        // As in `call_assertion`: a name the table declares and nothing here
311        // builds is what `tests/builtin_schema.rs` refuses to let happen.
312        _ => return Err(RuntimeError::new(format!("unknown constructor `{name}`")).at(span)),
313    })
314}
315
316/// `Vector.of(...)` and `Int.parse(...)`.
317pub fn call_associated(
318    host: &mut dyn Callable,
319    type_name: &str,
320    name: &str,
321    args: &mut Vec<Value>,
322    span: Span,
323) -> Result<Value, RuntimeError> {
324    match (type_name, name) {
325        ("Vector", "of") => Ok(host.allocate_vector(std::mem::take(args))),
326        // `Map.of` takes the `MapEntry` values `MapEntry(key:, value:)`
327        // builds. A literal with two identical keys is a mistake, not an
328        // intent, so a duplicate key is rejected rather than resolved by
329        // silently keeping the first or last entry.
330        ("Map", "of") => {
331            let mut map: BTreeMap<MapKey, Value> = BTreeMap::new();
332            for arg in args.drain(..) {
333                let Value(Repr::Struct(entry)) = &arg else {
334                    return Err(expects_map_entry(&arg, span));
335                };
336                if &*entry.type_name != MAP_ENTRY.name {
337                    return Err(expects_map_entry(&arg, span));
338                }
339                let key_value = entry.get("key").expect("MapEntry always has a `key` field");
340                let key = to_map_key("Map.of", "map key", key_value, span)?;
341                if map.contains_key(&key) {
342                    return Err(duplicate_key_error("Map.of", "key", &key, span));
343                }
344                let value = entry
345                    .get("value")
346                    .expect("MapEntry always has a `value` field")
347                    .clone();
348                map.insert(key, value);
349            }
350            Ok(Value(Repr::Map(Rc::new(map))))
351        }
352        // `Set.of` rejects a duplicate element for the same reason `Map.of`
353        // rejects a duplicate key.
354        ("Set", "of") => {
355            let mut set: BTreeSet<MapKey> = BTreeSet::new();
356            for item in args.drain(..) {
357                let key = to_map_key("Set.of", "set element", &item, span)?;
358                if !set.insert(key.clone()) {
359                    return Err(duplicate_key_error("Set.of", "element", &key, span));
360                }
361            }
362            Ok(Value(Repr::Set(Rc::new(set))))
363        }
364        // `Duration.nanos(count)`: the one primitive builder left.
365        // `micros` through `hours` are `std.duration.ofMicros` and its four
366        // neighbours now — see `cove_schema::builtins::standard_associated_binding`
367        // — and this arm no longer names them, so there is no factor to
368        // multiply by and nothing here can overflow: a `Duration` is signed
369        // nanoseconds and every `Int` is already a valid count of them.
370        ("Duration", "nanos") => {
371            let args = expect_args("Duration.nanos", args, 1, span)?;
372            let Value(Repr::Int(count)) = &args[0] else {
373                return Err(type_error("Duration.nanos", "count", "Int", &args[0], span));
374            };
375            Ok(Value(Repr::Duration(*count)))
376        }
377        ("Int", "parse") => {
378            let args = expect_args("Int.parse", args, 1, span)?;
379            let Value(Repr::Str(text)) = &args[0] else {
380                return Err(type_error("Int.parse", "text", "String", &args[0], span));
381            };
382            Ok(match text.parse::<i64>() {
383                Ok(value) => Value::ok(Value(Repr::Int(value))),
384                Err(_) => Value::err(Value::error(format!("`{text}` is not an Int"))),
385            })
386        }
387        // `Int.parse` in a base other than ten. A `radix` outside `2..=36`
388        // names no notation, so it stops the run the way an empty
389        // `String.split` separator does; text that is not a number in a
390        // radix that does exist is the data's failure and answers `Err`,
391        // which is the same line `Int.parse` draws. Rust's
392        // `i64::from_str_radix` reads a leading `+` or `-` and no digit
393        // separators, exactly as `parse::<i64>` above does.
394        ("Int", "parseRadix") => {
395            let args = expect_args("Int.parseRadix", args, 2, span)?;
396            let Value(Repr::Str(text)) = &args[0] else {
397                return Err(type_error(
398                    "Int.parseRadix",
399                    "text",
400                    "String",
401                    &args[0],
402                    span,
403                ));
404            };
405            let Value(Repr::Int(radix)) = &args[1] else {
406                return Err(type_error("Int.parseRadix", "radix", "Int", &args[1], span));
407            };
408            let Some(radix) = (2..=36).contains(radix).then_some(*radix as u32) else {
409                return Err(radix_error(*radix, span));
410            };
411            Ok(match i64::from_str_radix(text, radix) {
412                Ok(value) => Value::ok(Value(Repr::Int(value))),
413                Err(_) => Value::err(Value::error(format!(
414                    "`{text}` is not an Int in radix {radix}"
415                ))),
416            })
417        }
418        // The one-character `String` a Unicode code point names. A character
419        // in Cove is a `String` of length 1 — `chars()` answers an array of
420        // them — so this is that decomposition run backwards, and there is
421        // no `Character` type for it to answer instead.
422        ("String", "fromCodePoint") => {
423            let args = expect_args("String.fromCodePoint", args, 1, span)?;
424            let Value(Repr::Int(code_point)) = &args[0] else {
425                return Err(type_error(
426                    "String.fromCodePoint",
427                    "codePoint",
428                    "Int",
429                    &args[0],
430                    span,
431                ));
432            };
433            Ok(from_code_point(*code_point))
434        }
435        // Mirrors `Int.parse` exactly in shape. Rust's `f64::from_str`
436        // accepts `inf`, `-inf`, and `NaN`, which is why this does too, and
437        // it rejects the `_` digit separators a `Float` literal may be
438        // written with — the same thing `Int.parse` above already does,
439        // not a new choice made here.
440        ("Float", "parse") => {
441            let args = expect_args("Float.parse", args, 1, span)?;
442            let Value(Repr::Str(text)) = &args[0] else {
443                return Err(type_error("Float.parse", "text", "String", &args[0], span));
444            };
445            Ok(match text.parse::<f64>() {
446                Ok(value) => Value::ok(Value(Repr::Float(value))),
447                Err(_) => Value::err(Value::error(format!("`{text}` is not a Float"))),
448            })
449        }
450        _ => Err(
451            RuntimeError::new(format!("`{type_name}` has no associated function `{name}`"))
452                .at(span),
453        ),
454    }
455}
456
457/// Dispatches `receiver.name(args)` to a builtin method.
458pub fn call_method(
459    host: &mut dyn Callable,
460    receiver: &Value,
461    name: &str,
462    args: &mut Vec<Value>,
463    span: Span,
464) -> Result<Value, RuntimeError> {
465    // A receiver that answers `length()` is one a program might have written
466    // `count()` on, so the shared table's own methods are what decide who is
467    // taught the spelling. The name is compared first, so an ordinary call
468    // never asks.
469    if name == "count" {
470        let type_name = receiver.type_name();
471        if cove_schema::builtins::declares_length(&type_name) {
472            return Err(count_is_spelled_length(&type_name, span));
473        }
474    }
475    match receiver {
476        Value(Repr::Array(items)) => match name {
477            "get" => Ok(index_of("Array.get", args, span)?
478                .and_then(|i| items.get(i).cloned())
479                .map(Value::some)
480                .unwrap_or_else(Value::none)),
481            "length" => {
482                expect_args(name, args, 0, span)?;
483                Ok(Value(Repr::Int(items.len() as i64)))
484            }
485            // `isEmpty` used to answer here too, `length() == 0`. It does
486            // not reach this arm any more: `Interpreter::eval_method_call`
487            // resolves it to a call into `std.array.isEmpty` before this
488            // function is ever asked about it — see
489            // `cove_schema::builtins::standard_binding`.
490            "contains" => contains("Array.contains", items, args, span),
491            "indexOf" => index_of_element("Array.indexOf", items, args, span),
492            "slice" => Ok(Value(Repr::Array(slice("Array.slice", items, args, span)?))),
493            // `Vector.toArray` run backwards: a growable copy of these
494            // elements that nothing else holds a handle to, so a `freeze()`
495            // on it is the O(1) one. The elements are cloned as they are
496            // rather than snapshotted, which is `toArray`'s own rule — this
497            // separates the sequence and nothing inside it. The storage
498            // comes from the running task's heap like every other `Vector`,
499            // so the collector sees it.
500            "toVector" => {
501                expect_args("toVector", args, 0, span)?;
502                Ok(host.allocate_vector(items.to_vec()))
503            }
504            // `filter` and `fold` used to answer here too, through
505            // `walk_with` below. Neither reaches this arm any more:
506            // `Interpreter::eval_method_call` resolves both to a call into
507            // `std.array.filter` and `std.array.fold` before this function is
508            // ever asked about them — see
509            // `cove_schema::builtins::standard_binding`.
510            "map" | "sorted" => walk_with(host, "Array", items.to_vec(), name, args, span),
511            _ => Err(no_method("Array", name, span)),
512        },
513        Value(Repr::Vector(storage)) => {
514            check_live(storage, name, span)?;
515            match name {
516                "push" => {
517                    let args = expect_args("push", args, 1, span)?;
518                    storage.elements.borrow_mut().push(args.remove(0));
519                    Ok(Value(Repr::Unit))
520                }
521                // Replaces the element at `index` and answers what was
522                // there, or answers `None` and writes nothing when `index`
523                // is not already in the vector — which is `get`'s answer to
524                // the same bad index, so a program has one rule about
525                // indices rather than two. The write goes through the
526                // storage handle, exactly as `push`'s does, so an alias
527                // observes it and there is nothing to write back to the
528                // receiver's own slot.
529                "set" => {
530                    let args = expect_args("Vector.set", args, 2, span)?;
531                    let value = args.remove(1);
532                    let Some(index) = index_of("Vector.set", args, span)? else {
533                        return Ok(Value::none());
534                    };
535                    let mut elements = storage.elements.borrow_mut();
536                    let Some(slot) = elements.get_mut(index) else {
537                        return Ok(Value::none());
538                    };
539                    Ok(Value::some(std::mem::replace(slot, value)))
540                }
541                // Takes the last element out and answers it, or answers
542                // `None` and writes nothing when there is no last element.
543                //
544                // The empty case is `remove(length() - 1)` on an empty
545                // vector, where that index is `-1` — which `get`, `set` and
546                // `remove` all answer `None` for. One rule about indices,
547                // rather than a rule about indices and a rule about
548                // emptiness.
549                "pop" => {
550                    expect_args("Vector.pop", args, 0, span)?;
551                    Ok(storage
552                        .elements
553                        .borrow_mut()
554                        .pop()
555                        .map(Value::some)
556                        .unwrap_or_else(Value::none))
557                }
558                // Takes the element at `index` out, moves everything after
559                // it down one, and answers what was there — or answers
560                // `None` and removes nothing for an index that is not
561                // already in the vector, which is `get`'s answer and
562                // `set`'s. The write goes through the storage handle, as
563                // `push`'s and `set`'s do, so an alias observes the shrink.
564                "remove" => {
565                    let Some(index) = index_of("Vector.remove", args, span)? else {
566                        return Ok(Value::none());
567                    };
568                    let mut elements = storage.elements.borrow_mut();
569                    if index >= elements.len() {
570                        return Ok(Value::none());
571                    }
572                    Ok(Value::some(elements.remove(index)))
573                }
574                "get" => Ok(index_of("Vector.get", args, span)?
575                    .and_then(|i| storage.elements.borrow().get(i).cloned())
576                    .map(Value::some)
577                    .unwrap_or_else(Value::none)),
578                "contains" => contains("Vector.contains", &storage.elements.borrow(), args, span),
579                "indexOf" => {
580                    index_of_element("Vector.indexOf", &storage.elements.borrow(), args, span)
581                }
582                "slice" => {
583                    let sliced = slice("Vector.slice", &storage.elements.borrow(), args, span)?;
584                    Ok(Value(Repr::Array(sliced)))
585                }
586                "length" => {
587                    expect_args(name, args, 0, span)?;
588                    Ok(Value(Repr::Int(storage.len() as i64)))
589                }
590                // `isEmpty` used to answer here too, `storage.is_empty()`.
591                // It does not reach this arm any more:
592                // `Interpreter::eval_method_call` resolves it to a call into
593                // `std.vector.isEmpty` before this function is ever asked
594                // about it — see `cove_schema::builtins::standard_binding`.
595                "freeze" => {
596                    expect_args("freeze", args, 0, span)?;
597                    freeze(storage, span)
598                }
599                "toArray" => {
600                    expect_args("toArray", args, 0, span)?;
601                    Ok(Value(Repr::Array(
602                        storage.elements.borrow().iter().cloned().collect(),
603                    )))
604                }
605                // `filter` and `fold` used to answer here too, through
606                // `walk_with` below, taking the same copy first. Neither
607                // reaches this arm any more: `Interpreter::eval_method_call`
608                // resolves both to a call into `std.vector.filter` and
609                // `std.vector.fold` before this function is ever asked about
610                // them — see `cove_schema::builtins::standard_binding`.
611                "map" | "sorted" => {
612                    // The elements come out here, before the first callback,
613                    // and the borrow ends with this statement. Both matter:
614                    // a callback can reach this very vector and push onto it
615                    // or `freeze` it, and it must find neither a live borrow
616                    // nor a walk that changes under it.
617                    let elements = storage.elements.borrow().clone();
618                    walk_with(host, "Vector", elements, name, args, span)
619                }
620                _ => Err(no_method("Vector", name, span)),
621            }
622        }
623        Value(Repr::Map(entries)) => match name {
624            "get" => {
625                let args = expect_args("Map.get", args, 1, span)?;
626                let key = to_map_key("Map.get", "map key", &args[0], span)?;
627                Ok(entries
628                    .get(&key)
629                    .cloned()
630                    .map(Value::some)
631                    .unwrap_or_else(Value::none))
632            }
633            "contains" => {
634                let args = expect_args("Map.contains", args, 1, span)?;
635                let key = to_map_key("Map.contains", "map key", &args[0], span)?;
636                Ok(Value(Repr::Bool(entries.contains_key(&key))))
637            }
638            "length" => {
639                expect_args(name, args, 0, span)?;
640                Ok(Value(Repr::Int(entries.len() as i64)))
641            }
642            // `isEmpty` used to answer here too, `entries.is_empty()`. It
643            // does not reach this arm any more: `Interpreter::eval_method_call`
644            // resolves it to a call into `std.map.isEmpty` before this
645            // function is ever asked about it — see
646            // `cove_schema::builtins::standard_binding`.
647            // Ascending key order, matching the `BTreeMap` storage and the
648            // order `for` iterates.
649            "keys" => {
650                expect_args(name, args, 0, span)?;
651                Ok(Value(Repr::Array(
652                    entries.keys().map(MapKey::to_value).collect(),
653                )))
654            }
655            "values" => {
656                expect_args(name, args, 0, span)?;
657                Ok(Value(Repr::Array(entries.values().cloned().collect())))
658            }
659            // `Map` is immutable, so `inserted`/`removed` return a new map
660            // rather than write through `entries`; the past-participle names
661            // say so, unlike `Vector`'s mutating `push`.
662            "inserted" => {
663                let args = expect_args("Map.inserted", args, 2, span)?;
664                let value = args.remove(1);
665                let key = to_map_key("Map.inserted", "map key", &args[0], span)?;
666                let mut next = (**entries).clone();
667                next.insert(key, value);
668                Ok(Value(Repr::Map(Rc::new(next))))
669            }
670            "removed" => {
671                let args = expect_args("Map.removed", args, 1, span)?;
672                let key = to_map_key("Map.removed", "map key", &args[0], span)?;
673                let mut next = (**entries).clone();
674                next.remove(&key);
675                Ok(Value(Repr::Map(Rc::new(next))))
676            }
677            _ => Err(no_method("Map", name, span)),
678        },
679        Value(Repr::Set(items)) => match name {
680            "contains" => {
681                let args = expect_args("Set.contains", args, 1, span)?;
682                let key = to_map_key("Set.contains", "set element", &args[0], span)?;
683                Ok(Value(Repr::Bool(items.contains(&key))))
684            }
685            "length" => {
686                expect_args(name, args, 0, span)?;
687                Ok(Value(Repr::Int(items.len() as i64)))
688            }
689            // `isEmpty` used to answer here too, `items.is_empty()`. It does
690            // not reach this arm any more: `Interpreter::eval_method_call`
691            // resolves it to a call into `std.set.isEmpty` before this
692            // function is ever asked about it — see
693            // `cove_schema::builtins::standard_binding`.
694            "toArray" => {
695                expect_args(name, args, 0, span)?;
696                Ok(Value(Repr::Array(
697                    items.iter().map(MapKey::to_value).collect(),
698                )))
699            }
700            "inserted" => {
701                let args = expect_args("Set.inserted", args, 1, span)?;
702                let key = to_map_key("Set.inserted", "set element", &args[0], span)?;
703                let mut next = (**items).clone();
704                next.insert(key);
705                Ok(Value(Repr::Set(Rc::new(next))))
706            }
707            "removed" => {
708                let args = expect_args("Set.removed", args, 1, span)?;
709                let key = to_map_key("Set.removed", "set element", &args[0], span)?;
710                let mut next = (**items).clone();
711                next.remove(&key);
712                Ok(Value(Repr::Set(Rc::new(next))))
713            }
714            _ => Err(no_method("Set", name, span)),
715        },
716        Value(Repr::Str(text)) => match name {
717            "length" => {
718                expect_args(name, args, 0, span)?;
719                Ok(Value(Repr::Int(text.chars().count() as i64)))
720            }
721            // `isEmpty` used to answer here too, `text.is_empty()`. It does
722            // not reach this arm any more: `Interpreter::eval_method_call`
723            // resolves it to a call into `std.string.isEmpty` before this
724            // function is ever asked about it — see
725            // `cove_schema::builtins::standard_binding`.
726            "words" => {
727                expect_args(name, args, 0, span)?;
728                Ok(Value(Repr::Array(
729                    text.split_ascii_whitespace()
730                        .map(|w| Value(Repr::Str(w.into())))
731                        .collect(),
732                )))
733            }
734            "chars" => {
735                expect_args(name, args, 0, span)?;
736                Ok(Value(Repr::Array(
737                    text.chars()
738                        .map(|c| Value(Repr::Str(one_character(c))))
739                        .collect(),
740                )))
741            }
742            "split" => {
743                let args = expect_args("String.split", args, 1, span)?;
744                let separator = expect_str("String.split", "separator", &args[0], span)?;
745                if separator.is_empty() {
746                    return Err(empty_needle_error(
747                        "String.split",
748                        "separator",
749                        "use `chars()` to take a string apart character by character",
750                        span,
751                    ));
752                }
753                Ok(Value(Repr::Array(
754                    text.split(separator)
755                        .map(|part| Value(Repr::Str(part.into())))
756                        .collect(),
757                )))
758            }
759            "join" => {
760                let args = expect_args("String.join", args, 1, span)?;
761                let Value(Repr::Array(parts)) = &args[0] else {
762                    return Err(type_error(
763                        "String.join",
764                        "parts",
765                        "Array<String>",
766                        &args[0],
767                        span,
768                    ));
769                };
770                let mut joined = String::new();
771                for (index, part) in parts.iter().enumerate() {
772                    if index > 0 {
773                        joined.push_str(text);
774                    }
775                    joined.push_str(expect_str("String.join", "parts", part, span)?);
776                }
777                Ok(Value(Repr::Str(joined.into())))
778            }
779            "slice" => {
780                let args = expect_args("String.slice", args, 2, span)?;
781                let Value(Repr::Int(from)) = &args[0] else {
782                    return Err(type_error("String.slice", "from", "Int", &args[0], span));
783                };
784                let Value(Repr::Int(to)) = &args[1] else {
785                    return Err(type_error("String.slice", "to", "Int", &args[1], span));
786                };
787                let chars: Vec<char> = text.chars().collect();
788                let len = chars.len() as i64;
789                let from = (*from).clamp(0, len) as usize;
790                let to = (*to).clamp(0, len) as usize;
791                Ok(Value(Repr::Str(if to <= from {
792                    "".into()
793                } else {
794                    chars[from..to].iter().collect::<String>().into()
795                })))
796            }
797            "trim" => {
798                expect_args(name, args, 0, span)?;
799                Ok(Value(Repr::Str(text.trim().into())))
800            }
801            "contains" => {
802                let args = expect_args("String.contains", args, 1, span)?;
803                let needle = expect_str("String.contains", "text", &args[0], span)?;
804                Ok(Value(Repr::Bool(text.contains(needle))))
805            }
806            "startsWith" => {
807                let args = expect_args("String.startsWith", args, 1, span)?;
808                let prefix = expect_str("String.startsWith", "prefix", &args[0], span)?;
809                Ok(Value(Repr::Bool(text.starts_with(prefix))))
810            }
811            "endsWith" => {
812                let args = expect_args("String.endsWith", args, 1, span)?;
813                let suffix = expect_str("String.endsWith", "suffix", &args[0], span)?;
814                Ok(Value(Repr::Bool(text.ends_with(suffix))))
815            }
816            "indexOf" => {
817                let args = expect_args("String.indexOf", args, 1, span)?;
818                let needle = expect_str("String.indexOf", "text", &args[0], span)?;
819                Ok(match text.find(needle) {
820                    // `find` answers a byte offset; the characters before it
821                    // are counted to convert that into the character index
822                    // `length()` already counts in.
823                    Some(byte_index) => {
824                        Value::some(Value(Repr::Int(text[..byte_index].chars().count() as i64)))
825                    }
826                    None => Value::none(),
827                })
828            }
829            "replace" => {
830                let args = expect_args("String.replace", args, 2, span)?;
831                let old = expect_str("String.replace", "old", &args[0], span)?;
832                if old.is_empty() {
833                    return Err(empty_needle_error(
834                        "String.replace",
835                        "old",
836                        "`old` is the text to look for, and an empty `old` names none",
837                        span,
838                    ));
839                }
840                let new = expect_str("String.replace", "new", &args[1], span)?;
841                Ok(Value(Repr::Str(text.replace(old, new).into())))
842            }
843            "toUpper" => {
844                expect_args(name, args, 0, span)?;
845                Ok(Value(Repr::Str(text.to_uppercase().into())))
846            }
847            "toLower" => {
848                expect_args(name, args, 0, span)?;
849                Ok(Value(Repr::Str(text.to_lowercase().into())))
850            }
851            // The three byte-counted operations. Their diagnostics are
852            // written out again in `crates/cove-runtime/src/vm/builtins/text.rs`
853            // rather than shared, as every other builtin's are; what holds
854            // the two readings together is `tests/e2e/values_string`, which
855            // runs on both backends against one `expected.out`.
856            "byteLength" => {
857                expect_args(name, args, 0, span)?;
858                Ok(Value(Repr::Int(text.len() as i64)))
859            }
860            "byteAt" => {
861                let args = expect_args("String.byteAt", args, 1, span)?;
862                let Value(Repr::Int(offset)) = &args[0] else {
863                    return Err(type_error("String.byteAt", "offset", "Int", &args[0], span));
864                };
865                // Refused rather than answered, which is `sliceBytes`'s rule
866                // and not `codePointAtByte`'s: a byte offset out of range is
867                // one this type never handed out, and `byteLength()` is how a
868                // caller knows the range. The VM's `Inst::ByteAt` refuses in
869                // the same words.
870                match usize::try_from(*offset)
871                    .ok()
872                    .and_then(|at| text.as_bytes().get(at))
873                {
874                    Some(byte) => Ok(Value(Repr::Int(*byte as i64))),
875                    None => Err(RuntimeError::new(format!(
876                        "`byteAt` is `{offset}`, and a byte offset into this string is 0 to {}",
877                        text.len() as i64 - 1
878                    ))
879                    .at(span)),
880                }
881            }
882            "codePointAtByte" => {
883                let args = expect_args("String.codePointAtByte", args, 1, span)?;
884                let Value(Repr::Int(offset)) = &args[0] else {
885                    return Err(type_error(
886                        "String.codePointAtByte",
887                        "offset",
888                        "Int",
889                        &args[0],
890                        span,
891                    ));
892                };
893                // Past the end, before the start, and inside a character are
894                // one answer on purpose: a scanner that advances by the width
895                // of what it read reaches none of them, and telling them
896                // apart would cost a `Result` on the one path this exists for.
897                Ok(
898                    match usize::try_from(*offset)
899                        .ok()
900                        .filter(|at| *at < text.len() && text.is_char_boundary(*at))
901                        .and_then(|at| text[at..].chars().next())
902                    {
903                        Some(character) => Value::some(Value(Repr::Int(character as i64))),
904                        None => Value::none(),
905                    },
906                )
907            }
908            "sliceBytes" => {
909                let args = expect_args("String.sliceBytes", args, 2, span)?;
910                let Value(Repr::Int(from)) = &args[0] else {
911                    return Err(type_error(
912                        "String.sliceBytes",
913                        "from",
914                        "Int",
915                        &args[0],
916                        span,
917                    ));
918                };
919                let Value(Repr::Int(to)) = &args[1] else {
920                    return Err(type_error("String.sliceBytes", "to", "Int", &args[1], span));
921                };
922                Ok(match byte_range(text, *from, *to) {
923                    Ok(range) => Value::ok(Value(Repr::Str(text[range].into()))),
924                    Err(message) => Value::err(Value::error(message)),
925                })
926            }
927            _ => Err(no_method("String", name, span)),
928        },
929        Value(Repr::Range {
930            start,
931            end,
932            inclusive_end,
933        }) => {
934            let bounds = RangeBounds::of(*start, *end, *inclusive_end);
935            match name {
936                "length" => {
937                    expect_args(name, args, 0, span)?;
938                    Ok(Value(Repr::Int(bounds.len())))
939                }
940                "isEmpty" => {
941                    expect_args(name, args, 0, span)?;
942                    Ok(Value(Repr::Bool(bounds.is_empty())))
943                }
944                "contains" => {
945                    let args = expect_args("contains", args, 1, span)?;
946                    let Value(Repr::Int(value)) = &args[0] else {
947                        return Err(type_error("Range.contains", "value", "Int", &args[0], span));
948                    };
949                    Ok(Value(Repr::Bool(bounds.contains(*value))))
950                }
951                _ => Err(no_method("Range", name, span)),
952            }
953        }
954        // `Option` and `Result` do not answer here at all any more. Every
955        // one of their methods — `isSome`, `isNone`, `unwrapOr` on the one,
956        // `isOk`, `isError`, `unwrapOr`, `mapError` on the other — is
957        // resolved by `Interpreter::eval_method_call` to a call into
958        // `std.option` or `std.result` before this function is ever asked;
959        // see `cove_schema::builtins::standard_binding`.
960        //
961        // `mapError` was the last to go and it needed a language change
962        // rather than a migration: while a callback of no parameters could
963        // stand in for one that takes the error, no Cove body could call it.
964        // ADR 0044 removed that exception.
965        Value(Repr::Int(n)) => match name {
966            "toFloat" => {
967                expect_args(name, args, 0, span)?;
968                Ok(Value(Repr::Float(*n as f64)))
969            }
970            // `min`, `max`, and `abs` used to answer here too, `(*n).min(*other)`,
971            // `(*n).max(*other)`, and `n.checked_abs()`. None reaches this arm
972            // any more: `Interpreter::eval_method_call` resolves them to a
973            // call into `std.int.min`/`std.int.max`/`std.int.abs` before this
974            // function is ever asked about them — see
975            // `cove_schema::builtins::standard_binding`.
976            _ => Err(no_method("Int", name, span)),
977        },
978        Value(Repr::Float(x)) => match name {
979            "toInt" => {
980                expect_args(name, args, 0, span)?;
981                Ok(float_to_int(*x))
982            }
983            "round" => {
984                expect_args(name, args, 0, span)?;
985                Ok(Value(Repr::Float(x.round())))
986            }
987            "abs" => {
988                expect_args(name, args, 0, span)?;
989                Ok(Value(Repr::Float(x.abs())))
990            }
991            "sqrt" => {
992                expect_args(name, args, 0, span)?;
993                Ok(Value(Repr::Float(x.sqrt())))
994            }
995            "min" => {
996                let args = expect_args("Float.min", args, 1, span)?;
997                let Value(Repr::Float(other)) = &args[0] else {
998                    return Err(type_error("Float.min", "other", "Float", &args[0], span));
999                };
1000                Ok(Value(Repr::Float(x.min(*other))))
1001            }
1002            "max" => {
1003                let args = expect_args("Float.max", args, 1, span)?;
1004                let Value(Repr::Float(other)) = &args[0] else {
1005                    return Err(type_error("Float.max", "other", "Float", &args[0], span));
1006                };
1007                Ok(Value(Repr::Float(x.max(*other))))
1008            }
1009            "format" => {
1010                let args = expect_args("Float.format", args, 1, span)?;
1011                let Value(Repr::Int(digits)) = &args[0] else {
1012                    return Err(type_error("Float.format", "digits", "Int", &args[0], span));
1013                };
1014                if !(0..=17).contains(digits) {
1015                    return Err(format_digits_error(*digits, span));
1016                }
1017                Ok(Value(Repr::Str(
1018                    format!("{:.*}", *digits as usize, x).into(),
1019                )))
1020            }
1021            _ => Err(no_method("Float", name, span)),
1022        },
1023        // `d.nanos()`: the one primitive reader left. `micros` through
1024        // `hours` are `std.duration.micros` and its four neighbours now,
1025        // resolved by `Interpreter::eval_method_call` before this function
1026        // is ever asked — see
1027        // `cove_schema::builtins::standard_binding`.
1028        Value(Repr::Duration(ns)) => match name {
1029            "nanos" => {
1030                expect_args(name, args, 0, span)?;
1031                Ok(Value(Repr::Int(*ns)))
1032            }
1033            _ => Err(no_method("Duration", name, span)),
1034        },
1035        other => Err(no_method(&other.type_name(), name, span)),
1036    }
1037}
1038
1039/// `map` and `sorted`, the two operations on an `Array` and on a `Vector`
1040/// that still take their callback here, as the interpreter runs them: the
1041/// linear-memory backend lowers each of the two to its own loop instead —
1042/// see `crates/cove-ir/src/lower/walks.rs`, which calls this file's version
1043/// the oracle it has to agree with.
1044///
1045/// `filter` and `fold` used to be two more. They are ordinary calls into
1046/// `std.array` and `std.vector` now, resolved before either evaluator ever
1047/// reaches this function — see `cove_schema::builtins::standard_binding`.
1048///
1049/// `elements` is already the caller's own copy — the `Array`'s elements, or
1050/// the `Vector`'s taken out from under its `RefCell` before this was
1051/// called — which is what makes the walk a walk over a snapshot. A callback
1052/// that reaches the vector it was handed an element of may push onto it,
1053/// `freeze` it, or drop the last other handle to it, and neither changes
1054/// what is being walked or what comes back. The lowering makes the same
1055/// decision by reading a sequence's length once, with `Inst::Len`, before it
1056/// walks; this is that decision in the place where a closure rather than a
1057/// loop body is what could do the mutating.
1058///
1059/// Everything a callback costs is accounted where any other call is:
1060/// [`Callable::call_value`] is the evaluator re-entered, so fuel, the depth
1061/// limit, the host's `max_call_depth`, cancellation, and the trace are the
1062/// running task's exactly as they are outside a builtin. There is nothing
1063/// here that steps around a safepoint, because there is nothing here that
1064/// runs Cove code by any other route.
1065///
1066/// A callback that fails takes the whole call with it. The answer is built
1067/// to the side and returned only on success, so no half-built array and no
1068/// half-sorted sequence is ever reachable, and no receiver is written
1069/// through on any path.
1070///
1071/// # The argument list is `args`, once, for the whole walk
1072///
1073/// Each of the two takes its callback out of `args` first, which leaves that
1074/// vector empty with its capacity intact — so it is what every invocation of
1075/// the callback is handed, filled and drained again per element rather than
1076/// allocated per element. That is issue #193: `map` built a `vec![item]` for
1077/// each element it visited, `filter` a `vec![item.clone()]`, `fold` a
1078/// `vec![total, item]`, and `sorted` one per comparison, which for
1079/// `examples/life`'s `population()` is an allocation per creature per tick —
1080/// true of all four at the time #193 was fixed, even though two of them have
1081/// since moved out of this function entirely.
1082///
1083/// It costs nothing to arrange because `args` is already a vector the
1084/// caller lends. The predecessor pooled its own argument vectors the same
1085/// way starting at #184; #193 is that scheme reaching a path it could not
1086/// reach before, by being handed one level further down. A slice would not
1087/// do here for the same reason it would not do there — `map` moves its
1088/// element into the call, and the callback re-enters the evaluator and may
1089/// push onto the very stack a slice would point into.
1090fn walk_with(
1091    host: &mut dyn Callable,
1092    type_name: &str,
1093    elements: Vec<Value>,
1094    name: &str,
1095    args: &mut Vec<Value>,
1096    span: Span,
1097) -> Result<Value, RuntimeError> {
1098    let method = format!("{type_name}.{name}");
1099    match name {
1100        "map" => {
1101            let args = expect_args(&method, args, 1, span)?;
1102            let transform = args.remove(0);
1103            expect_callback(
1104                host,
1105                &method,
1106                "transform",
1107                "fn(T) -> R",
1108                1,
1109                &transform,
1110                span,
1111            )?;
1112            let mut mapped = Vec::with_capacity(elements.len());
1113            for item in elements {
1114                args.push(item);
1115                mapped.push(host.call_value(&transform, args, span)?);
1116            }
1117            Ok(Value(Repr::Array(mapped.into())))
1118        }
1119        "sorted" => {
1120            let args = expect_args(&method, args, 1, span)?;
1121            let by = args.remove(0);
1122            expect_callback(host, &method, "by", "fn(T, T) -> Bool", 2, &by, span)?;
1123            Ok(Value(Repr::Array(
1124                merge_sort(host, &method, elements, &by, args, span)?.into(),
1125            )))
1126        }
1127        // Only the two names above are routed here now; `filter` and `fold`
1128        // are resolved to a standard-library call before either evaluator
1129        // reaches this function. Answering the way an unknown method is
1130        // answered keeps that a fact rather than a `panic!` nobody can reach.
1131        _ => Err(no_method(type_name, name, span)),
1132    }
1133}
1134
1135/// A stable merge sort under a Cove callback.
1136///
1137/// Written out rather than handed to `slice::sort_by`, for two reasons
1138/// either of which would be enough on its own.
1139///
1140/// `by` can fail — it is a Cove closure, and a closure can raise or be
1141/// cancelled — and a `FnMut(&T, &T) -> Ordering` has nowhere to put a
1142/// failure. Smuggling one out through a cell and re-raising it afterwards
1143/// would mean the sort kept comparing after the run should have stopped.
1144///
1145/// And `by` can contradict itself. `slice::sort_by` panics when its
1146/// comparison function does not order the elements, and a panic in this
1147/// runtime means a broken invariant of the runtime; a program that wrote an
1148/// inconsistent comparison has broken nothing but its own ordering. A merge
1149/// answers some permutation instead, which is exactly what "no promise about
1150/// which" means, and it is the schema's stated behaviour rather than a
1151/// consequence of the algorithm that was to hand.
1152///
1153/// Bottom up: runs of one merged into runs of two, then four. The right
1154/// run's element is taken only when `by` says it comes *strictly* before the
1155/// left run's, which is what makes the sort stable — equal elements meet
1156/// with the earlier one on the left and the earlier one is kept.
1157fn merge_sort(
1158    host: &mut dyn Callable,
1159    method: &str,
1160    elements: Vec<Value>,
1161    by: &Value,
1162    args: &mut Vec<Value>,
1163    span: Span,
1164) -> Result<Vec<Value>, RuntimeError> {
1165    let len = elements.len();
1166    let mut source = elements;
1167    let mut merged: Vec<Value> = Vec::with_capacity(len);
1168    let mut width = 1usize;
1169    while width < len {
1170        merged.clear();
1171        let mut start = 0usize;
1172        while start < len {
1173            let middle = (start + width).min(len);
1174            let end = (start + width * 2).min(len);
1175            let (mut left, mut right) = (start, middle);
1176            while left < middle && right < end {
1177                args.push(source[right].clone());
1178                args.push(source[left].clone());
1179                let verdict = host.call_value(by, args, span)?;
1180                if callback_bool(method, "by", &verdict, span)? {
1181                    merged.push(source[right].clone());
1182                    right += 1;
1183                } else {
1184                    merged.push(source[left].clone());
1185                    left += 1;
1186                }
1187            }
1188            merged.extend_from_slice(&source[left..middle]);
1189            merged.extend_from_slice(&source[right..end]);
1190            start = end;
1191        }
1192        std::mem::swap(&mut source, &mut merged);
1193        width *= 2;
1194    }
1195    Ok(source)
1196}
1197
1198/// Holds a higher-order builtin's callback to the shape its signature
1199/// declares, before it is called rather than while it is being called.
1200///
1201/// The checker settles this for every program it accepts, so nothing a
1202/// checked program does reaches either failure. It is still asked here,
1203/// once for the whole walk, rather than left for
1204/// `Interpreter::call_value_slots` to discover on the first call: a walk of
1205/// zero elements never makes that call at all, so leaving the check there
1206/// would mean an empty `Array` misses a callback of the wrong arity that a
1207/// full one catches. Asking here also gives the failure the builtin's own
1208/// words — the declared shape, `fn(T) -> R`, and which parameter — rather
1209/// than a plain arity count. `map` and `sorted` are the interpreter's own
1210/// implementation of the two walks that remain here — `filter` and `fold`
1211/// moved to the standard library and never reach this function — and the
1212/// linear-memory backend lowers `map` and `sorted` on its own and never
1213/// reaches this function either.
1214fn expect_callback(
1215    host: &dyn Callable,
1216    method: &str,
1217    parameter: &str,
1218    expected: &str,
1219    parameters: usize,
1220    value: &Value,
1221    span: Span,
1222) -> Result<(), RuntimeError> {
1223    match host.arity(value) {
1224        Some(found) if found == parameters => Ok(()),
1225        Some(found) => Err(RuntimeError::new(format!(
1226            "`{method}` expects `{expected}` for `{parameter}`, but found a function of {found} parameter(s)"
1227        ))
1228        .at(span)),
1229        None => Err(type_error(method, parameter, expected, value, span)),
1230    }
1231}
1232
1233/// Reads a callback's answer as the `Bool` its signature declares.
1234///
1235/// Unreachable from a checked program for the same reason [`expect_callback`]
1236/// is, and stated for the same reason: the alternative is a `Bool` taken on
1237/// trust in the middle of a sort.
1238fn callback_bool(
1239    method: &str,
1240    parameter: &str,
1241    value: &Value,
1242    span: Span,
1243) -> Result<bool, RuntimeError> {
1244    match value {
1245        Value(Repr::Bool(answer)) => Ok(*answer),
1246        other => Err(RuntimeError::new(format!(
1247            "`{method}` expects `{parameter}` to answer a `Bool`, but found `{}`",
1248            other.type_name()
1249        ))
1250        .at(span)),
1251    }
1252}
1253
1254/// Consumes uniquely owned vector storage and returns its elements as an
1255/// `Array` in O(1).
1256///
1257/// Uniqueness is the runtime form of the Language Card's local uniqueness
1258/// check: the caller must hold the only handle to this storage.
1259pub fn freeze(storage: &Rc<VectorStorage>, span: Span) -> Result<Value, RuntimeError> {
1260    check_live(storage, "freeze", span)?;
1261    if Rc::strong_count(storage) != 1 {
1262        return Err(RuntimeError::new(
1263            "`freeze()` needs uniquely owned vector storage, but another alias observes this vector",
1264        )
1265        .at(span)
1266        .with_rule(
1267            "`freeze()` consumes a locally unique vector and returns an immutable array in O(1).",
1268        )
1269        .with_help(
1270            "call `toArray()` instead, which copies the elements in O(n), or drop the other alias before calling `freeze()`",
1271        ));
1272    }
1273    let elements = storage.elements.take();
1274    *storage.frozen.borrow_mut() = true;
1275    Ok(Value(Repr::Array(elements.into())))
1276}
1277
1278/// A vector consumed by `freeze()` is no longer usable.
1279pub fn check_live(
1280    storage: &Rc<VectorStorage>,
1281    method: &str,
1282    span: Span,
1283) -> Result<(), RuntimeError> {
1284    if *storage.frozen.borrow() {
1285        return Err(RuntimeError::new(format!(
1286            "`{method}` was called on a vector that `freeze()` already consumed"
1287        ))
1288        .at(span)
1289        .with_rule("`freeze()` consumes its vector; the source vector is no longer usable.")
1290        .with_help("use the `Array` that `freeze()` returned, or build a new vector"));
1291    }
1292    Ok(())
1293}
1294
1295/// `contains(element)` on a sequence: whether any element is `==` to it.
1296///
1297/// Equality is [`Value::eq_value`], the same one `==` is and the same one
1298/// `Map` and `Set` are keyed by, so a sequence answers membership exactly as
1299/// a comparison of the two values would. An empty receiver answers `false`,
1300/// and no argument can be refused: every value has an equality.
1301fn contains(
1302    method: &str,
1303    items: &[Value],
1304    args: &mut Vec<Value>,
1305    span: Span,
1306) -> Result<Value, RuntimeError> {
1307    let args = expect_args(method, args, 1, span)?;
1308    Ok(Value(Repr::Bool(
1309        items.iter().any(|item| item.eq_value(&args[0])),
1310    )))
1311}
1312
1313/// `indexOf(element)` on a sequence: the first position holding a value `==`
1314/// to it, or `None`.
1315///
1316/// The same equality [`contains`] uses, so the two cannot disagree about
1317/// whether an element is there. An empty receiver and an element that is not
1318/// in the sequence both answer `None`, which is what `String.indexOf` and
1319/// `Array.get` answer a question with no position to name.
1320fn index_of_element(
1321    method: &str,
1322    items: &[Value],
1323    args: &mut Vec<Value>,
1324    span: Span,
1325) -> Result<Value, RuntimeError> {
1326    let args = expect_args(method, args, 1, span)?;
1327    Ok(items
1328        .iter()
1329        .position(|item| item.eq_value(&args[0]))
1330        .map(|at| Value::some(Value(Repr::Int(at as i64))))
1331        .unwrap_or_else(Value::none))
1332}
1333
1334/// `slice(from, to)` on a sequence: the elements at `from..<to`.
1335///
1336/// Both bounds are clamped into `0..len` and a `to` at or below `from`
1337/// answers nothing, which is `String.slice`'s rule applied where the same
1338/// question arises rather than a second answer to it. So no argument can
1339/// stop the run: this refuses only a bound that is not an `Int` at all,
1340/// which is the receiver being called wrongly rather than an index being out
1341/// of range.
1342fn slice(
1343    method: &str,
1344    items: &[Value],
1345    args: &[Value],
1346    span: Span,
1347) -> Result<Rc<[Value]>, RuntimeError> {
1348    if args.len() != 2 {
1349        return Err(arity_error(method, 2, args.len(), span));
1350    }
1351    let bound = |at: usize, parameter: &str| match &args[at] {
1352        Value(Repr::Int(index)) => Ok((*index).clamp(0, items.len() as i64) as usize),
1353        other => Err(type_error(method, parameter, "Int", other, span)),
1354    };
1355    let from = bound(0, "from")?;
1356    let to = bound(1, "to")?;
1357    if to <= from {
1358        return Ok(Rc::from([]));
1359    }
1360    Ok(Rc::from(&items[from..to]))
1361}
1362
1363fn index_of(method: &str, args: &[Value], span: Span) -> Result<Option<usize>, RuntimeError> {
1364    if args.len() != 1 {
1365        return Err(arity_error(method, 1, args.len(), span));
1366    }
1367    match &args[0] {
1368        Value(Repr::Int(i)) if *i >= 0 => Ok(Some(*i as usize)),
1369        Value(Repr::Int(_)) => Ok(None),
1370        other => Err(type_error(method, "index", "Int", other, span)),
1371    }
1372}
1373
1374fn expect_args<'a>(
1375    method: &str,
1376    args: &'a mut Vec<Value>,
1377    count: usize,
1378    span: Span,
1379) -> Result<&'a mut Vec<Value>, RuntimeError> {
1380    if args.len() != count {
1381        return Err(arity_error(method, count, args.len(), span));
1382    }
1383    Ok(args)
1384}
1385
1386fn arity_error(method: &str, expected: usize, found: usize, span: Span) -> RuntimeError {
1387    RuntimeError::new(format!(
1388        "`{method}` takes {expected} argument(s), but {found} were given"
1389    ))
1390    .at(span)
1391}
1392
1393fn type_error(
1394    method: &str,
1395    parameter: &str,
1396    expected: &str,
1397    found: &Value,
1398    span: Span,
1399) -> RuntimeError {
1400    RuntimeError::new(format!(
1401        "`{method}` expects `{expected}` for `{parameter}`, but found `{}`",
1402        found.type_name()
1403    ))
1404    .at(span)
1405}
1406
1407fn no_method(type_name: &str, method: &str, span: Span) -> RuntimeError {
1408    RuntimeError::new(format!("`{type_name}` has no method `{method}`")).at(span)
1409}
1410
1411/// `Float.toInt`: truncates toward zero and names which of the three expected
1412/// failures stopped it. `NaN` is not a number, an infinity has no
1413/// truncation, and a magnitude at or past 2^63 does not fit in an `Int`.
1414fn float_to_int(x: f64) -> Value {
1415    if x.is_nan() {
1416        return Value::err(Value::error(
1417            "`Float.toInt` cannot convert `NaN`, which is not a number",
1418        ));
1419    }
1420    if x.is_infinite() {
1421        return Value::err(Value::error(format!(
1422            "`Float.toInt` cannot convert `{x}`, which has no truncation"
1423        )));
1424    }
1425    let truncated = x.trunc();
1426    if truncated < i64::MIN as f64 || truncated >= i64::MAX as f64 {
1427        return Value::err(Value::error(format!(
1428            "`Float.toInt` cannot convert `{x}`, which is outside Int's range"
1429        )));
1430    }
1431    Value::ok(Value(Repr::Int(truncated as i64)))
1432}
1433
1434/// `String.fromCodePoint`: the one-character `String` a code point names, and
1435/// otherwise which of the two ways the number names no character.
1436///
1437/// The surrogates get a sentence of their own because they are the failure a
1438/// caller is most likely to be able to do something about. A format that
1439/// writes a code point in sixteen bits — JSON's `\u`, and UTF-16 generally —
1440/// writes anything past `0xFFFF` as a pair of them, so a program that reached
1441/// here with a `0xD800` has half of a character rather than a bad one, and
1442/// what it needs to hear is that the other half is still to come. Combining
1443/// the pair is arithmetic the program does before it calls this: there is no
1444/// half-formed value to hand back, because a Cove `String` is UTF-8 and holds
1445/// no such thing.
1446fn from_code_point(code_point: i64) -> Value {
1447    if (0xD800..=0xDFFF).contains(&code_point) {
1448        return Value::err(Value::error(format!(
1449            "`{code_point}` is a surrogate half, which is not a character on its own"
1450        )));
1451    }
1452    match u32::try_from(code_point).ok().and_then(char::from_u32) {
1453        Some(character) => Value::ok(Value(Repr::Str(one_character(character)))),
1454        None => Value::err(Value::error(format!(
1455            "`{code_point}` is not a Unicode code point"
1456        ))),
1457    }
1458}
1459
1460/// `Int.parseRadix` refused a `radix` outside `2..=36`.
1461///
1462/// A radix of 1 has no place value and a radix of 0 has no digits, and past
1463/// 36 there are no more letters to spell one with. None of those is text the
1464/// data got wrong, so none of them is an `Err`: it is the call that is wrong,
1465/// and the run stops the way it stops for an empty `String.split` separator.
1466fn radix_error(radix: i64, span: Span) -> RuntimeError {
1467    RuntimeError::new(format!(
1468        "`Int.parseRadix` cannot read a number in radix `{radix}`"
1469    ))
1470    .at(span)
1471    .with_rule(
1472        "A radix is 2 through 36, which is as many digits as the ten numerals and the twenty-six letters afford.",
1473    )
1474    .with_help("pass a `radix` between 2 and 36, such as 16 for hexadecimal")
1475}
1476
1477/// `Float.format` refused a `digits` outside `0..=17`.
1478///
1479/// A `Float` carries at most 17 significant decimal digits, so a `digits`
1480/// past that asks for padding rather than precision, and a negative `digits`
1481/// names nothing.
1482fn format_digits_error(digits: i64, span: Span) -> RuntimeError {
1483    RuntimeError::new(format!("`Float.format` cannot use `{digits}` digits"))
1484        .at(span)
1485        .with_rule(
1486            "A Float carries at most 17 significant decimal digits, so `digits` must be between 0 and 17.",
1487        )
1488}
1489
1490/// The byte range `sliceBytes(from, to)` names, or what is wrong with it.
1491///
1492/// Four things can be, and they are checked in the order a reader would ask
1493/// them: is each end a byte offset into this string at all, do they run
1494/// forwards, and does each begin a character. The last is the one `slice` has
1495/// no equivalent of, and it is why this refuses where `slice` clamps — an
1496/// offset inside a character was never handed out by `codePointAtByte`, so
1497/// moving it to the nearest legal one would answer a question nobody asked.
1498fn byte_range(text: &str, from: i64, to: i64) -> Result<std::ops::Range<usize>, String> {
1499    let len = text.len();
1500    let offset = |name: &str, value: i64| -> Result<usize, String> {
1501        usize::try_from(value)
1502            .ok()
1503            .filter(|at| *at <= len)
1504            .ok_or_else(|| {
1505                format!("`{name}` is `{value}`, and a byte offset into this string is 0 to {len}")
1506            })
1507    };
1508    let start = offset("from", from)?;
1509    let end = offset("to", to)?;
1510    if start > end {
1511        return Err(format!(
1512            "`from` is `{from}` and `to` is `{to}`, so this range runs backwards"
1513        ));
1514    }
1515    for (name, at) in [("from", start), ("to", end)] {
1516        if !text.is_char_boundary(at) {
1517            return Err(format!(
1518                "`{name}` is `{at}`, which is inside a character rather than at the start of one"
1519            ));
1520        }
1521    }
1522    Ok(start..end)
1523}
1524
1525/// The one-character string `character` spells.
1526///
1527/// An ASCII character answers a string this thread already made, because
1528/// `chars()` is how a program takes text apart and a scanner asks for every
1529/// character of every line it reads. Allocating one string per character made
1530/// that the largest single source of allocation in `examples/cq`, and a
1531/// character's string is immutable and interchangeable, so there is no way for
1532/// a program to tell a shared one from a fresh one (issue #104).
1533///
1534/// The table is per thread rather than global because `Rc` is not shareable
1535/// across threads, which is the same reason `Value` uses `Rc` at all.
1536fn one_character(character: char) -> Rc<str> {
1537    thread_local! {
1538        static ASCII: [Rc<str>; 128] =
1539            std::array::from_fn(|byte| Rc::from((byte as u8 as char).to_string().as_str()));
1540    }
1541    if character.is_ascii() {
1542        return ASCII.with(|table| table[character as usize].clone());
1543    }
1544    character.to_string().into()
1545}
1546
1547/// Reads `value` as a `String`, or reports the type `method` declares for
1548/// `parameter` instead.
1549fn expect_str<'a>(
1550    method: &str,
1551    parameter: &str,
1552    value: &'a Value,
1553    span: Span,
1554) -> Result<&'a str, RuntimeError> {
1555    match value {
1556        Value(Repr::Str(text)) => Ok(text),
1557        other => Err(type_error(method, parameter, "String", other, span)),
1558    }
1559}
1560
1561/// `split` and `replace` both refuse an empty needle: matching against one
1562/// would match between every character rather than answer either method's
1563/// question.
1564///
1565/// The two are told different things afterwards, because the operation they
1566/// were reaching for is different. Splitting on nothing is a request for the
1567/// characters, which `chars()` answers; replacing nothing is not a request for
1568/// anything, so `replace` is told what it is missing rather than offered a
1569/// substitute.
1570fn empty_needle_error(method: &str, parameter: &str, help: &str, span: Span) -> RuntimeError {
1571    RuntimeError::new(format!("`{method}` cannot use an empty `{parameter}`"))
1572        .at(span)
1573        .with_rule(
1574            "An empty separator or search string would match between every character, rather than answer the question the method asks.",
1575        )
1576        .with_help(help)
1577}
1578
1579/// Converts `value` to a [`MapKey`], or reports why it cannot be a map key or
1580/// set element.
1581fn to_map_key(method: &str, role: &str, value: &Value, span: Span) -> Result<MapKey, RuntimeError> {
1582    MapKey::from_value(value).map_err(|invalid| invalid_key_error(method, role, &invalid, span))
1583}
1584
1585/// Names the specific offending part when the invalid value is nested, such
1586/// as `` a `Vector` inside `Point.tags` ``, rather than blaming the whole
1587/// struct: the Language Card promises errors that teach the rule they name.
1588fn invalid_key_error(method: &str, role: &str, invalid: &InvalidKey, span: Span) -> RuntimeError {
1589    let message = if invalid.path.is_empty() {
1590        format!(
1591            "`{method}` cannot use a `{}` as a {role}",
1592            invalid.type_name
1593        )
1594    } else {
1595        format!(
1596            "`{method}` cannot use a `{}` inside `{}` as a {role}",
1597            invalid.type_name, invalid.path
1598        )
1599    };
1600    RuntimeError::new(message)
1601        .at(span)
1602        .with_rule(invalid.rule())
1603        .with_help(invalid.help())
1604}
1605
1606/// `Map.of` and `Set.of` reject a duplicate key or element rather than
1607/// silently keeping one entry, because a literal with two identical keys is a
1608/// mistake, not an intent.
1609fn duplicate_key_error(method: &str, role: &str, key: &MapKey, span: Span) -> RuntimeError {
1610    RuntimeError::new(format!("`{method}` was given the {role} `{key}` more than once"))
1611        .at(span)
1612        .with_rule(
1613            "A literal with two identical keys is a mistake, not an intent; duplicate keys are rejected rather than silently resolved by keeping the last one.",
1614        )
1615        .with_help(format!("remove the duplicate, or give it a different {role}"))
1616}
1617
1618/// `Map.of` takes `MapEntry` values, built with `MapEntry(key:, value:)`.
1619fn expects_map_entry(found: &Value, span: Span) -> RuntimeError {
1620    RuntimeError::new(format!(
1621        "`Map.of` expects `MapEntry` values, but found `{}`",
1622        found.type_name()
1623    ))
1624    .at(span)
1625    .with_rule(
1626        "`Map.of(entries: MapEntry<K, V>...)` takes values built with `MapEntry(key:, value:)`.",
1627    )
1628}
1629
1630/// `count()` was removed in favour of a single spelling.
1631fn count_is_spelled_length(type_name: &str, span: Span) -> RuntimeError {
1632    RuntimeError::new(format!(
1633        "`{type_name}` has no method `count`; Cove spells the number of elements `length()`"
1634    ))
1635    .at(span)
1636    .with_rule("Every sequence reports its element count as `length()`; there is no `count()`.")
1637    .with_help("write `length()` instead of `count()`")
1638}