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>, WriteError> {
31    let mut child = program.create_kernel();
32    for (var, value) in iter_bindings {
33        child.set_input(var, value.clone())?;
34    }
35    PolydatKernel::wire_child_under(child.as_mut(), parent);
36    Ok(child)
37}
38
39/// Copy `parent`'s input values into `child`'s inputs of the same name:
40/// what carries a cascade of externs down a scope tree past what the
41/// child imports as outputs.
42///
43/// Coordinates are not copied; a host positions a child with
44/// `set_inputs`. An input with no value on the parent is skipped. A
45/// value the child's declared input refuses is an error naming both,
46/// not a skip (input_variance.md §7).
47pub fn propagate_inputs(parent: &dyn Kernel, child: &mut dyn Kernel) -> Result<(), WriteError> {
48    let child_coords = child.coord_count();
49    for (index, name) in parent.input_names().iter().enumerate() {
50        let Some(value) = parent.input_value_at(index) else {
51            continue;
52        };
53        if matches!(value, Value::None) {
54            continue;
55        }
56        let Some(child_index) = child.input_index(name) else {
57            continue;
58        };
59        // A coordinate is positioned with `set_inputs`; a cell-bound slot
60        // reads its cell, and writing it would publish into a register
61        // the whole scope shares.
62        if child_index < child_coords || child.input_is_cell_bound(child_index) {
63            continue;
64        }
65        child.set_input_at(child_index, value)?;
66    }
67    Ok(())
68}