adam_lang/composition.rs
1//! Builds complete-shape templates without parsing source text.
2//!
3//! Emitters return fresh segments; they never execute expressions during definition
4//! checking. Each live factory creates fresh mutable execution frames. Immutable
5//! definitions and intentionally shared host inputs may be captured by emitters.
6//!
7//! ```
8//! use adam_lang::{CompositionBuilder, ParsedSheet, TypeRegistry};
9//! use adam_lang::type_registry::TypeShape;
10//! use std::any::TypeId;
11//! let types = TypeRegistry::new();
12//! let shape = TypeShape::Named(TypeId::of::<i32>());
13//! let mut builder = CompositionBuilder::new(types.clone());
14//! builder.parameter("x", shape.clone())?;
15//! let template = builder.finish()?;
16//! let mut destination = ParsedSheet::new();
17//! let x = destination.add_cell(3i32);
18//! destination.register_cell("x", x, shape, &types)?;
19//! template.instantiate(&mut destination, &[("x", x)])?;
20//! # Ok::<(), adam_lang::ModuleDiagnostic>(())
21//! ```
22
23use std::any::{Any, TypeId};
24use std::cell::RefCell;
25use std::collections::{HashMap, HashSet};
26use std::rc::Rc;
27
28use adam_rs::{
29 CellId, Instance, MatchExpr, Method, OccurrenceId, RelationshipSlot, SheetTemplate,
30 TemplateBuilder, TemplateId, TemplateInclusion,
31};
32use cel_runtime::DynSegment;
33
34use crate::cell_value::{CellValue, Frame};
35use crate::type_registry::TypeShape;
36use crate::{ModuleDiagnostic, ParsedSheet, SourceLabel, TypeRegistry};
37
38/// Retains a native local handle and its complete declared shape.
39///
40/// See the [module example](self).
41#[derive(Clone, Debug, PartialEq, Eq)]
42pub struct CellSlot {
43 native: adam_rs::CellSlot,
44 shape: TypeShape,
45}
46
47impl CellSlot {
48 /// Borrows the owner-checked native handle for occurrence inspection.
49 ///
50 /// # Examples
51 /// ```
52 /// let mut builder = adam_lang::CompositionBuilder::new(adam_lang::TypeRegistry::new());
53 /// let slot = builder.parameter("x", adam_lang::type_registry::TypeShape::Tuple(vec![]))?;
54 /// assert_eq!(slot.native(), slot.native());
55 /// # Ok::<(), adam_lang::ModuleDiagnostic>(())
56 /// ```
57 pub fn native(&self) -> &adam_rs::CellSlot {
58 &self.native
59 }
60
61 /// Borrows the complete shape, independently of native storage identity.
62 ///
63 /// # Examples
64 /// See [`Self::native`].
65 pub fn shape(&self) -> &TypeShape {
66 &self.shape
67 }
68}
69
70/// Owns a repeatable native method with full input and output shapes.
71///
72/// One output receives the whole result; multiple outputs destructure a tuple.
73/// See [`CompositionBuilder::method`].
74pub struct MethodRecipe {
75 native: adam_rs::MethodRecipe,
76 inputs: Vec<TypeShape>,
77 outputs: Vec<TypeShape>,
78}
79
80impl MethodRecipe {
81 /// Borrows the positional input shapes.
82 ///
83 /// # Examples
84 /// See [`CompositionBuilder::method`].
85 pub fn input_shapes(&self) -> &[TypeShape] {
86 &self.inputs
87 }
88
89 /// Borrows the positional output shapes.
90 ///
91 /// # Examples
92 /// See [`CompositionBuilder::method`].
93 pub fn output_shapes(&self) -> &[TypeShape] {
94 &self.outputs
95 }
96}
97
98/// Retains one occurrence using parent links, never copied full invocation paths.
99///
100/// Identities retain no recipe edges. See [`TypedTemplate::instantiate`].
101#[derive(Clone, Debug)]
102pub struct OccurrenceOrigin {
103 /// Identifies this live occurrence.
104 pub id: OccurrenceId,
105 /// Identifies its immediate parent, or `None` for a root invocation.
106 pub parent: Option<OccurrenceId>,
107 /// Identifies its immutable definition.
108 pub template: TemplateId,
109 /// Identifies the local inclusion group in the parent.
110 pub inclusion_site: Option<RelationshipSlot>,
111 /// Retains a real definition location, or a span-free native label.
112 pub label: SourceLabel,
113 /// Retains authored local metadata, independent of definition owners.
114 pub authored: Rc<[AuthoredItem]>,
115 /// Maps authored conditional ordinals to their live execution identities.
116 pub conditionals: Vec<adam_rs::ConditionalId>,
117}
118
119impl OccurrenceOrigin {
120 /// Borrows authored items, including original method ordinals and invocation sites.
121 ///
122 /// # Examples
123 /// See [`TypedTemplate::instantiate`].
124 pub fn authored_items(&self) -> &[AuthoredItem] {
125 &self.authored
126 }
127}
128
129/// Retains one authored node and its native slots without expanding included definitions.
130///
131/// Parent indices refer only to this definition's authored item slice.
132/// See [`OccurrenceOrigin::authored_items`].
133#[derive(Clone, Debug)]
134pub struct AuthoredItem {
135 /// Distinguishes declaration, method, policy and invocation roles.
136 pub role: AuthoredRole,
137 /// Identifies the immediate authored parent in the same definition.
138 pub parent: Option<usize>,
139 /// Retains the exact authored source owner and span.
140 pub label: SourceLabel,
141 /// Identifies the declared or governed local cell, where applicable.
142 pub cell: Option<adam_rs::CellSlot>,
143 /// Identifies the native relationship or inclusion group, where applicable.
144 pub relationship: Option<RelationshipSlot>,
145 /// Identifies an authored method before whole-method source specialization.
146 pub method_index: Option<usize>,
147 /// Retains an explicit requirement name; unnamed requirements have no name.
148 pub requirement_name: Option<String>,
149 /// Retains the exact expression site independently of its enclosing authored item.
150 pub expression: Option<SourceLabel>,
151}
152
153/// Classifies a local authored record without guessing from label text or output count.
154///
155/// See [`AuthoredItem`].
156#[derive(Clone, Copy, Debug, Eq, PartialEq)]
157pub enum AuthoredRole {
158 /// Binds a caller cell.
159 Parameter,
160 /// Allocates a private ordinary cell.
161 Cell,
162 /// Allocates a private source.
163 Source,
164 /// Allocates a private output and its sole writer.
165 Out,
166 /// Attaches a private-cell filter.
167 Filter,
168 /// Attaches a private-cell numeric-range filter.
169 RangeFilter,
170 /// Attaches a private-cell requirement.
171 Requirement,
172 /// Registers an ordinary relationship.
173 Relationship,
174 /// Declares an authored alternative direction.
175 Method,
176 /// Includes a reusable definition.
177 Use,
178 /// Selects native relationship groups.
179 Conditional,
180 /// Declares a conditional branch or default.
181 Branch,
182}
183
184/// Retains one direct invocation's flat occurrence records after template drop.
185///
186/// See [`TypedTemplate::instantiate`].
187#[derive(Clone, Debug)]
188pub struct InstanceOrigin {
189 /// Preserves depth-first native occurrence order.
190 pub occurrences: Vec<OccurrenceOrigin>,
191}
192
193/// Maps one local declaration or bound formal to its live occurrence.
194///
195/// A live boundary cell can retain origins from several occurrences.
196/// See [`TypedTemplate::instantiate`].
197#[derive(Clone, Debug)]
198pub struct CellOrigin {
199 /// Identifies the occurrence containing this declaration or formal.
200 pub occurrence: OccurrenceId,
201 /// Identifies the local cell within that occurrence's definition.
202 pub slot: adam_rs::CellSlot,
203 /// Retains the formal or private declaration's exact source label.
204 pub label: SourceLabel,
205}
206
207/// Selects an authored declaration using exact native slots and original ordinals.
208///
209/// - Complexity: O(a) authored records, with O(1) additional space.
210fn construction_item<'a>(
211 authored: &'a [AuthoredItem],
212 site: &adam_rs::template::ConstructionSite,
213) -> Option<&'a AuthoredItem> {
214 use adam_rs::template::ConstructionSite;
215 match site {
216 ConstructionSite::Cell(cell) => authored.iter().find(|item| {
217 item.cell.as_ref() == Some(cell)
218 && matches!(
219 item.role,
220 AuthoredRole::Cell | AuthoredRole::Source | AuthoredRole::Out
221 )
222 }),
223 ConstructionSite::Policy { cell, index } => authored
224 .iter()
225 .filter(|item| {
226 item.cell.as_ref() == Some(cell)
227 && matches!(
228 item.role,
229 AuthoredRole::Filter
230 | AuthoredRole::RangeFilter
231 | AuthoredRole::Requirement
232 )
233 })
234 .nth(*index),
235 ConstructionSite::Method {
236 relationship,
237 index,
238 } => authored.iter().find(|item| {
239 item.relationship.as_ref() == Some(relationship)
240 && (item.method_index == Some(*index) || item.role == AuthoredRole::Out)
241 }),
242 ConstructionSite::Relationship(relationship) => authored.iter().find(|item| {
243 item.relationship.as_ref() == Some(relationship)
244 && matches!(item.role, AuthoredRole::Relationship | AuthoredRole::Out)
245 }),
246 }
247}
248
249/// Borrows the original CEL span from a construction cause, without erasing its type.
250///
251/// - Complexity: O(h) nested construction envelopes.
252fn construction_span(error: &adam_rs::TemplateError) -> Option<cel_parser::SourceSpan> {
253 match error {
254 adam_rs::TemplateError::Factory { source, .. }
255 | adam_rs::TemplateError::ConditionalFactory { source }
256 | adam_rs::TemplateError::Runtime(adam_rs::Error::MethodFailed { error: source, .. }) => {
257 source
258 .downcast_ref::<cel_parser::SpanContext>()
259 .map(cel_parser::SpanContext::span)
260 }
261 adam_rs::TemplateError::Construction { source, .. }
262 | adam_rs::TemplateError::ConditionalConstruction { source, .. } => {
263 construction_span(source)
264 }
265 _ => None,
266 }
267}
268
269#[cfg(test)]
270mod diagnostic_tests {
271 use super::*;
272 use adam_rs::template::ConstructionSite;
273
274 /// Builds one source-free authored record with exact native identities.
275 fn item(
276 role: AuthoredRole,
277 cell: &adam_rs::CellSlot,
278 relationship: &RelationshipSlot,
279 method_index: Option<usize>,
280 ) -> AuthoredItem {
281 AuthoredItem {
282 role,
283 parent: None,
284 label: SourceLabel {
285 location: None,
286 label: format!("{role:?}"),
287 },
288 cell: Some(cell.clone()),
289 relationship: Some(relationship.clone()),
290 method_index,
291 requirement_name: None,
292 expression: None,
293 }
294 }
295
296 /// Selects native construction slots, cell-local policy ordinals and authored method gaps.
297 #[test]
298 fn construction_items_use_exact_identity_and_authored_ordinals() {
299 let mut builder = TemplateBuilder::new();
300 let cell = builder.cell("value", || Ok(1i32)).unwrap();
301 let relationship = builder
302 .relationship(vec![
303 adam_rs::MethodRecipe::new(&[], std::slice::from_ref(&cell), |_, _| unreachable!())
304 .unwrap(),
305 ])
306 .unwrap();
307 let authored = [
308 item(AuthoredRole::Cell, &cell, &relationship, None),
309 item(AuthoredRole::Filter, &cell, &relationship, None),
310 item(AuthoredRole::Requirement, &cell, &relationship, None),
311 item(AuthoredRole::Relationship, &cell, &relationship, None),
312 item(AuthoredRole::Method, &cell, &relationship, Some(3)),
313 ];
314 let cases = [
315 (ConstructionSite::Cell(cell.clone()), 0),
316 (
317 ConstructionSite::Policy {
318 cell: cell.clone(),
319 index: 1,
320 },
321 2,
322 ),
323 (ConstructionSite::Relationship(relationship.clone()), 3),
324 (
325 ConstructionSite::Method {
326 relationship: relationship.clone(),
327 index: 3,
328 },
329 4,
330 ),
331 ];
332 for (site, index) in cases {
333 assert!(std::ptr::eq(
334 construction_item(&authored, &site).unwrap(),
335 &authored[index]
336 ));
337 }
338 assert!(
339 construction_item(
340 &authored,
341 &ConstructionSite::Method {
342 relationship,
343 index: 0
344 }
345 )
346 .is_none()
347 );
348 let foreign = TemplateBuilder::new().cell("foreign", || Ok(1i32)).unwrap();
349 assert!(construction_item(&authored, &ConstructionSite::Cell(foreign)).is_none());
350 }
351
352 /// Keeps exact CEL coordinates optional while leaving the original typed error intact.
353 #[test]
354 fn construction_span_retains_typed_causes() {
355 let slot = TemplateBuilder::new().cell("value", || Ok(1i32)).unwrap();
356 let error = adam_rs::TemplateError::Factory {
357 slot: slot.clone(),
358 source: std::io::Error::other("native").into(),
359 };
360 assert!(construction_span(&error).is_none());
361 let span = cel_parser::SourceSpan::new(2, 4, 2, 9);
362 let error = adam_rs::TemplateError::Factory {
363 slot,
364 source: anyhow::Error::new(std::io::Error::other("division"))
365 .context(cel_parser::SpanContext::new(span)),
366 };
367 assert_eq!(construction_span(&error), Some(span));
368 let adam_rs::TemplateError::Factory { source, .. } = error else {
369 unreachable!()
370 };
371 assert!(source.downcast_ref::<std::io::Error>().is_some());
372 let error = adam_rs::TemplateError::Runtime(adam_rs::Error::MethodFailed {
373 error: source,
374 sites: vec![],
375 });
376 assert_eq!(construction_span(&error), Some(span));
377 }
378}
379
380/// Constructs fresh type-erased initial values for monomorphized native adapters.
381type Initializer = Box<dyn Fn() -> anyhow::Result<Box<dyn Any>>>;
382
383/// Selects native constructors for one registered concrete storage type.
384#[derive(Clone, Copy)]
385pub(crate) struct LeafAdapters {
386 parameter: fn(&mut TemplateBuilder, &str) -> Result<adam_rs::CellSlot, adam_rs::TemplateError>,
387 cell: fn(
388 &mut TemplateBuilder,
389 &str,
390 Initializer,
391 bool,
392 ) -> Result<adam_rs::CellSlot, adam_rs::TemplateError>,
393}
394
395impl LeafAdapters {
396 /// Selects monomorphized constructors without a fixed list of host types.
397 pub(crate) fn of<T: Any + PartialEq>() -> Self {
398 Self {
399 parameter: TemplateBuilder::parameter::<T>,
400 cell: add_private::<T>,
401 }
402 }
403}
404
405/// Adds a private concrete cell or source using the canonical frame's output.
406///
407/// # Errors
408/// Rejects duplicate names or native recipe errors; initializer errors occur later.
409/// - Complexity: Amortized O(l) name bytes, excluding later initializer costs.
410fn add_private<T: Any + PartialEq>(
411 builder: &mut TemplateBuilder,
412 name: &str,
413 initial: Initializer,
414 source: bool,
415) -> Result<adam_rs::CellSlot, adam_rs::TemplateError> {
416 let make = move || {
417 initial()?
418 .downcast::<T>()
419 .map(|value| *value)
420 .map_err(|_| anyhow::anyhow!("initializer storage mismatch"))
421 };
422 if source {
423 builder.source(name, make)
424 } else {
425 builder.cell(name, make)
426 }
427}
428
429/// Retains only a definition's local shapes and compact child metadata edges.
430struct Metadata {
431 id: TemplateId,
432 cells: HashMap<adam_rs::CellSlot, TypeShape>,
433 formals: HashMap<String, CellSlot>,
434 children: Vec<Rc<Metadata>>,
435 label: SourceLabel,
436 authored: Rc<[AuthoredItem]>,
437 cell_labels: HashMap<adam_rs::CellSlot, SourceLabel>,
438 requirements: HashMap<adam_rs::CellSlot, Vec<Option<String>>>,
439}
440
441impl Drop for Metadata {
442 /// Releases last-owned child metadata iteratively, including deep include chains.
443 ///
444 /// - Complexity: O(n) released metadata and O(w) pending child edges.
445 fn drop(&mut self) {
446 let mut pending = std::mem::take(&mut self.children);
447 while let Some(child) = pending.pop() {
448 if let Ok(mut child) = Rc::try_unwrap(child) {
449 pending.append(&mut child.children);
450 }
451 }
452 }
453}
454
455/// Builds shape-checked recipes by delegating every graph operation to Adam.
456///
457/// Fallible boundaries reject unregistered shapes before constructing adapters.
458/// Factories must return fresh segments and fresh segment-local mutable owners.
459/// Emitters run during checking and instance construction, but their expressions
460/// run only during initialization or ordinary native execution.
461/// See the [module example](self).
462pub struct CompositionBuilder {
463 native: TemplateBuilder,
464 types: Rc<TypeRegistry>,
465 cells: HashMap<adam_rs::CellSlot, TypeShape>,
466 formals: HashMap<String, CellSlot>,
467 children: Vec<Rc<Metadata>>,
468 label: SourceLabel,
469 authored: Vec<AuthoredItem>,
470}
471
472impl CompositionBuilder {
473 /// Owns a registry and starts an empty source-free recipe.
474 ///
475 /// # Examples
476 /// See the [module example](self).
477 pub fn new(types: TypeRegistry) -> Self {
478 Self::with_types(Rc::new(types))
479 }
480
481 /// Shares only the immutable compiler registry, never construction state.
482 pub(crate) fn with_types(types: Rc<TypeRegistry>) -> Self {
483 Self {
484 native: TemplateBuilder::new(),
485 types,
486 cells: HashMap::new(),
487 formals: HashMap::new(),
488 children: Vec::new(),
489 label: SourceLabel {
490 location: None,
491 label: "native template".into(),
492 },
493 authored: Vec::new(),
494 }
495 }
496
497 /// Retains flat authored metadata after native slots have been assigned.
498 ///
499 /// - Precondition: Native handles belong to this builder; parents precede children.
500 /// - Complexity: O(n) item storage transfer.
501 pub(crate) fn set_authored(&mut self, authored: Vec<AuthoredItem>) {
502 debug_assert!(
503 authored
504 .iter()
505 .enumerate()
506 .all(|(i, a)| a.parent.is_none_or(|p| p < i))
507 );
508 self.authored = authored;
509 }
510
511 /// Retains a definition label without synthesizing source locations.
512 ///
513 /// # Examples
514 /// ```
515 /// let mut builder = adam_lang::CompositionBuilder::new(adam_lang::TypeRegistry::new());
516 /// builder.set_label(adam_lang::SourceLabel { location: None, label: "host".into() });
517 /// ```
518 pub fn set_label(&mut self, label: SourceLabel) {
519 self.label = label;
520 }
521
522 /// Declares a named parameter with its complete concrete shape.
523 ///
524 /// # Errors
525 /// Rejects unregistered shapes and native declaration errors without mutation.
526 /// - Complexity: Expected amortized O(n + l) shape nodes and name bytes.
527 ///
528 /// # Examples
529 /// See the [module example](self).
530 pub fn parameter(
531 &mut self,
532 name: &str,
533 shape: TypeShape,
534 ) -> Result<CellSlot, ModuleDiagnostic> {
535 let adapters = self.adapters(&shape)?;
536 let native = (adapters.parameter)(&mut self.native, name).map_err(diagnostic)?;
537 let slot = self.record(native, shape);
538 self.formals.insert(name.into(), slot.clone());
539 Ok(slot)
540 }
541
542 /// Declares a fresh private ordinary cell initialized at instantiation.
543 ///
544 /// # Errors
545 /// Rejects unknown shapes, incompatible emitted signatures, missing capabilities,
546 /// or native declaration errors. Initializer execution errors occur at instantiation.
547 /// - Complexity: O(n + e + l) shape nodes, emitted reads and name bytes,
548 /// excluding emitter costs.
549 ///
550 /// # Examples
551 /// ```
552 /// use adam_lang::{CompositionBuilder, TypeRegistry};
553 /// use adam_lang::type_registry::TypeShape;
554 /// let mut builder = CompositionBuilder::new(TypeRegistry::new());
555 /// builder.cell("x", TypeShape::Named(std::any::TypeId::of::<i32>()), || {
556 /// let mut body = cel_runtime::DynSegment::new::<()>();
557 /// body.op0(|| 1i32);
558 /// Ok(body)
559 /// })?;
560 /// # Ok::<(), adam_lang::ModuleDiagnostic>(())
561 /// ```
562 pub fn cell<F>(
563 &mut self,
564 name: &str,
565 shape: TypeShape,
566 emit: F,
567 ) -> Result<CellSlot, ModuleDiagnostic>
568 where
569 F: Fn() -> anyhow::Result<DynSegment> + 'static,
570 {
571 self.private(name, shape, emit, false)
572 }
573
574 /// Declares a fresh private source initialized at instantiation.
575 ///
576 /// # Errors
577 /// Returns the same definition errors as [`Self::cell`].
578 /// - Complexity: The same as [`Self::cell`].
579 ///
580 /// # Examples
581 /// See [`Self::cell`]; use `source` instead of `cell`.
582 pub fn source<F>(
583 &mut self,
584 name: &str,
585 shape: TypeShape,
586 emit: F,
587 ) -> Result<CellSlot, ModuleDiagnostic>
588 where
589 F: Fn() -> anyhow::Result<DynSegment> + 'static,
590 {
591 self.private(name, shape, emit, true)
592 }
593
594 /// Declares a private cell using the registry's canonical default emitter.
595 ///
596 /// No default value is constructed until instantiation.
597 /// # Errors
598 /// Rejects unknown shapes, missing defaults/capabilities, or native declaration errors.
599 /// - Complexity: O(n + l) shape nodes and name bytes.
600 ///
601 /// # Examples
602 /// ```
603 /// let mut b = adam_lang::CompositionBuilder::new(adam_lang::TypeRegistry::new());
604 /// b.default_cell("empty", adam_lang::type_registry::TypeShape::Tuple(vec![]))?;
605 /// # Ok::<(), adam_lang::ModuleDiagnostic>(())
606 /// ```
607 pub fn default_cell(
608 &mut self,
609 name: &str,
610 shape: TypeShape,
611 ) -> Result<CellSlot, ModuleDiagnostic> {
612 self.default_private(name, shape, false)
613 }
614
615 /// Declares a checked canonical default without constructing its value.
616 ///
617 /// # Errors
618 /// Reports unavailable defaults, shapes, capabilities or invalid native declarations.
619 /// - Complexity: O(n + l) shape nodes and name bytes.
620 pub(crate) fn default_private(
621 &mut self,
622 name: &str,
623 shape: TypeShape,
624 source: bool,
625 ) -> Result<CellSlot, ModuleDiagnostic> {
626 let adapters = self.adapters(&shape)?;
627 self.types.validate_default(&shape).map_err(diagnostic)?;
628 let types = self.types.clone();
629 let default_shape = shape.clone();
630 // Preparation must not call host Default implementations.
631 let ty = types.value_type(&shape);
632 CellValue::validate(&ty).map_err(diagnostic)?;
633 let native = (adapters.cell)(
634 &mut self.native,
635 name,
636 Box::new(move || {
637 let mut body = DynSegment::new::<()>();
638 types.emit_default(&default_shape, &mut body)?;
639 let mut frame = Frame::new(
640 body,
641 &[],
642 std::slice::from_ref(&default_shape),
643 false,
644 &types,
645 )?;
646 Ok(frame.call(&[])?.remove(0))
647 }),
648 source,
649 )
650 .map_err(diagnostic)?;
651 Ok(self.record(native, shape))
652 }
653
654 /// Checks an initializer without executing it, then retains its repeatable factory.
655 ///
656 /// # Errors
657 /// Rejects unknown shapes, signatures, capabilities, or native declarations.
658 /// - Complexity: O(n + e + l) descriptor nodes, emitted reads and name bytes.
659 fn private<F>(
660 &mut self,
661 name: &str,
662 shape: TypeShape,
663 emit: F,
664 source: bool,
665 ) -> Result<CellSlot, ModuleDiagnostic>
666 where
667 F: Fn() -> anyhow::Result<DynSegment> + 'static,
668 {
669 let adapters = self.adapters(&shape)?;
670 CellValue::validate(&self.types.value_type(&shape)).map_err(diagnostic)?;
671 Frame::new(
672 emit().map_err(diagnostic)?,
673 &[],
674 std::slice::from_ref(&shape),
675 false,
676 &self.types,
677 )
678 .map_err(diagnostic)?;
679 let types = self.types.clone();
680 let initial_shape = shape.clone();
681 let native = (adapters.cell)(
682 &mut self.native,
683 name,
684 Box::new(move || {
685 let mut frame = Frame::new(
686 emit()?,
687 &[],
688 std::slice::from_ref(&initial_shape),
689 false,
690 &types,
691 )?;
692 Ok(frame.call(&[])?.remove(0))
693 }),
694 source,
695 )
696 .map_err(diagnostic)?;
697 Ok(self.record(native, shape))
698 }
699
700 /// Constructs a repeatable, complete-shape method without executing its expression.
701 ///
702 /// Inputs use positional `push_arg` indices. A single output receives the whole
703 /// result; two or more outputs destructure the result's source-ordered tuple.
704 /// Callbacks obey native method purity and self-reference idempotence contracts.
705 /// # Errors
706 /// Rejects foreign handles, malformed edges, incompatible bodies, or unknown shapes.
707 /// - Complexity: O(n + e + o²) descriptor nodes n, emitted reads e and outputs o.
708 ///
709 /// # Examples
710 /// ```
711 /// use adam_lang::{CompositionBuilder, TypeRegistry};
712 /// use adam_lang::type_registry::TypeShape;
713 /// let mut b = CompositionBuilder::new(TypeRegistry::new());
714 /// let x = b.parameter("x", TypeShape::Named(std::any::TypeId::of::<i32>()))?;
715 /// let recipe = b.method(std::slice::from_ref(&x), std::slice::from_ref(&x), || {
716 /// let mut body = cel_runtime::DynSegment::new::<()>();
717 /// body.push_arg::<i32>(0);
718 /// Ok(body)
719 /// })?;
720 /// assert_eq!(recipe.input_shapes(), recipe.output_shapes());
721 /// b.relationship(vec![recipe])?;
722 /// # Ok::<(), adam_lang::ModuleDiagnostic>(())
723 /// ```
724 pub fn method<F>(
725 &self,
726 inputs: &[CellSlot],
727 outputs: &[CellSlot],
728 emit: F,
729 ) -> Result<MethodRecipe, ModuleDiagnostic>
730 where
731 F: Fn() -> anyhow::Result<DynSegment> + 'static,
732 {
733 self.method_pattern(inputs, outputs, outputs.len() > 1, emit)
734 }
735
736 /// Prepares an explicitly whole-result or destructured checked output pattern.
737 ///
738 /// # Errors
739 /// Reports invalid native edges or incompatible complete signatures.
740 /// - Complexity: As [`Self::method`].
741 pub(crate) fn method_pattern<F>(
742 &self,
743 inputs: &[CellSlot],
744 outputs: &[CellSlot],
745 destructure: bool,
746 emit: F,
747 ) -> Result<MethodRecipe, ModuleDiagnostic>
748 where
749 F: Fn() -> anyhow::Result<DynSegment> + 'static,
750 {
751 let (input_slots, input_shapes) = self.slots(inputs)?;
752 let (output_slots, output_shapes) = self.slots(outputs)?;
753 if outputs.is_empty() {
754 return Err(diagnostic("a method requires an output"));
755 }
756 Frame::new(
757 emit().map_err(diagnostic)?,
758 &input_shapes,
759 &output_shapes,
760 destructure,
761 &self.types,
762 )
763 .map_err(diagnostic)?;
764 let types = self.types.clone();
765 let i_shapes = input_shapes.clone();
766 let o_shapes = output_shapes.clone();
767 let native = adam_rs::MethodRecipe::new(&input_slots, &output_slots, move |i, o| {
768 frame_method(emit()?, i, o, &i_shapes, &o_shapes, destructure, &types)
769 })
770 .map_err(diagnostic)?;
771 Ok(MethodRecipe {
772 native,
773 inputs: input_shapes,
774 outputs: output_shapes,
775 })
776 }
777
778 /// Appends a native relationship, preserving its alternative method ordering.
779 ///
780 /// # Errors
781 /// Returns native ownership and relationship validation errors before mutation.
782 /// - Complexity: O(m² × e) for m methods of at most e edges.
783 ///
784 /// # Examples
785 /// See [`Self::method`].
786 pub fn relationship(
787 &mut self,
788 methods: Vec<MethodRecipe>,
789 ) -> Result<RelationshipSlot, ModuleDiagnostic> {
790 self.native
791 .relationship(methods.into_iter().map(|m| m.native).collect())
792 .map_err(diagnostic)
793 }
794
795 /// Promotes a private ordinary cell through its single sole-writer method.
796 ///
797 /// # Errors
798 /// Rejects formal, source, multi-output, foreign, or previously written targets.
799 /// - Complexity: O(i + r) inputs and prior output summaries.
800 ///
801 /// # Examples
802 /// See [`Self::method`]; pass a private single-output recipe to `out`.
803 pub fn out(&mut self, writer: MethodRecipe) -> Result<RelationshipSlot, ModuleDiagnostic> {
804 self.native.out(writer.native).map_err(diagnostic)
805 }
806
807 /// Attaches a value-returning or numeric-range filter with a fresh frame per instance.
808 ///
809 /// Argument zero is the candidate; subsequent arguments are the declared inputs.
810 /// The callback obeys the native filter's purity contract.
811 /// # Errors
812 /// Rejects foreign/formal targets, invalid dependencies, unknown shapes or mismatched bodies.
813 /// - Complexity: O(n + e + i) shape nodes, emitted reads and inputs.
814 ///
815 /// # Examples
816 /// See [`Self::cell`]; a filter emitter may use `push_arg::<i32>(0)` for its candidate.
817 pub fn filter<F>(
818 &mut self,
819 cell: &CellSlot,
820 inputs: &[CellSlot],
821 emit: F,
822 ) -> Result<(), ModuleDiagnostic>
823 where
824 F: Fn() -> anyhow::Result<DynSegment> + 'static,
825 {
826 self.check_slot(cell)?;
827 let (slots, shapes) = self.slots(inputs)?;
828 let types = self.types.clone();
829 let shape = cell.shape.clone();
830 let candidate_shapes: Vec<_> = std::iter::once(shape.clone())
831 .chain(shapes.iter().cloned())
832 .collect();
833 let body = emit().map_err(diagnostic)?;
834 let range = body
835 .peek_output_type_id()
836 .and_then(|id| types.range_entry(id));
837 if let Some(range) = range {
838 if range.element_type_id != crate::cell_value::cell_type_id(&shape) {
839 return Err(diagnostic("filter range element mismatch"));
840 }
841 let result = body.peek_stack_infos(1)[0].value_type.clone();
842 crate::cell_value::RawFrame::new(body, &candidate_shapes, &result, &types)
843 .map_err(diagnostic)?;
844 } else {
845 Frame::new(
846 body,
847 &candidate_shapes,
848 std::slice::from_ref(&shape),
849 false,
850 &types,
851 )
852 .map_err(diagnostic)?;
853 }
854 self.native
855 .filter(&cell.native, &slots, move |_, ids| {
856 let body = emit()?;
857 if body
858 .peek_output_type_id()
859 .and_then(|id| types.range_entry(id))
860 .is_some()
861 {
862 crate::cell_value::build_range_filter(
863 body,
864 ids.to_vec(),
865 &shapes,
866 &shape,
867 &types,
868 )
869 } else {
870 crate::cell_value::build_value_filter(
871 body,
872 ids.to_vec(),
873 &shapes,
874 &shape,
875 &types,
876 )
877 }
878 })
879 .map_err(diagnostic)
880 }
881
882 /// Attaches a bool-returning requirement with a fresh frame per instance.
883 ///
884 /// The callback obeys the native requirement's purity contract.
885 /// # Errors
886 /// Rejects foreign/formal targets, unknown shapes and incompatible emitted signatures.
887 /// Ordinary/source requirement execution errors occur during native attachment.
888 /// - Complexity: O(n + e + i) descriptor nodes, reads and inputs.
889 ///
890 /// # Examples
891 /// See [`Self::cell`]; a nullary requirement may emit `body.op0(|| true)`.
892 pub fn require<F>(
893 &mut self,
894 cell: &CellSlot,
895 inputs: &[CellSlot],
896 emit: F,
897 ) -> Result<(), ModuleDiagnostic>
898 where
899 F: Fn() -> anyhow::Result<DynSegment> + 'static,
900 {
901 self.types
902 .validate_shape(&TypeShape::Named(TypeId::of::<bool>()))
903 .map_err(diagnostic)?;
904 self.check_slot(cell)?;
905 let (slots, shapes) = self.slots(inputs)?;
906 let types = self.types.clone();
907 Frame::new(
908 emit().map_err(diagnostic)?,
909 &shapes,
910 &[TypeShape::Named(TypeId::of::<bool>())],
911 false,
912 &types,
913 )
914 .map_err(diagnostic)?;
915 self.native
916 .require(&cell.native, &slots, move |ids| {
917 crate::cell_value::build_requirement(emit()?, ids.to_vec(), &shapes, &types)
918 })
919 .map_err(diagnostic)
920 }
921
922 /// Registers conditional groups using a registered host leaf as the match key.
923 ///
924 /// Each included group must be transitively relationship-only. Keys and emitted
925 /// results have exactly type `T`; callbacks obey native match-expression purity.
926 /// # Errors
927 /// Rejects unknown key/input shapes, incompatible bodies or native group errors.
928 /// - Complexity: O(n + e + i + g + k) descriptors, reads, inputs, groups and keys.
929 ///
930 /// # Examples
931 /// ```
932 /// let mut b = adam_lang::CompositionBuilder::new(adam_lang::TypeRegistry::new());
933 /// b.conditional(&[], || {
934 /// let mut body = cel_runtime::DynSegment::new::<()>();
935 /// body.op0(|| true);
936 /// Ok(body)
937 /// }, vec![(vec![true], vec![])], vec![])?;
938 /// # Ok::<(), adam_lang::ModuleDiagnostic>(())
939 /// ```
940 pub fn conditional<T, F>(
941 &mut self,
942 inputs: &[CellSlot],
943 emit: F,
944 branches: Vec<(Vec<T>, Vec<RelationshipSlot>)>,
945 default: Vec<RelationshipSlot>,
946 ) -> Result<(), ModuleDiagnostic>
947 where
948 T: Any + Clone + PartialEq,
949 F: Fn() -> anyhow::Result<DynSegment> + 'static,
950 {
951 let shape = TypeShape::Named(TypeId::of::<T>());
952 self.types.validate_shape(&shape).map_err(diagnostic)?;
953 self.conditional_frame(inputs, emit, shape, branches, default)
954 }
955
956 /// Registers conditional groups using complete canonical compound keys.
957 ///
958 /// The witness supplies both key and result identity. Compound equality and cloning
959 /// must be available; callable keys without equality are rejected.
960 /// # Errors
961 /// Rejects unknown shapes, non-compound witnesses, missing capabilities,
962 /// incompatible emitted results, or native group errors before recipe mutation.
963 /// - Complexity: O(n + e + i + g + k) descriptors, reads, inputs, groups and key values.
964 ///
965 /// # Examples
966 /// ```
967 /// use cel_runtime::{Array, ArrayOf, Leaf, DynSegment};
968 /// let mut b = adam_lang::CompositionBuilder::new(adam_lang::TypeRegistry::new());
969 /// b.conditional_compound::<ArrayOf<Leaf<bool>>, _>(&[], || {
970 /// let mut body = DynSegment::new::<()>();
971 /// body.op0_repr::<ArrayOf<Leaf<bool>>, _>(|| Array::from_vec(vec![true]))?;
972 /// Ok(body)
973 /// }, vec![(vec![Array::from_vec(vec![true])], vec![])], vec![])?;
974 /// # Ok::<(), adam_lang::ModuleDiagnostic>(())
975 /// ```
976 pub fn conditional_compound<R, F>(
977 &mut self,
978 inputs: &[CellSlot],
979 emit: F,
980 branches: Vec<(Vec<R::Value>, Vec<RelationshipSlot>)>,
981 default: Vec<RelationshipSlot>,
982 ) -> Result<(), ModuleDiagnostic>
983 where
984 R: cel_runtime::ValueRepr,
985 F: Fn() -> anyhow::Result<DynSegment> + 'static,
986 {
987 let shape = self.types.shape_of(&R::value_type()).map_err(diagnostic)?;
988 if matches!(shape, TypeShape::Named(_)) {
989 return Err(diagnostic("expected a compound conditional witness"));
990 }
991 let descriptor = self.types.checked_value_type(&shape).map_err(diagnostic)?;
992 CellValue::validate(&descriptor).map_err(diagnostic)?;
993 let branches = branches
994 .into_iter()
995 .map(|(keys, groups)| {
996 let keys = keys
997 .into_iter()
998 .map(|key| {
999 cel_runtime::OwnedValue::new::<R>(key)
1000 .with_value_type(descriptor.clone())
1001 .map(CellValue)
1002 })
1003 .collect::<anyhow::Result<Vec<_>>>()?;
1004 Ok((keys, groups))
1005 })
1006 .collect::<anyhow::Result<_>>()
1007 .map_err(diagnostic)?;
1008 self.conditional_frame(inputs, emit, shape, branches, default)
1009 }
1010
1011 /// Prepares a conditional frame with a checked native storage result.
1012 ///
1013 /// - Precondition: `shape` is registered and its storage is exactly `T`.
1014 /// # Errors
1015 /// Rejects invalid inputs, emitted signatures or native group structure.
1016 /// - Complexity: O(n + e + i + g + k) descriptors, reads, inputs, groups and keys.
1017 fn conditional_frame<T, F>(
1018 &mut self,
1019 inputs: &[CellSlot],
1020 emit: F,
1021 shape: TypeShape,
1022 branches: Vec<(Vec<T>, Vec<RelationshipSlot>)>,
1023 default: Vec<RelationshipSlot>,
1024 ) -> Result<(), ModuleDiagnostic>
1025 where
1026 T: Any + Clone + PartialEq,
1027 F: Fn() -> anyhow::Result<DynSegment> + 'static,
1028 {
1029 let (slots, shapes) = self.slots(inputs)?;
1030 let types = self.types.clone();
1031 Frame::new(
1032 emit().map_err(diagnostic)?,
1033 &shapes,
1034 std::slice::from_ref(&shape),
1035 false,
1036 &types,
1037 )
1038 .map_err(diagnostic)?;
1039 self.native
1040 .conditional(
1041 &slots,
1042 move |ids| {
1043 let frame = RefCell::new(Frame::new(
1044 emit()?,
1045 &shapes,
1046 std::slice::from_ref(&shape),
1047 false,
1048 &types,
1049 )?);
1050 Ok(MatchExpr::new(
1051 ids.to_vec(),
1052 storage_ids(&shapes),
1053 TypeId::of::<T>(),
1054 |a, b| a.downcast_ref::<T>() == b.downcast_ref::<T>(),
1055 move |args| Ok(frame.borrow_mut().call(args)?.remove(0)),
1056 ))
1057 },
1058 branches,
1059 default,
1060 )
1061 .map_err(diagnostic)
1062 }
1063
1064 /// Registers retained literal-key and match emitters without executing either.
1065 ///
1066 /// Checked canonical values provide equality for scalar and compound match shapes.
1067 /// # Errors
1068 /// Reports invalid handles or signatures before native mutation.
1069 /// - Complexity: Expression preparation plus O(i + g) inputs and groups.
1070 pub(crate) fn conditional_owned<F, K>(
1071 &mut self,
1072 inputs: &[CellSlot],
1073 shape: TypeShape,
1074 emit: F,
1075 keys: K,
1076 branches: Vec<Vec<RelationshipSlot>>,
1077 default: Vec<RelationshipSlot>,
1078 ) -> Result<(), ModuleDiagnostic>
1079 where
1080 F: Fn() -> anyhow::Result<DynSegment> + 'static,
1081 K: Fn() -> anyhow::Result<Vec<Vec<CellValue>>> + 'static,
1082 {
1083 let (slots, shapes) = self.slots(inputs)?;
1084 let types = self.types.clone();
1085 let result = types.checked_value_type(&shape).map_err(diagnostic)?;
1086 CellValue::validate(&result).map_err(diagnostic)?;
1087 crate::cell_value::RawFrame::new(emit().map_err(diagnostic)?, &shapes, &result, &types)
1088 .map_err(diagnostic)?;
1089 self.native
1090 .conditional_factory::<CellValue, _, _>(
1091 &slots,
1092 move |ids| {
1093 let frame = RefCell::new(crate::cell_value::RawFrame::new(
1094 emit()?,
1095 &shapes,
1096 &result,
1097 &types,
1098 )?);
1099 Ok(MatchExpr::new(
1100 ids.to_vec(),
1101 storage_ids(&shapes),
1102 TypeId::of::<CellValue>(),
1103 |a, b| a.downcast_ref::<CellValue>() == b.downcast_ref::<CellValue>(),
1104 move |args| Ok(Box::new(CellValue(frame.borrow_mut().call(args)?))),
1105 ))
1106 },
1107 keys,
1108 branches,
1109 default,
1110 )
1111 .map_err(diagnostic)
1112 }
1113
1114 /// Includes a typed child using complete named bindings before native mutation.
1115 ///
1116 /// # Errors
1117 /// Rejects foreign handles, unknown/missing/duplicate names, aliases, full-shape
1118 /// mismatches and native kind/structural errors. Rejection leaves the recipe unchanged.
1119 /// - Complexity: O(b + n) bindings and compared shape nodes, plus native local preflight.
1120 ///
1121 /// # Examples
1122 /// ```
1123 /// let types = adam_lang::TypeRegistry::new();
1124 /// let child = adam_lang::CompositionBuilder::new(types.clone()).finish()?;
1125 /// let mut parent = adam_lang::CompositionBuilder::new(types);
1126 /// parent.include(&child, &[])?;
1127 /// # Ok::<(), adam_lang::ModuleDiagnostic>(())
1128 /// ```
1129 pub fn include(
1130 &mut self,
1131 child: &TypedTemplate,
1132 bindings: &[(&str, CellSlot)],
1133 ) -> Result<TemplateInclusion, ModuleDiagnostic> {
1134 let mut names = HashSet::new();
1135 let mut actuals = HashSet::new();
1136 for (name, slot) in bindings {
1137 self.check_slot(slot)?;
1138 let formal = child
1139 .metadata
1140 .formals
1141 .get(*name)
1142 .ok_or_else(|| diagnostic("unknown parameter"))?;
1143 if !names.insert(*name) || !actuals.insert(&slot.native) {
1144 return Err(diagnostic("duplicate parameter or aliased argument"));
1145 }
1146 if formal.shape != slot.shape {
1147 return Err(diagnostic("complete shape mismatch"));
1148 }
1149 }
1150 if names.len() != child.metadata.formals.len() {
1151 return Err(diagnostic("missing parameter"));
1152 }
1153 let native: Vec<_> = bindings
1154 .iter()
1155 .map(|(name, slot)| (*name, slot.native.clone()))
1156 .collect();
1157 let inclusion = self
1158 .native
1159 .include(child.native.clone(), &native)
1160 .map_err(diagnostic)?;
1161 self.children.push(child.metadata.clone());
1162 Ok(inclusion)
1163 }
1164
1165 /// Finalizes an immutable native recipe and its complete local metadata.
1166 ///
1167 /// # Errors
1168 /// Rejects included parameters that became private outputs after inclusion.
1169 /// - Complexity: O(n + a + e) local declarations, authored records and edges,
1170 /// without DAG expansion.
1171 ///
1172 /// # Examples
1173 /// See the [module example](self).
1174 pub fn finish(self) -> Result<TypedTemplate, ModuleDiagnostic> {
1175 let native = Rc::new(self.native.finish().map_err(diagnostic)?);
1176 let mut cell_labels = HashMap::new();
1177 let mut requirements = HashMap::<_, Vec<_>>::new();
1178 for item in &self.authored {
1179 if let Some(cell) = &item.cell {
1180 match item.role {
1181 AuthoredRole::Parameter
1182 | AuthoredRole::Cell
1183 | AuthoredRole::Source
1184 | AuthoredRole::Out => {
1185 cell_labels.insert(cell.clone(), item.label.clone());
1186 }
1187 AuthoredRole::Requirement => {
1188 requirements
1189 .entry(cell.clone())
1190 .or_default()
1191 .push(item.requirement_name.clone());
1192 }
1193 _ => {}
1194 }
1195 }
1196 }
1197 let metadata = Rc::new(Metadata {
1198 id: native.id(),
1199 cells: self.cells,
1200 formals: self.formals,
1201 children: self.children,
1202 label: self.label,
1203 authored: self.authored.into(),
1204 cell_labels,
1205 requirements,
1206 });
1207 Ok(TypedTemplate { native, metadata })
1208 }
1209
1210 /// Validates a complete shape before selecting its storage constructor.
1211 ///
1212 /// # Errors
1213 /// Rejects every unregistered leaf, including nested callable signatures.
1214 /// - Complexity: O(n) shape nodes.
1215 fn adapters(&self, shape: &TypeShape) -> Result<LeafAdapters, ModuleDiagnostic> {
1216 self.types.validate_shape(shape).map_err(diagnostic)?;
1217 Ok(match shape {
1218 TypeShape::Named(id) => {
1219 self.types
1220 .entry_by_type_id(*id)
1221 .expect("validated leaf")
1222 .template
1223 }
1224 _ => LeafAdapters::of::<CellValue>(),
1225 })
1226 }
1227
1228 /// Records a successfully declared local shape.
1229 ///
1230 /// - Complexity: O(n) cloned shape nodes and expected O(1) map insertion.
1231 fn record(&mut self, native: adam_rs::CellSlot, shape: TypeShape) -> CellSlot {
1232 self.cells.insert(native.clone(), shape.clone());
1233 CellSlot { native, shape }
1234 }
1235
1236 /// Validates one local owner and complete shape without allocating edge vectors.
1237 ///
1238 /// # Errors
1239 /// Rejects foreign or unregistered slots before emitter invocation.
1240 /// - Complexity: O(n) shape nodes.
1241 fn check_slot(&self, slot: &CellSlot) -> Result<(), ModuleDiagnostic> {
1242 if self.cells.get(&slot.native) != Some(&slot.shape) {
1243 return Err(diagnostic("foreign cell slot"));
1244 }
1245 self.types.validate_shape(&slot.shape).map_err(diagnostic)
1246 }
1247
1248 /// Checks ownership and resolves positional slots without relying on carrier equality.
1249 ///
1250 /// # Errors
1251 /// Rejects foreign or unregistered slots before emitter invocation.
1252 /// - Complexity: O(i + n) slots and shape nodes.
1253 fn slots(
1254 &self,
1255 slots: &[CellSlot],
1256 ) -> Result<(Vec<adam_rs::CellSlot>, Vec<TypeShape>), ModuleDiagnostic> {
1257 for slot in slots {
1258 self.check_slot(slot)?;
1259 }
1260 Ok((
1261 slots.iter().map(|s| s.native.clone()).collect(),
1262 slots.iter().map(|s| s.shape.clone()).collect(),
1263 ))
1264 }
1265}
1266
1267/// Owns a reusable native definition, complete formal/local shapes and source metadata.
1268///
1269/// No live runtime is cloned. Metadata retains compact child edges and releases them
1270/// iteratively on last-owner drop. See the [module example](self).
1271pub struct TypedTemplate {
1272 native: Rc<SheetTemplate>,
1273 metadata: Rc<Metadata>,
1274}
1275
1276impl Clone for TypedTemplate {
1277 /// Shares immutable recipes and metadata without copying any live execution state.
1278 fn clone(&self) -> Self {
1279 Self {
1280 native: self.native.clone(),
1281 metadata: self.metadata.clone(),
1282 }
1283 }
1284}
1285
1286impl TypedTemplate {
1287 /// Appends genuine source owners while visiting each immutable definition only once.
1288 ///
1289 /// - Precondition: `sources` already contains every identity in `seen_sources`.
1290 /// - Complexity: O(d + a) newly visited metadata nodes, edges and authored records.
1291 pub(crate) fn retain_sources(
1292 &self,
1293 seen_definitions: &mut HashSet<TemplateId>,
1294 seen_sources: &mut HashSet<crate::SourceId>,
1295 sources: &mut Vec<crate::SourceId>,
1296 ) {
1297 let mut pending = vec![self.metadata.as_ref()];
1298 while let Some(node) = pending.pop() {
1299 if !seen_definitions.insert(node.id.clone()) {
1300 continue;
1301 }
1302 for label in std::iter::once(&node.label).chain(
1303 node.authored
1304 .iter()
1305 .flat_map(|item| std::iter::once(&item.label).chain(item.expression.iter())),
1306 ) {
1307 if let Some(location) = &label.location
1308 && seen_sources.insert(location.source.clone())
1309 {
1310 sources.push(location.source.clone());
1311 }
1312 }
1313 pending.extend(node.children.iter().map(Rc::as_ref));
1314 }
1315 }
1316
1317 /// Borrows flat authored labels and local native slot mappings.
1318 ///
1319 /// Native templates may have no authored item records.
1320 /// # Examples
1321 /// ```
1322 /// let t = adam_lang::CompositionBuilder::new(adam_lang::TypeRegistry::new()).finish()?;
1323 /// assert!(t.authored_items().is_empty());
1324 /// # Ok::<(), adam_lang::ModuleDiagnostic>(())
1325 /// ```
1326 pub fn authored_items(&self) -> &[AuthoredItem] {
1327 &self.metadata.authored
1328 }
1329
1330 /// Borrows complete formal signatures in declaration order without running factories.
1331 ///
1332 /// - Complexity: O(c) traversal for c local declarations, with expected O(1) lookup per formal.
1333 ///
1334 /// # Examples
1335 /// ```
1336 /// let mut builder = adam_lang::CompositionBuilder::new(adam_lang::TypeRegistry::new());
1337 /// builder.parameter("x", adam_lang::type_registry::TypeShape::Named(std::any::TypeId::of::<i32>()))?;
1338 /// let template = builder.finish()?;
1339 /// assert_eq!(template.parameters().next().unwrap().0, "x");
1340 /// # Ok::<(), adam_lang::ModuleDiagnostic>(())
1341 /// ```
1342 pub fn parameters(&self) -> impl Iterator<Item = (&str, &TypeShape)> {
1343 self.native
1344 .parameter_names()
1345 .map(|name| (name, self.metadata.formals[name].shape()))
1346 }
1347
1348 /// Checks complete formal source statuses through native whole-method preflight.
1349 ///
1350 /// # Errors
1351 /// Reports invalid binding names, capacity failure, or an emptied relationship.
1352 /// - Complexity: Native source preflight plus O(n) diagnostic bytes on failure.
1353 pub(crate) fn check_source_bindings(
1354 &self,
1355 bindings: &[(&str, bool)],
1356 ) -> Result<(), ModuleDiagnostic> {
1357 self.native
1358 .check_source_bindings(bindings)
1359 .map_err(|error| self.error(error))
1360 }
1361
1362 /// Borrows the authored definition label, including span-free native labels.
1363 ///
1364 /// # Examples
1365 /// ```
1366 /// let template = adam_lang::CompositionBuilder::new(adam_lang::TypeRegistry::new()).finish()?;
1367 /// assert!(template.definition_label().location.is_none());
1368 /// # Ok::<(), adam_lang::ModuleDiagnostic>(())
1369 /// ```
1370 pub fn definition_label(&self) -> &SourceLabel {
1371 &self.metadata.label
1372 }
1373
1374 /// Instantiates into an existing source-free or parsed destination.
1375 ///
1376 /// Named bindings must be complete, distinct, registered in the destination and
1377 /// exactly shape-compatible. Native checks enforce cell kinds and specialization.
1378 /// Binding/structural rejection precedes all mutation. Later initializer/body
1379 /// factory errors leave the destination safely discardable, not rolled back.
1380 /// Private names never enter root lookup maps; full private shapes and flat
1381 /// occurrence, method and conditional origins survive definition and returned instance drop.
1382 /// # Errors
1383 /// Reports binding, native structural/capacity, initializer or body-factory failures.
1384 /// Native diagnostics retain span-free labels unless a real label was supplied.
1385 /// - Complexity: Expected O(b + n + d + o + c + r + m + k + s) bindings, shape nodes,
1386 /// unique metadata DAG size, occurrences, local cell mappings, relationships and
1387 /// surviving methods, conditional mappings k and previously retained sources s, plus native expansion
1388 /// and factory costs.
1389 ///
1390 /// # Examples
1391 /// See the [module example](self).
1392 pub fn instantiate(
1393 &self,
1394 destination: &mut ParsedSheet,
1395 bindings: &[(&str, CellId)],
1396 ) -> Result<Instance, ModuleDiagnostic> {
1397 let mut names = HashSet::new();
1398 let mut actuals = HashSet::new();
1399 for (name, id) in bindings {
1400 let formal = self
1401 .metadata
1402 .formals
1403 .get(*name)
1404 .ok_or_else(|| self.error("unknown parameter"))?;
1405 if !names.insert(*name) || !actuals.insert(*id) {
1406 return Err(self.error("duplicate parameter or aliased argument"));
1407 }
1408 if destination.cell_shape(*id) != Some(&formal.shape) {
1409 return Err(self.error("complete shape mismatch or unregistered actual"));
1410 }
1411 destination
1412 .validate_cell_shape(*id, &formal.shape)
1413 .map_err(|e| self.error(e))?;
1414 }
1415 if names.len() != self.metadata.formals.len() {
1416 return Err(self.error("missing parameter"));
1417 }
1418 let instance = self
1419 .native
1420 .instantiate(&mut destination.sheet, bindings)
1421 .map_err(|error| self.construction_error(error))?;
1422 let definitions = self.definitions();
1423 let mut occurrences = Vec::with_capacity(instance.occurrences().len());
1424 let mut source_ids: HashSet<_> = destination.sources.iter().cloned().collect();
1425 let mut requirement_offsets = HashMap::<CellId, usize>::new();
1426 for occurrence in instance.occurrences() {
1427 let definition = definitions[occurrence.template()];
1428 for (slot, shape) in &definition.cells {
1429 let id = instance
1430 .cell(occurrence.id(), slot)
1431 .expect("native owner-checked mapping");
1432 destination.cell_shapes.insert(id, shape.clone());
1433 destination
1434 .cell_origins
1435 .entry(id)
1436 .or_default()
1437 .push(CellOrigin {
1438 occurrence: occurrence.id().clone(),
1439 slot: slot.clone(),
1440 label: definition
1441 .cell_labels
1442 .get(slot)
1443 .unwrap_or(&definition.label)
1444 .clone(),
1445 });
1446 // Preserve attachment order, including unnamed requirements.
1447 if let Some(policies) = definition.requirements.get(slot) {
1448 let ids = destination.sheet.cell_requirements(id).unwrap_or_default();
1449 let start = requirement_offsets.entry(id).or_default();
1450 let authored = definition.authored.iter().filter(|item| {
1451 item.role == AuthoredRole::Requirement && item.cell.as_ref() == Some(slot)
1452 });
1453 for ((name, id), item) in
1454 policies.iter().zip(ids.iter().skip(*start)).zip(authored)
1455 {
1456 if let Some(name) = name {
1457 destination.requirement_names.insert(*id, name.clone());
1458 }
1459 destination
1460 .requirement_labels
1461 .insert(*id, item.label.clone());
1462 }
1463 *start += policies.len();
1464 }
1465 }
1466 if let Some(location) = &definition.label.location
1467 && source_ids.insert(location.source.clone())
1468 {
1469 destination.sources.push(location.source.clone());
1470 }
1471 occurrences.push(OccurrenceOrigin {
1472 id: occurrence.id().clone(),
1473 parent: occurrence.parent().cloned(),
1474 template: occurrence.template().clone(),
1475 inclusion_site: occurrence.inclusion_site().cloned(),
1476 label: definition.label.clone(),
1477 authored: definition.authored.clone(),
1478 conditionals: occurrence.conditionals().to_vec(),
1479 });
1480 }
1481 destination.method_origins.extend(
1482 instance
1483 .method_origins()
1484 .map(|(r, m, origin)| ((r, m), origin)),
1485 );
1486 destination
1487 .instance_origins
1488 .push(InstanceOrigin { occurrences });
1489 Ok(instance)
1490 }
1491
1492 /// Returns native relationship-only eligibility before any source specialization.
1493 ///
1494 /// # Examples
1495 /// ```
1496 /// let template = adam_lang::CompositionBuilder::new(adam_lang::TypeRegistry::new()).finish()?;
1497 /// assert!(template.is_relationship_only());
1498 /// # Ok::<(), adam_lang::ModuleDiagnostic>(())
1499 /// ```
1500 pub fn is_relationship_only(&self) -> bool {
1501 self.native.is_relationship_only()
1502 }
1503
1504 /// Indexes unique immutable metadata nodes without expanding a second graph.
1505 ///
1506 /// - Complexity: O(d) time and space in unique metadata nodes and inclusion edges.
1507 fn definitions(&self) -> HashMap<&TemplateId, &Metadata> {
1508 let mut result = HashMap::new();
1509 let mut pending = vec![self.metadata.as_ref()];
1510 while let Some(node) = pending.pop() {
1511 if result.insert(&node.id, node).is_none() {
1512 pending.extend(node.children.iter().map(Rc::as_ref));
1513 }
1514 }
1515 result
1516 }
1517
1518 /// Labels an instantiation failure without inventing a source range.
1519 ///
1520 /// - Complexity: O(m) formatted message bytes.
1521 fn error(&self, error: impl std::fmt::Display) -> ModuleDiagnostic {
1522 ModuleDiagnostic {
1523 cause: None,
1524 message: error.to_string(),
1525 labels: vec![self.metadata.label.clone()],
1526 }
1527 }
1528
1529 /// Retains the native construction error and real authored source owners.
1530 ///
1531 /// Contextual failures label the failed definition/item and only the active native
1532 /// inclusion chain. Failures without native occurrence context retain only known
1533 /// declarations; reachable uses do not imply a selected path.
1534 /// - Complexity: O(d + a + h × a) unique metadata nodes/edges d, authored items a
1535 /// and active conditional inclusion depth h.
1536 fn construction_error(&self, error: adam_rs::TemplateError) -> ModuleDiagnostic {
1537 if let adam_rs::TemplateError::Construction {
1538 template,
1539 site,
1540 inclusions,
1541 source,
1542 } = &error
1543 {
1544 let definitions = self.definitions();
1545 let mut labels = Vec::new();
1546 if let Some(definition) = definitions.get(template) {
1547 if let Some(item) = construction_item(&definition.authored, site) {
1548 let mut label = item.label.clone();
1549 if let Some(span) = construction_span(source)
1550 && let Some(location) = item
1551 .expression
1552 .as_ref()
1553 .and_then(|label| label.location.as_ref())
1554 {
1555 label.location = Some(crate::SourceLocation {
1556 source: location.source.clone(),
1557 span,
1558 });
1559 }
1560 labels.push(label);
1561 }
1562 labels.push(definition.label.clone());
1563 }
1564 for site in inclusions {
1565 labels.extend(
1566 definitions
1567 .values()
1568 .flat_map(|definition| definition.authored.iter())
1569 .filter(|item| {
1570 item.role == AuthoredRole::Use
1571 && item.relationship.as_ref() == Some(site)
1572 })
1573 .map(|item| item.label.clone()),
1574 );
1575 }
1576 return ModuleDiagnostic {
1577 message: error.to_string(),
1578 labels,
1579 cause: Some(crate::source_map::ModuleCause::new(error)),
1580 };
1581 }
1582 if let adam_rs::TemplateError::ConditionalConstruction {
1583 template,
1584 conditional,
1585 inclusions,
1586 ..
1587 } = &error
1588 {
1589 let definitions = self.definitions();
1590 let mut labels = vec![self.metadata.label.clone()];
1591 if let Some(definition) = definitions.get(template) {
1592 labels.push(definition.label.clone());
1593 if let Some(item) = definition
1594 .authored
1595 .iter()
1596 .filter(|item| item.role == AuthoredRole::Conditional)
1597 .nth(*conditional)
1598 {
1599 labels.push(item.label.clone());
1600 }
1601 }
1602 // Native inclusion identities distinguish repeated calls to the same definition.
1603 for site in inclusions {
1604 labels.extend(
1605 definitions
1606 .values()
1607 .flat_map(|definition| definition.authored.iter())
1608 .filter(|item| {
1609 item.role == AuthoredRole::Use
1610 && item.relationship.as_ref() == Some(site)
1611 })
1612 .map(|item| item.label.clone()),
1613 );
1614 }
1615 return ModuleDiagnostic {
1616 message: error.to_string(),
1617 labels,
1618 cause: Some(crate::source_map::ModuleCause::new(error)),
1619 };
1620 }
1621 let slot = match &error {
1622 adam_rs::TemplateError::Factory { slot, .. }
1623 | adam_rs::TemplateError::ForeignHandle { slot }
1624 | adam_rs::TemplateError::InvalidDefinition {
1625 slot: Some(slot), ..
1626 } => Some(slot),
1627 _ => None,
1628 };
1629 let mut labels = vec![self.metadata.label.clone()];
1630 for definition in self.definitions().values() {
1631 if slot.is_some_and(|slot| definition.cells.contains_key(slot)) {
1632 labels.push(definition.label.clone());
1633 labels.extend(
1634 definition
1635 .authored
1636 .iter()
1637 .filter(|item| item.cell.as_ref() == slot)
1638 .map(|item| item.label.clone()),
1639 );
1640 }
1641 }
1642 ModuleDiagnostic {
1643 message: error.to_string(),
1644 labels,
1645 cause: Some(crate::source_map::ModuleCause::new(error)),
1646 }
1647 }
1648}
1649
1650/// Produces a source-free diagnostic with no invented location.
1651///
1652/// - Complexity: O(m) formatted message bytes.
1653pub(crate) fn diagnostic(error: impl std::fmt::Display) -> ModuleDiagnostic {
1654 let message = error.to_string();
1655 ModuleDiagnostic {
1656 cause: None,
1657 labels: vec![SourceLabel {
1658 location: None,
1659 label: message.clone(),
1660 }],
1661 message,
1662 }
1663}
1664
1665/// Converts complete shapes to the storage identities used only at the native boundary.
1666///
1667/// - Complexity: O(i) shapes.
1668pub(crate) fn storage_ids(shapes: &[TypeShape]) -> Vec<TypeId> {
1669 shapes.iter().map(crate::cell_value::cell_type_id).collect()
1670}
1671
1672/// Prepares a fresh canonical method frame and wraps the native callback boundary.
1673///
1674/// - Precondition: All named shapes are registered.
1675/// # Errors
1676/// Rejects incompatible emitted signatures or unavailable cell capabilities.
1677/// - Complexity: O(n + e + i + o) signature nodes, reads and edges.
1678fn frame_method(
1679 body: DynSegment,
1680 inputs: &[CellId],
1681 outputs: &[CellId],
1682 input_shapes: &[TypeShape],
1683 output_shapes: &[TypeShape],
1684 destructure: bool,
1685 types: &TypeRegistry,
1686) -> anyhow::Result<Method> {
1687 let frame = RefCell::new(Frame::new(
1688 body,
1689 input_shapes,
1690 output_shapes,
1691 destructure,
1692 types,
1693 )?);
1694 Ok(Method::new(
1695 inputs.to_vec(),
1696 outputs.to_vec(),
1697 storage_ids(input_shapes),
1698 storage_ids(output_shapes),
1699 move |args| frame.borrow_mut().call(args),
1700 ))
1701}