Skip to main content

polydat_core/kernel/
bind.rs

1// Copyright 2024-2026 Jonathan Shook
2// SPDX-License-Identifier: Apache-2.0
3
4//! Binding a child kernel under a parent, on any engine
5//! (native_scope_trees.md §5).
6//!
7//! A scope tree is built by binding each child under its parent: the
8//! parent's cells attached, its values copied in, the child's scope-init
9//! constants materialized. The wiring is over `dyn Kernel` on both sides,
10//! so a child of any engine binds under a parent of any engine, and a
11//! parent shared across threads may have children bound under it from
12//! several at once (`Kernel: Sync`).
13
14use std::sync::Arc;
15
16use crate::ast::Value;
17use crate::kernel::{Kernel, KernelProgram, PolydatKernel, WriteError};
18
19/// A kernel of `program` bound under `parent`: `iter_bindings` written
20/// first, so the child's own scope coordinates see them, then the
21/// parent's cells attached and its values copied in.
22///
23/// The kernel's [`Kernel::program_id`] is `program`'s, whichever engine
24/// `program` is on, so a host that sealed a plan of indices against the
25/// program can use it on every kernel bound from it.
26pub fn bind_under(
27    parent: &dyn Kernel,
28    program: Arc<dyn KernelProgram>,
29    iter_bindings: &[(String, Value)],
30) -> Result<Box<dyn Kernel>, crate::KernelError> {
31    let mut child = program.create_uninitialized();
32    for (var, value) in iter_bindings {
33        child
34            .set_input(var, value.clone())
35            .map_err(crate::KernelError::Write)?;
36    }
37    // Binding initializes the child once the parent's values are in.
38    PolydatKernel::wire_child_under(child.as_mut(), parent)?;
39    Ok(child)
40}
41
42/// Copy `parent`'s input values into `child`'s inputs of the same name:
43/// what carries a cascade of externs down a scope tree past what the
44/// child imports as outputs.
45///
46/// Coordinates are not copied; a host positions a child with
47/// `set_inputs`. An input with no value on the parent is skipped. A
48/// value the child's declared input refuses is an error naming both,
49/// not a skip (input_variance.md §7).
50pub fn propagate_inputs(parent: &dyn Kernel, child: &mut dyn Kernel) -> Result<(), WriteError> {
51    let child_coords = child.coord_count();
52    for (index, name) in parent.input_names().iter().enumerate() {
53        let Some(value) = parent.input_value_at(index) else {
54            continue;
55        };
56        if matches!(value, Value::None) {
57            continue;
58        }
59        let Some(child_index) = child.input_index(name) else {
60            continue;
61        };
62        // A coordinate is positioned with `set_inputs`; a cell-bound slot
63        // reads its cell, and writing it would publish into a register
64        // the whole scope shares.
65        if child_index < child_coords || child.input_is_cell_bound(child_index) {
66            continue;
67        }
68        child.set_input_at(child_index, value)?;
69    }
70    Ok(())
71}