Skip to main content

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}