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}