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 (subcontext_construction.md §2.2).
5//!
6//! The specs carry the lifecycle 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 (scope_model.md §3).
24/// Carried into the module's contract; finalize does not compare it
25/// against the parent (subcontext_construction.md §2.2).
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 /// (Rule 4 routes through the parent's coordinate buffer).
39 IterationExtern,
40}
41
42/// Lifecycle classification for an export — what kind of
43/// downstream contract this export carries.
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub enum ExportClassification {
46 /// Locally-defined output of the body. The default shape.
47 Local,
48 /// `const` modifier on the body's binding.
49 Final,
50 /// `shared` modifier on the body's binding — shared cell
51 /// available to descendants.
52 Shared,
53 /// Iteration variable (comprehension coordinate). Routed
54 /// through the parent's coord buffer at spawn (Rule 4).
55 Coordinate,
56 /// `volatile` modifier — excluded from const-fold identity.
57 Volatile,
58}
59
60/// Typed import declaration: a name the child's body will
61/// reference, expecting the parent to export it.
62#[derive(Debug, Clone)]
63pub struct ImportSpec {
64 /// Name as referenced inside the child body.
65 pub name: String,
66 /// Expected port type. Carried into the contract but not
67 /// compared against the parent; the child's slot type checks
68 /// protect the actual input (subcontext_construction.md §2.2).
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; finalize uses the export 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}