1use serde::{Deserialize, Serialize};
7use telltale_types::ValType;
8
9use crate::instr::{Endpoint, PC};
10use crate::session::{Edge, HandlerId, SessionId};
11
12fn default_cost_budget() -> usize {
13 usize::MAX
14}
15
16pub type RegFile = Vec<Value>;
18
19#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
21pub struct ProgressToken {
22 pub sid: SessionId,
24 pub endpoint: Endpoint,
26}
27
28impl ProgressToken {
29 #[must_use]
31 pub fn for_endpoint(endpoint: Endpoint) -> Self {
32 Self {
33 sid: endpoint.sid,
34 endpoint,
35 }
36 }
37}
38
39#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
41pub struct EffectCtx<E = ()> {
42 pub last_effect: Option<E>,
44}
45
46impl<E> Default for EffectCtx<E> {
47 fn default() -> Self {
48 Self { last_effect: None }
49 }
50}
51
52#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
54pub enum Value {
55 Unit,
57 Nat(u64),
59 Bool(bool),
61 Str(String),
63 Prod(Box<Value>, Box<Value>),
65 Endpoint(Endpoint),
67}
68
69#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
71pub enum CoroStatus {
72 Ready,
74 Blocked(BlockReason),
76 Done,
78 Faulted(Fault),
80 Speculating,
82}
83
84#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
86pub enum BlockReason {
87 #[serde(alias = "RecvWait")]
89 Recv {
90 edge: Edge,
92 token: ProgressToken,
94 },
95 #[serde(alias = "SendWait")]
97 Send {
98 edge: Edge,
100 },
101 #[serde(alias = "InvokeWait")]
103 Invoke {
104 handler: HandlerId,
106 },
107 AcquireDenied {
109 layer: String,
111 },
112 #[serde(alias = "ConsensusWait")]
114 Consensus {
115 tag: usize,
117 },
118 #[serde(alias = "SpawnWait")]
120 Spawn,
121 #[serde(alias = "CloseWait")]
123 Close {
124 sid: SessionId,
126 },
127}
128
129#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
131pub enum Fault {
132 TypeViolation {
134 expected: ValType,
136 actual: ValType,
138 message: String,
140 },
141 UnknownLabel {
143 label: String,
145 },
146 ChannelClosed {
148 endpoint: Endpoint,
150 },
151 InvalidSignature {
153 edge: Edge,
155 },
156 VerificationFailed {
158 edge: Edge,
160 message: String,
162 },
163 #[serde(alias = "InvokeFault")]
165 Invoke {
166 message: String,
168 },
169 #[serde(alias = "AcquireFault")]
171 Acquire {
172 layer: String,
174 message: String,
176 },
177 #[serde(alias = "TransferFault")]
179 Transfer {
180 message: String,
182 },
183 #[serde(alias = "SpecFault")]
185 Speculation {
186 message: String,
188 },
189 #[serde(alias = "CloseFault")]
191 Close {
192 message: String,
194 },
195 FlowViolation {
197 message: String,
199 },
200 NoProgressToken {
202 edge: Edge,
204 },
205 #[serde(alias = "OutputConditionFault")]
207 OutputCondition {
208 predicate_ref: String,
210 },
211 OutOfRegisters,
213 PcOutOfBounds,
215 BufferFull {
217 endpoint: Endpoint,
219 },
220 OutOfCredits,
222}
223
224impl std::fmt::Display for Fault {
225 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226 match self {
227 Self::TypeViolation {
228 expected,
229 actual,
230 message,
231 } => write!(
232 f,
233 "type violation (expected {expected:?}, actual {actual:?}): {message}"
234 ),
235 Self::UnknownLabel { label } => write!(f, "unknown label: {label}"),
236 Self::ChannelClosed { endpoint } => {
237 write!(f, "channel closed: {}:{}", endpoint.sid, endpoint.role)
238 }
239 Self::InvalidSignature { edge } => write!(
240 f,
241 "invalid signature on edge {}:{}→{}",
242 edge.sid, edge.sender, edge.receiver
243 ),
244 Self::VerificationFailed { edge, message } => write!(
245 f,
246 "verification failed on edge {}:{}→{}: {message}",
247 edge.sid, edge.sender, edge.receiver
248 ),
249 Self::Invoke { message } => write!(f, "invoke fault: {message}"),
250 Self::Acquire { layer, message } => {
251 write!(f, "acquire fault ({layer}): {message}")
252 }
253 Self::Transfer { message } => write!(f, "transfer fault: {message}"),
254 Self::Speculation { message } => write!(f, "speculation fault: {message}"),
255 Self::Close { message } => write!(f, "close fault: {message}"),
256 Self::FlowViolation { message } => write!(f, "flow violation: {message}"),
257 Self::NoProgressToken { edge } => write!(
258 f,
259 "missing progress token for edge {}:{}→{}",
260 edge.sid, edge.sender, edge.receiver
261 ),
262 Self::OutputCondition { predicate_ref } => {
263 write!(f, "output-condition rejected: {predicate_ref}")
264 }
265 Self::OutOfRegisters => write!(f, "out of registers"),
266 Self::PcOutOfBounds => write!(f, "PC out of bounds"),
267 Self::BufferFull { endpoint } => {
268 write!(f, "buffer full: {}:{}", endpoint.sid, endpoint.role)
269 }
270 Self::OutOfCredits => write!(f, "out of credits"),
271 }
272 }
273}
274
275#[derive(Debug, Clone, Serialize, Deserialize)]
277pub struct Coroutine<E = ()> {
278 pub id: usize,
280 pub program_id: usize,
282 pub pc: PC,
284 pub regs: RegFile,
286 pub status: CoroStatus,
288 #[serde(default)]
290 pub effect_ctx: EffectCtx<E>,
291 pub owned_endpoints: Vec<Endpoint>,
293 pub progress_tokens: Vec<ProgressToken>,
295 pub knowledge_set: KnowledgeSet,
297 pub spec_state: Option<SpeculationState>,
299 pub session_id: SessionId,
301 pub role: String,
303 #[serde(default = "default_cost_budget")]
305 pub cost_budget: usize,
306}
307
308pub type CoroutineState<E = ()> = Coroutine<E>;
310
311#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
313pub struct KnowledgeFact {
314 pub endpoint: Endpoint,
316 pub fact: String,
318}
319
320pub type KnowledgeSet = Vec<KnowledgeFact>;
322
323#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
325pub struct SpeculationState {
326 pub ghost_sid: usize,
328 pub depth: usize,
330}
331
332impl Coroutine {
333 #[must_use]
335 pub fn new(
336 id: usize,
337 program_id: usize,
338 session_id: SessionId,
339 role: String,
340 num_regs: u16,
341 cost_budget: usize,
342 ) -> Self {
343 Self {
344 id,
345 program_id,
346 pc: 0,
347 regs: vec![Value::Unit; usize::from(num_regs)],
348 status: CoroStatus::Ready,
349 effect_ctx: EffectCtx::default(),
350 owned_endpoints: Vec::with_capacity(1),
351 progress_tokens: Vec::with_capacity(1),
352 knowledge_set: Vec::with_capacity(1),
353 spec_state: None,
354 session_id,
355 role,
356 cost_budget,
357 }
358 }
359
360 #[must_use]
362 pub fn is_ready(&self) -> bool {
363 self.status == CoroStatus::Ready
364 }
365
366 #[must_use]
368 pub fn is_terminal(&self) -> bool {
369 matches!(self.status, CoroStatus::Done | CoroStatus::Faulted(_))
370 }
371}
372
373impl Fault {
374 #[must_use]
376 pub fn type_violation(message: impl Into<String>) -> Self {
377 Self::TypeViolation {
378 expected: ValType::Unit,
379 actual: ValType::Unit,
380 message: message.into(),
381 }
382 }
383}