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}