polydat_core/kernel/subcontext/spec.rs
1// Copyright 2024-2026 Jonathan Shook
2// SPDX-License-Identifier: Apache-2.0
3
4//! Typed import / export contracts (per SRD-13e §1.2).
5//!
6//! The specs carry the SRD-13e taxonomy (lifecycle
7//! classifications, port types, binding modifiers) as data.
8//! What [`crate::kernel::subcontext::SubcontextBuilder::finalize`]
9//! enforces from them is fixed by
10//! `docs/design/subcontext_construction.md` §2.2: every import
11//! name must exist on the parent (`UnboundImport`), a child
12//! export may not shadow a parent `const` output (`FinalShadow`),
13//! and a child export matching an in-scope shared cell becomes a
14//! write-through binding. `port_type` and `classification` are
15//! preserved in the public `ScopeContract` but are not compared
16//! against a typed parent manifest; the child's input slots and
17//! shared-cell writes are protected by the compiler's slot type
18//! checks and by `kernel::state::check_write_through_type`.
19
20use crate::ast::PortType;
21use crate::dsl::ast::BindingModifier;
22
23/// Lifecycle classification for an import — taxonomically what
24/// SRD-13e §1.2 specifies. Drives the spawn-time validation
25/// decisions per SRD-67 Rule 1.
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum ImportClassification {
28 /// `final X: T` — fold the parent's value into the child at
29 /// compile/init time.
30 CompileConst,
31 /// `extern X: T` — wire to the parent's value via an input
32 /// slot. Most common shape.
33 Extern,
34 /// `shared X: T` — share-cell-attach against a parent's
35 /// `shared`-modifier export.
36 Shared,
37 /// Iteration extern: parent's coordinate / iteration variable
38 /// (SRD-67 Rule 4 routes through the parent's coordinate
39 /// buffer).
40 IterationExtern,
41}
42
43/// Lifecycle classification for an export — what kind of
44/// downstream contract this export carries.
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub enum ExportClassification {
47 /// Locally-defined output of the body. The default shape.
48 Local,
49 /// `const` modifier on the body's binding.
50 Final,
51 /// `shared` modifier on the body's binding — shared cell
52 /// available to descendants.
53 Shared,
54 /// Iteration variable (comprehension coordinate). Routed
55 /// through the parent's coord buffer at spawn (Rule 4).
56 Coordinate,
57 /// `volatile` modifier — excluded from const-fold identity.
58 Volatile,
59}
60
61/// Typed import declaration: a name the child's body will
62/// reference, expecting the parent to export it.
63#[derive(Debug, Clone)]
64pub struct ImportSpec {
65 /// Name as referenced inside the child body.
66 pub name: String,
67 /// Expected port type. Spawn validates against the parent's
68 /// matching export (Rule 1).
69 pub port_type: PortType,
70 /// Lifecycle classification.
71 pub classification: ImportClassification,
72}
73
74impl ImportSpec {
75 /// An import written to by the host, as an `extern` port.
76 pub fn extern_(name: impl Into<String>, port_type: PortType) -> Self {
77 Self {
78 name: name.into(),
79 port_type,
80 classification: ImportClassification::Extern,
81 }
82 }
83
84 /// An import fixed at compile time, from a parent `final` export.
85 pub fn final_(name: impl Into<String>, port_type: PortType) -> Self {
86 Self {
87 name: name.into(),
88 port_type,
89 classification: ImportClassification::CompileConst,
90 }
91 }
92
93 /// An import bound to a shared cell.
94 pub fn shared(name: impl Into<String>, port_type: PortType) -> Self {
95 Self {
96 name: name.into(),
97 port_type,
98 classification: ImportClassification::Shared,
99 }
100 }
101
102 /// An import rebound per activation of an enclosing iteration.
103 pub fn iter_var(name: impl Into<String>, port_type: PortType) -> Self {
104 Self {
105 name: name.into(),
106 port_type,
107 classification: ImportClassification::IterationExtern,
108 }
109 }
110}
111
112/// Typed export declaration: a named value the child produces,
113/// available to its own descendants.
114#[derive(Debug, Clone)]
115pub struct ExportSpec {
116 /// Name as it appears in the child's body.
117 pub name: String,
118 /// Port type the child binds.
119 pub port_type: PortType,
120 /// Modifier (`final` / `shared` / none) — the standard GK
121 /// modifier set; spawn uses it to apply Rule 2.
122 pub modifier: BindingModifier,
123 /// Lifecycle classification.
124 pub classification: ExportClassification,
125}
126
127impl ExportSpec {
128 /// A local export with no modifier.
129 pub fn local(name: impl Into<String>, port_type: PortType) -> Self {
130 Self {
131 name: name.into(),
132 port_type,
133 modifier: BindingModifier::NONE,
134 classification: ExportClassification::Local,
135 }
136 }
137
138 /// A `final` export: fixed once bound.
139 pub fn final_(name: impl Into<String>, port_type: PortType) -> Self {
140 Self {
141 name: name.into(),
142 port_type,
143 modifier: BindingModifier::CONST,
144 classification: ExportClassification::Final,
145 }
146 }
147
148 /// A `shared` export: a cell the descendants write through.
149 pub fn shared(name: impl Into<String>, port_type: PortType) -> Self {
150 Self {
151 name: name.into(),
152 port_type,
153 modifier: BindingModifier::SHARED,
154 classification: ExportClassification::Shared,
155 }
156 }
157
158 /// An export rebound per activation of an enclosing iteration.
159 pub fn iter_var(name: impl Into<String>, port_type: PortType) -> Self {
160 Self {
161 name: name.into(),
162 port_type,
163 modifier: BindingModifier::NONE,
164 classification: ExportClassification::Coordinate,
165 }
166 }
167}