1use crate::ast::{PortType, Value};
10use crate::kernel::{Construction, Dataflow, Metadata, PolydatKernel};
11
12impl Metadata for PolydatKernel {
13 #[inline]
14 fn find_input(&self, name: &str) -> Option<usize> {
15 self.program().find_input(name)
16 }
17
18 #[inline]
19 fn input_names(&self) -> Vec<String> {
20 self.program().input_names()
21 }
22
23 #[inline]
24 fn output_names(&self) -> Vec<String> {
25 self.program()
26 .output_names()
27 .iter()
28 .map(|s| s.to_string())
29 .collect()
30 }
31
32 #[inline]
33 fn coord_count(&self) -> usize {
34 self.program().coord_count()
35 }
36
37 #[inline]
38 fn input_port_type(&self, name: &str) -> Option<PortType> {
39 self.program().input_port_type(name)
40 }
41
42 #[inline]
43 fn input_port_type_by_idx(&self, idx: usize) -> Option<PortType> {
44 self.program().input_port_type_by_idx(idx)
45 }
46
47 #[inline]
48 fn output_port_type(&self, name: &str) -> Option<PortType> {
49 self.program().output_port_type(name)
50 }
51}
52
53impl Dataflow for PolydatKernel {
54 fn set_wire_idx(
55 &mut self,
56 idx: usize,
57 value: Value,
58 ) -> Result<(), crate::kernel::api::WriteError> {
59 use crate::kernel::api::WriteError;
60
61 let slot_type = match self.program().input_port_type_by_idx(idx) {
67 Some(t) => t,
68 None => {
69 return Err(WriteError::UnknownWire {
70 key: format!("wire[{idx}]"),
71 known: Vec::new(),
72 });
73 }
74 };
75
76 let slot_name = self
77 .program()
78 .input_name_by_idx(idx)
79 .map(|s| s.to_string())
80 .unwrap_or_else(|| format!("wire[{idx}]"));
81
82 let got = value.port_type();
92 let adapted = crate::kernel::state::adapt_boundary_value(&slot_name, slot_type, value);
93 if !adapted.satisfies_slot(slot_type) {
104 return Err(WriteError::TypeMismatch {
105 slot: slot_name,
106 expected: slot_type,
107 got,
108 });
109 }
110 self.state().set_input(idx, adapted);
111 Ok(())
112 }
113
114 #[inline]
115 fn get_wire_idx(&self, idx: usize) -> Value {
116 self.state_ref().get_input(idx)
117 }
118}
119
120impl Construction for PolydatKernel {
121 type Error = crate::kernel::subcontext::ContractViolation;
122
123 fn root(matter: crate::kernel::subcontext::PolydatMatter<'_>) -> Result<Self, Self::Error> {
124 use crate::kernel::subcontext::PolydatMatterInner;
125 match matter.inner {
126 PolydatMatterInner::Source(s) => {
127 let options = crate::dsl::compile::CompileOptions {
128 source_dir: s.options.workload_dir.clone(),
129 lib_paths: s.options.polydat_lib_paths,
130 required_outputs: s.options.required_outputs.clone(),
131 strict: s.options.strict,
132 context: s
133 .options
134 .context_label
135 .clone()
136 .unwrap_or_else(|| s.label.clone()),
137 cursor_limit: s.options.cursor_limit,
138 input_variance: s.options.input_variance,
139 inferred_externs: Vec::new(),
140 ledger: None,
141 engine: crate::Engine::default(),
142 };
143 crate::dsl::compile::compile_polydat_interpreter_with_options(
144 &s.body, &options, None,
145 )
146 .map_err(|e| crate::kernel::subcontext::ContractViolation::Compile(e.to_string()))
147 }
148 PolydatMatterInner::Statements(s) => {
149 let file = crate::dsl::ast::PolydatFile {
154 statements: s.statements,
155 };
156 let options = crate::dsl::compile::CompileOptions {
157 source_dir: s.options.workload_dir.clone(),
158 lib_paths: s.options.polydat_lib_paths,
159 required_outputs: s.options.required_outputs.clone(),
160 strict: s.options.strict,
161 context: s
162 .options
163 .context_label
164 .clone()
165 .unwrap_or_else(|| s.label.clone()),
166 cursor_limit: None,
167 input_variance: s.options.input_variance,
168 inferred_externs: Vec::new(),
169 ledger: None,
170 engine: crate::Engine::default(),
171 };
172 crate::dsl::compile::compile_ast_interpreter_with_options(&file, "", &options, None)
173 .map_err(|e| {
174 crate::kernel::subcontext::ContractViolation::Compile(e.to_string())
175 })
176 }
177 PolydatMatterInner::Program(p) => {
178 let mut k = PolydatKernel::from_program(p.program);
179 for (var, value) in p.iter_bindings {
180 if let Some(idx) = k.program().find_input(var) {
181 k.state().set_input(idx, value.clone());
182 }
183 }
184 Ok(k)
185 }
186 }
187 }
188
189 fn subscope(
190 &self,
191 matter: crate::kernel::subcontext::PolydatMatter<'_>,
192 ) -> Result<Self, Self::Error> {
193 PolydatKernel::build_subscope(self, matter)
195 }
196}
197
198impl crate::kernel::Kernel for PolydatKernel {
201 fn engine(&self) -> crate::compile::select::Engine {
202 crate::compile::select::Engine::Interpreter(self.program().cone_mode())
203 }
204 fn set_inputs(&mut self, coords: &[u64]) {
205 PolydatKernel::set_inputs(self, coords);
206 }
207 fn set_input(&mut self, name: &str, value: Value) -> Result<(), crate::kernel::WriteError> {
208 PolydatKernel::set_input(self, name, value)
209 }
210 fn set_cursor(
211 &mut self,
212 name: &str,
213 partition: &crate::iteration::cursor_partition::Partition,
214 ) -> Result<(), crate::kernel::WriteError> {
215 PolydatKernel::set_cursor(self, name, partition)
216 }
217 fn eval(&mut self) {
220 PolydatKernel::eval_read(self);
221 }
222 fn pull(&mut self, name: &str) -> Value {
223 PolydatKernel::pull_ref(self, name).clone()
224 }
225 fn input_names(&self) -> Vec<String> {
226 Metadata::input_names(self)
227 }
228 fn output_names(&self) -> Vec<String> {
229 Metadata::output_names(self)
230 }
231 fn output_type(&self, name: &str) -> Option<PortType> {
232 Metadata::output_port_type(self, name)
233 }
234 fn externs(&self) -> Vec<(String, PortType)> {
235 let program = self.program();
236 Metadata::input_names(self)
237 .into_iter()
238 .enumerate()
239 .filter(|(i, _)| program.input_kind(*i) != Some(crate::kernel::InputKind::Coordinate))
240 .filter_map(|(i, name)| Metadata::input_port_type_by_idx(self, i).map(|t| (name, t)))
241 .collect()
242 }
243 fn cursor_schemas(&self) -> &[crate::iteration::source::SourceSchema] {
244 self.program().cursor_schemas()
245 }
246 fn input_value(&self, name: &str) -> Option<Value> {
247 let idx = self.program().find_input(name)?;
248 Some(self.state_ref().get_input(idx))
249 }
250 fn input_index(&self, name: &str) -> Option<usize> {
251 self.program().find_input(name)
252 }
253 fn set_input_at(
254 &mut self,
255 index: usize,
256 value: Value,
257 ) -> Result<(), crate::kernel::WriteError> {
258 PolydatKernel::set_input_at(self, index, value)
259 }
260 fn output_index(&self, name: &str) -> Option<usize> {
261 self.program().output_index(name)
262 }
263 fn const_inits(&self) -> &[crate::kernel::ConstInit] {
264 self.program().const_inits()
265 }
266 fn init_input_at(
267 &mut self,
268 index: usize,
269 value: Value,
270 ) -> Result<(), crate::kernel::WriteError> {
271 PolydatKernel::init_input_at(self, index, value)
272 }
273 fn pull_at(&mut self, index: usize) -> Value {
274 PolydatKernel::pull_ref_at(self, index).clone()
275 }
276 fn traversals(&self) -> &[crate::dsl::traversal::Traversal] {
277 self.program().traversals()
278 }
279 fn plan(&self) -> crate::EnginePlan {
280 self.program().engine_plan()
281 }
282 fn traverse(&mut self, index: usize) -> Result<crate::kernel::TraversalStream, String> {
283 PolydatKernel::traverse(self, index)
284 }
285 fn invalidate_all(&mut self) {
286 self.state().invalidate_all();
287 }
288 fn shared_cells(&self) -> Vec<crate::kernel::SharedCellEntry> {
289 self.shared_cells_in_scope()
290 }
291 fn output_cell(&self, name: &str) -> Option<crate::kernel::SharedCell> {
292 self.state_ref().core.output_cell(self.program(), name)
294 }
295 fn output_modifier(&self, name: &str) -> crate::dsl::ast::BindingModifier {
296 self.program().output_modifier(name)
297 }
298 fn cells_in_scope(&self) -> Vec<crate::kernel::SharedCellEntry> {
299 self.shared_cells_in_scope()
302 }
303 fn set_transit_cells(&mut self, cells: Vec<crate::kernel::SharedCellEntry>) {
304 self.replace_transit_cells(cells);
305 }
306 fn scope_coordinates(&self) -> &[crate::kernel::ScopeCoord] {
307 PolydatKernel::scope_coordinates(self)
308 }
309 fn extend_scope_coordinates(&mut self, outer: &[crate::kernel::ScopeCoord]) {
310 PolydatKernel::extend_scope_coordinates(self, outer);
311 }
312 fn input_port_type(&self, name: &str) -> Option<crate::ast::PortType> {
313 self.program().input_port_type(name)
314 }
315 fn bind_input_cell(&mut self, name: &str, cell: crate::kernel::SharedCell) -> bool {
316 let Some(idx) = self.program().find_input(name) else {
321 return false;
322 };
323 self.state().attach_shared_cell(idx, cell);
324 true
325 }
326 fn attach_shared_cell(
327 &mut self,
328 name: &str,
329 cell: crate::kernel::SharedCell,
330 ) -> Result<(), String> {
331 let program = self.program().clone();
332 let shared = program.shared_outputs();
333 let idx = program.find_input(name).filter(|_| shared.contains(&name));
334 let Some(idx) = idx else {
335 return Err(format!(
336 "no `shared` binding named '{name}'; this kernel's shared bindings are {shared:?}"
337 ));
338 };
339 self.state().attach_shared_cell(idx, cell);
340 Ok(())
341 }
342 fn into_program(self: Box<Self>) -> std::sync::Arc<dyn crate::kernel::KernelProgram> {
343 PolydatKernel::into_program(*self)
344 }
345 fn ledger(&self) -> &std::sync::Arc<crate::kernel::CompileLedger> {
346 self.program().ledger()
347 }
348 fn coord_count(&self) -> usize {
349 self.program().coord_count()
350 }
351 fn input_value_at(&self, index: usize) -> Option<Value> {
352 (index < self.state_ref().core.inputs.len())
353 .then(|| self.state_ref().read_input_value(index))
354 }
355 fn input_default_at(&self, index: usize) -> Option<Value> {
356 self.program().input_default_by_idx(index).cloned()
357 }
358 fn input_is_cell_bound(&self, index: usize) -> bool {
359 self.state_ref().shared_cell(index).is_some()
360 }
361 fn reset_inputs(&mut self) {
362 let from = self.program().coord_count();
363 self.state().reset_inputs_from(from);
364 }
365 fn fork(&self) -> Box<dyn crate::kernel::Kernel> {
366 Box::new(self.fork_kernel())
367 }
368 fn publish_broadcasts(&mut self) {
369 self.advance_broadcasts();
370 }
371 fn commit_write_throughs(&mut self) -> Result<(), String> {
372 PolydatKernel::commit_write_throughs(self)
373 }
374 fn program_id(&self) -> crate::kernel::ProgramId {
375 crate::kernel::ProgramId(std::sync::Arc::as_ptr(self.program()) as *const () as usize)
376 }
377 fn input_type_origin(&self, name: &str) -> Option<crate::kernel::TypeOrigin> {
378 self.program().input_type_origin(name)
379 }
380}
381
382impl crate::kernel::KernelInternals for PolydatKernel {
383 fn set_write_throughs(&mut self, pairs: Vec<(String, String)>) {
384 PolydatKernel::set_write_throughs(
385 self,
386 pairs
387 .into_iter()
388 .map(
389 |(export_name, source_output)| crate::kernel::KernelWriteThrough {
390 export_name,
391 source_output,
392 },
393 )
394 .collect(),
395 );
396 }
397 fn set_traversals(
398 &mut self,
399 traversals: Vec<crate::dsl::traversal::Traversal>,
400 producers: Vec<crate::dsl::traversal::Producer>,
401 ) {
402 PolydatKernel::set_traversals(self, traversals, producers);
403 }
404 fn folded_value(&self, name: &str) -> Option<Value> {
405 self.get_constant(name).cloned()
406 }
407 fn set_cursor_extent(&mut self, index: usize, extent: u64) {
408 let mut schemas = self.program().cursor_schemas().to_vec();
409 if let Some(schema) = schemas.get_mut(index) {
410 schema.extent = Some(extent);
411 self.set_cursor_schemas(schemas);
412 }
413 }
414}
415
416impl crate::kernel::KernelProgram for crate::kernel::PolydatProgram {
417 fn engine(&self) -> crate::compile::select::Engine {
418 crate::compile::select::Engine::Interpreter(self.cone_mode())
419 }
420 fn as_interpreter(self: std::sync::Arc<Self>) -> Option<std::sync::Arc<Self>> {
421 Some(self)
422 }
423 fn create_uninitialized(self: std::sync::Arc<Self>) -> Box<dyn crate::kernel::Kernel> {
424 Box::new(PolydatKernel::from_program(self))
425 }
426 fn ledger(&self) -> &std::sync::Arc<crate::kernel::CompileLedger> {
427 crate::kernel::PolydatProgram::ledger(self)
428 }
429 fn program_id(&self) -> crate::kernel::ProgramId {
430 crate::kernel::ProgramId(self as *const Self as *const () as usize)
431 }
432}
433
434#[cfg(test)]
435#[allow(deprecated)]
437mod tests {
438 use super::*;
439 use crate::dsl::compile::compile_polydat_interpreter;
440
441 #[test]
443 fn dataflow_indexed_set_get() {
444 let mut k = compile_polydat_interpreter("input cycle: u64\nconst x := 7\n").unwrap();
445 k.set_wire(0_usize, Value::U64(42)).expect("typed write");
447 assert_eq!(k.get_wire(0_usize), Some(Value::U64(42)));
448 }
449
450 #[test]
452 fn dataflow_named_set_get() {
453 let mut k = compile_polydat_interpreter("input cycle: u64\nextern n: u64\n").unwrap();
454 k.set_wire("n", Value::U64(5)).expect("typed write");
455 match k.get_wire("n") {
456 Some(Value::U64(5)) => {}
457 other => panic!("expected U64(5), got {other:?}"),
458 }
459 }
460
461 #[test]
463 fn dataflow_string_key() {
464 let mut k = compile_polydat_interpreter("input cycle: u64\nextern n: u64\n").unwrap();
465 let name = String::from("n");
466 k.set_wire(&name, Value::U64(99)).expect("typed write");
467 assert_eq!(k.get_wire(name.clone()), Some(Value::U64(99)));
468 }
469
470 #[test]
472 fn dataflow_unknown_name_safe() {
473 let mut k = compile_polydat_interpreter("input cycle: u64\n").unwrap();
474 let err = k.set_wire("nonexistent", Value::U64(1)).unwrap_err();
475 assert!(matches!(
476 err,
477 crate::kernel::api::WriteError::UnknownWire { .. }
478 ));
479 assert!(k.get_wire("nonexistent").is_none());
480 }
481
482 #[test]
491 fn dataflow_type_mismatch_rejected() {
492 let mut k = compile_polydat_interpreter("input cycle: u64\nextern n: u64\n").unwrap();
493 let err = k
494 .set_wire(
495 "n",
496 Value::VecF32(crate::ast::SliceArc::from_vec(vec![1.0_f32, 2.0])),
497 )
498 .unwrap_err();
499 match err {
500 crate::kernel::api::WriteError::TypeMismatch {
501 slot,
502 expected,
503 got,
504 } => {
505 assert_eq!(slot, "n");
506 assert_eq!(expected, PortType::U64);
507 assert_eq!(got, PortType::VecF32);
508 }
509 other => panic!("expected TypeMismatch, got {other:?}"),
510 }
511 }
512
513 #[test]
518 fn vec_to_scalar_diagnostic_mentions_explicit_helpers() {
519 let err = crate::kernel::api::WriteError::TypeMismatch {
520 slot: "score".into(),
521 expected: PortType::F64,
522 got: PortType::VecF32,
523 };
524 let msg = err.to_string();
525 assert!(
526 msg.contains("reduction node"),
527 "missing reduction hint: {msg}"
528 );
529 assert!(msg.contains("vec_dot"), "missing vec_dot hint: {msg}");
530 }
531
532 #[test]
535 fn dataflow_healable_mismatch_adapts() {
536 let mut k = compile_polydat_interpreter("input cycle: u64\nextern x: f64\n").unwrap();
537 k.set_wire("x", Value::U64(42))
540 .expect("u64→f64 boundary adapter");
541 match k.get_wire("x") {
542 Some(Value::F64(42.0)) => {}
543 other => panic!("expected adapted F64(42.0), got {other:?}"),
544 }
545 }
546
547 #[test]
551 fn dataflow_none_passes_through_any_slot() {
552 let mut k = compile_polydat_interpreter("input cycle: u64\nextern n: u64\n").unwrap();
553 k.set_wire("n", Value::None).expect("None always permitted");
554 }
555
556 #[test]
558 fn metadata_listings() {
559 let k = compile_polydat_interpreter(
560 "input (cycle: u64, thread: u64)\nextern n: u64\nconst x := 7\n",
561 )
562 .unwrap();
563 let inputs: Vec<String> = k.input_names();
564 assert!(inputs.iter().any(|s| s == "cycle"));
565 assert!(inputs.iter().any(|s| s == "n"));
566 assert_eq!(k.coord_count(), 2); assert!(k.find_input("n").is_some());
568 assert_eq!(k.input_port_type("n"), Some(PortType::U64));
569 }
570
571 #[test]
575 fn construction_symmetric_paths() {
576 let root_opts = crate::kernel::subcontext::CompileOptions {
577 workload_dir: None,
578 polydat_lib_paths: Vec::new(),
579 strict: false,
580 required_outputs: Vec::new(),
581 context_label: Some("root".to_string()),
582 cursor_limit: None,
583 ..Default::default()
584 };
585 let root_matter = crate::kernel::subcontext::PolydatMatter::builder()
586 .label("root")
587 .source("input cycle: u64\nshared flag := 0\n")
588 .options(root_opts)
589 .build()
590 .expect("matter build");
591 let root =
592 <PolydatKernel as Construction>::root(root_matter).expect("root from source matter");
593
594 let sub_opts = crate::kernel::subcontext::CompileOptions {
595 workload_dir: None,
596 polydat_lib_paths: Vec::new(),
597 strict: false,
598 required_outputs: Vec::new(),
599 context_label: Some("sub".to_string()),
600 cursor_limit: None,
601 ..Default::default()
602 };
603 let sub_matter = crate::kernel::subcontext::PolydatMatter::builder()
604 .label("sub")
605 .source("input cycle: u64\n")
606 .options(sub_opts)
607 .build()
608 .expect("matter build");
609 let _sub = root
610 .subscope(sub_matter)
611 .expect("subscope from source matter");
612 }
613
614 #[test]
618 fn construction_root_from_program() {
619 let template = compile_polydat_interpreter("input cycle: u64\nextern n: u64\n").unwrap();
620 let program = template.program().clone();
621 let matter = crate::kernel::subcontext::PolydatMatter::builder()
622 .program(program)
623 .build()
624 .expect("matter build");
625 let mut root =
626 <PolydatKernel as Construction>::root(matter).expect("root from program matter");
627 root.set_wire("n", Value::U64(13)).expect("set_wire");
628 assert_eq!(root.get_wire("n"), Some(Value::U64(13)));
629 }
630
631 #[test]
633 fn builder_rejects_multiple_forms() {
634 let template = compile_polydat_interpreter("input cycle: u64\n").unwrap();
635 match crate::kernel::subcontext::PolydatMatter::builder()
636 .source("input cycle: u64\n")
637 .program(template.program().clone())
638 .build()
639 {
640 Err(msg) => assert!(
641 msg.contains("multiple"),
642 "expected multiple-forms error, got: {msg}"
643 ),
644 Ok(_) => panic!("multiple forms must error"),
645 }
646 }
647
648 #[test]
650 fn builder_rejects_empty() {
651 match crate::kernel::subcontext::PolydatMatter::builder().build() {
652 Err(msg) => assert!(
653 msg.contains("no input form"),
654 "expected no-form error, got: {msg}"
655 ),
656 Ok(_) => panic!("empty matter must error"),
657 }
658 }
659}