cel_runtime/dyn_closure.rs
1//! A first-class, callable CEL value: a compiled body plus its declared signature.
2//!
3//! See `docs/superpowers/specs/2026-08-21-cel-closures-design.md` for the full design rationale
4//! (why `Rc`, why `RefCell`, why no captured environment).
5//!
6//! # Examples
7//!
8//! Build a closure that adds two `i32` values, then call it with different arguments:
9//!
10//! ```rust
11//! use cel_runtime::{DynClosure, DynSegment};
12//! use std::any::TypeId;
13//!
14//! // Compile a body that takes two i32 arguments and returns their sum.
15//! let mut body = DynSegment::new::<()>();
16//! body.push_arg::<i32>(0);
17//! body.push_arg::<i32>(1);
18//! body.op2(|a: i32, b: i32| a + b).unwrap();
19//!
20//! // Wrap it as a callable closure with its declared signature.
21//! let closure = DynClosure::new(
22//! vec![TypeId::of::<i32>(), TypeId::of::<i32>()],
23//! TypeId::of::<i32>(),
24//! body,
25//! );
26//!
27//! // Call the closure multiple times with different arguments.
28//! let (a, b) = (2i32, 3i32);
29//! assert_eq!(closure.call::<i32>(&[&a, &b]).unwrap(), 5);
30//!
31//! let (c, d) = (10i32, 20i32);
32//! assert_eq!(closure.call::<i32>(&[&c, &d]).unwrap(), 30);
33//! ```
34
35use std::any::{Any, TypeId};
36use std::cell::{Cell, RefCell};
37use std::rc::Rc;
38
39use crate::ValueType;
40use crate::dyn_segment::DynSegment;
41
42/// An exact compile-time callable shape, including nested aggregate and callable results.
43#[derive(Clone, Debug)]
44pub(crate) struct ClosureSignature {
45 pub(crate) parameters: Vec<ValueType>,
46 pub(crate) result: ValueType,
47}
48
49impl ClosureSignature {
50 /// Converts known recursive metadata without changing canonical storage.
51 ///
52 /// # Errors
53 /// Returns an error for missing or mismatched leaf factories, unknown nested
54 /// callable signatures, or an unrepresentable canonical tuple layout.
55 /// - Complexity: O(n) traversal time and output space for n signature nodes,
56 /// excluding leaf factory costs.
57 fn complete(
58 &self,
59 leaf: &mut impl FnMut(TypeId) -> Option<crate::value_type::ValueType>,
60 ) -> anyhow::Result<crate::value_type::ValueType> {
61 let parameters = self
62 .parameters
63 .iter()
64 .map(|parameter| enrich_value_type(parameter, leaf))
65 .collect::<anyhow::Result<Vec<_>>>()?;
66 Ok(crate::value_type::ValueType::callable(
67 parameters,
68 enrich_value_type(&self.result, leaf)?,
69 ))
70 }
71}
72
73/// Resolves one concrete registered leaf through a previously monomorphized factory.
74///
75/// # Errors
76/// Returns an error if the factory is missing, structural, or describes another
77/// concrete type or layout. A closure marker without a signature is rejected.
78fn complete_leaf(
79 id: TypeId,
80 size: usize,
81 align: usize,
82 leaf: &mut impl FnMut(TypeId) -> Option<crate::value_type::ValueType>,
83) -> anyhow::Result<crate::value_type::ValueType> {
84 anyhow::ensure!(
85 id != TypeId::of::<DynClosure>(),
86 "closure signature metadata is unavailable"
87 );
88 let complete =
89 leaf(id).ok_or_else(|| anyhow::anyhow!("complete leaf factory is unavailable"))?;
90 complete.validate_opaque_leaf(id)?;
91 anyhow::ensure!(
92 complete.layout().size() == size && complete.layout().align() == align,
93 "complete leaf factory layout mismatch"
94 );
95 Ok(complete)
96}
97
98/// Enriches canonical signature leaves through explicitly supplied factories.
99///
100/// # Errors
101/// Returns an error for unavailable signatures/factories or invalid tuple layout.
102/// - Complexity: O(n) time and output space for n recursive descriptor nodes.
103fn enrich_value_type(
104 value: &ValueType,
105 leaf: &mut impl FnMut(TypeId) -> Option<crate::value_type::ValueType>,
106) -> anyhow::Result<crate::value_type::ValueType> {
107 use crate::value_type::{AssociatedType, ValueType as Complete};
108 match value.kind() {
109 crate::ValueKind::Tuple(fields) => Complete::tuple(
110 fields
111 .iter()
112 .map(|field| {
113 Ok(AssociatedType {
114 offset: 0,
115 value_type: enrich_value_type(&field.value_type, leaf)?,
116 })
117 })
118 .collect::<anyhow::Result<Vec<_>>>()?,
119 ),
120 crate::ValueKind::Array(element) => Ok(Complete::array(enrich_value_type(element, leaf)?)),
121 crate::ValueKind::Callable { parameters, result } => Ok(Complete::callable(
122 parameters
123 .iter()
124 .map(|parameter| enrich_value_type(parameter, leaf))
125 .collect::<anyhow::Result<_>>()?,
126 enrich_value_type(result, leaf)?,
127 )),
128 crate::ValueKind::Leaf(id) => complete_leaf(*id, value.size(), value.align(), leaf),
129 }
130}
131
132/// Compares complete known signatures without relying on the erased closure marker.
133///
134/// - Complexity: O(number of recursive type nodes).
135#[cfg(test)]
136pub(crate) fn same_signature(
137 left: Option<&ClosureSignature>,
138 right: Option<&ClosureSignature>,
139) -> bool {
140 match (left, right) {
141 (None, None) => true,
142 (Some(a), Some(b)) => {
143 a.parameters.len() == b.parameters.len()
144 && a.parameters
145 .iter()
146 .zip(&b.parameters)
147 .all(|(a, b)| a.same_shape(b))
148 && a.result.same_shape(&b.result)
149 }
150 _ => false,
151 }
152}
153
154/// A monomorphized dispatcher for [`DynClosure::call_boxed`]: runs a closure's body via
155/// `DynSegment::call_dyn::<R>` for some concrete `R` the dispatcher itself already knows, boxing
156/// the result so the caller (which only knows `R` dynamically, as a `TypeId`) doesn't need to.
157pub type CallDynFn = fn(&mut DynSegment, &[&dyn Any]) -> anyhow::Result<Box<dyn Any>>;
158
159/// Invokes an exact construction-bound payload and initializes output on success only.
160///
161/// Both frame pointers and the payload must match the dispatcher's concrete types.
162pub(crate) type FrameDispatch = unsafe fn(*mut (), *const u8, *mut u8) -> anyhow::Result<()>;
163
164/// Owns either a compiler-facing body or a private type-bound frame dispatcher.
165enum ClosureBody {
166 Interpreted(DynSegment),
167 Framed {
168 payload: Box<dyn Any>,
169 dispatch: FrameDispatch,
170 },
171}
172
173impl ClosureBody {
174 /// Returns the compiler-facing body, rejecting immutable typed facade bodies.
175 ///
176 /// # Errors
177 /// Rejects framed native and prepared bodies.
178 fn segment(&mut self) -> anyhow::Result<&mut DynSegment> {
179 match self {
180 Self::Interpreted(body) => Ok(body),
181 Self::Framed { .. } => anyhow::bail!("typed callable has no mutable compiler body"),
182 }
183 }
184}
185
186/// Binds a canonical contiguous argument frame and a raw interpreted result once.
187struct FramePlan {
188 offsets: Vec<usize>,
189 result: crate::container_execution::ResultPlan,
190}
191
192/// Shares one body and counts live trusted-binding leases across all carrier aliases.
193struct ClosureData {
194 param_types: Vec<TypeId>,
195 return_type: TypeId,
196 body: RefCell<ClosureBody>,
197 frame: Option<FramePlan>,
198 signature: Option<std::sync::Arc<ClosureSignature>>,
199 trusted_bindings: Cell<usize>,
200}
201
202/// Prevents arbitrary mutable body dispatch until this independent lease is dropped.
203///
204/// Holds the shared carrier alive; moving the lease preserves the freeze, and
205/// dropping it releases exactly one binding even during unwinding. Carrier clones
206/// share the counter but neither acquire nor release leases themselves.
207pub(crate) struct TrustedBindingLease {
208 closure: DynClosure,
209}
210
211impl Drop for TrustedBindingLease {
212 /// Decrements the lease count without borrowing the body, before releasing the carrier.
213 fn drop(&mut self) {
214 let bindings = &self.closure.0.trusted_bindings;
215 debug_assert!(bindings.get() > 0);
216 bindings.set(bindings.get() - 1);
217 }
218}
219
220/// `Debug` implementation that formats the closure's signature but deliberately omits `body` from the output
221/// (via `finish_non_exhaustive`), since `DynSegment` is not easily debuggable and the signature alone
222/// is typically what callers need to see.
223impl std::fmt::Debug for ClosureData {
224 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
225 f.debug_struct("ClosureData")
226 .field("param_types", &self.param_types)
227 .field("return_type", &self.return_type)
228 .finish_non_exhaustive()
229 }
230}
231
232/// A first-class, callable CEL value: a compiled body plus its declared parameter/return types.
233///
234/// Holds no captured environment — only its own parameters resolve inside `body`. Calling it
235/// twice with different `args` is exactly as fresh each time as calling any other `DynSegment`.
236/// `Rc`-wrapped so `Clone` never requires `DynSegment` itself to implement `Clone` (it doesn't —
237/// see the design spec); `RefCell` because callers only ever reach a `DynClosure` through `&self`
238/// (matching `adam_rs::Filter`/`Method`'s `Fn`, not `FnMut`, storage), while `DynSegment`'s own
239/// call methods need `&mut self`.
240#[derive(Clone, Debug)]
241pub struct DynClosure(Rc<ClosureData>, Option<Rc<TrustedBindingLease>>);
242
243impl std::fmt::Debug for TrustedBindingLease {
244 /// Formats the lease without recursively traversing its retained carrier.
245 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
246 f.write_str("TrustedBindingLease")
247 }
248}
249
250impl DynClosure {
251 /// Shares a construction-bound frame dispatcher without exposing its payload.
252 ///
253 /// A prepared payload owns its immutable body and all nested trusted leases.
254 ///
255 /// # Safety
256 /// `dispatch` must match `payload` and the facade's exact Rust signature.
257 /// On success it must initialize exactly one result; on error or unwind it
258 /// must leave output uninitialized. If supplied, metadata must describe that
259 /// exact signature.
260 /// - Complexity: O(p) parameter metadata storage for p declared parameters.
261 pub(crate) unsafe fn from_frame_body(
262 payload: Box<dyn Any>,
263 dispatch: FrameDispatch,
264 signature: Option<ClosureSignature>,
265 ) -> Self {
266 let param_types = signature
267 .as_ref()
268 .map(|s| s.parameters.iter().map(ValueType::type_id).collect())
269 .unwrap_or_default();
270 let return_type = signature
271 .as_ref()
272 .map(|s| s.result.type_id())
273 .unwrap_or(TypeId::of::<()>());
274 Self(
275 Rc::new(ClosureData {
276 param_types,
277 return_type,
278 body: RefCell::new(ClosureBody::Framed { payload, dispatch }),
279 frame: None,
280 signature: signature.map(std::sync::Arc::new),
281 trusted_bindings: Cell::new(0),
282 }),
283 None,
284 )
285 }
286
287 /// Dispatches a complete preparation-validated contiguous borrowed argument frame.
288 ///
289 /// # Safety
290 /// Both frame pointers must match this exact carrier's validated signature.
291 /// Output is aligned uninitialized result storage. A compiler-facing interpreted body
292 /// must be protected by a trusted lease for the duration of the call.
293 /// # Errors
294 /// Returns reentrant entry or original body errors without initializing output.
295 /// - Complexity: Body-dependent; interpreted bodies set up O(a) pointers.
296 pub(crate) unsafe fn call_frame_trusted(
297 &self,
298 args: *const u8,
299 result: *mut u8,
300 ) -> anyhow::Result<()> {
301 let mut body = self
302 .0
303 .body
304 .try_borrow_mut()
305 .map_err(|_| anyhow::anyhow!("closure body is already executing"))?;
306 match &mut *body {
307 ClosureBody::Framed { payload, dispatch } => {
308 // Erasing an Any pointer preserves the boxed concrete data
309 // address; construction binds its monomorphized dispatcher.
310 let pointer = std::ptr::from_mut(payload.as_mut()).cast::<()>();
311 unsafe { dispatch(pointer, args, result) }
312 }
313 ClosureBody::Interpreted(body) => {
314 let frame = self.0.frame.as_ref().expect("validated canonical body");
315 let arguments: Vec<_> = frame
316 .offsets
317 .iter()
318 .map(|offset| unsafe { args.add(*offset) })
319 .collect();
320 let mut stack = unsafe { body.execute_raw(&arguments)? };
321 let mut output = unsafe { frame.result.take(&mut stack) };
322 unsafe { output.move_into(result) };
323 Ok(())
324 }
325 }
326 }
327
328 /// Returns the complete canonical callable descriptor after validating its current body.
329 ///
330 /// # Errors
331 /// Rejects untyped closures, executing bodies, or bodies changed incompatibly
332 /// through an unfrozen mutable dispatcher.
333 /// - Complexity: O(n) complete comparisons for n signature/body descriptor nodes.
334 ///
335 /// # Examples
336 /// ```
337 /// let mut body = cel_runtime::DynSegment::new::<()>(); body.just(7i32);
338 /// let closure = cel_runtime::DynClosure::typed(vec![], body).unwrap();
339 /// assert!(matches!(closure.value_type().unwrap().kind(), cel_runtime::ValueKind::Callable { .. }));
340 /// ```
341 pub fn value_type(&self) -> anyhow::Result<ValueType> {
342 self.validate_body_signature()?;
343 let signature = self
344 .0
345 .signature
346 .as_ref()
347 .expect("validated typed signature");
348 Ok(ValueType::callable(
349 signature.parameters.clone(),
350 signature.result.clone(),
351 ))
352 }
353
354 /// Wraps `body` as a closure value declaring `param_types` (in order) and `return_type`.
355 ///
356 /// - Precondition: `body` was compiled expecting exactly `param_types.len()` positional
357 /// arguments (via `push_arg`/`push_argument`), in that order, and
358 /// produces exactly one result of `return_type`.
359 ///
360 /// # Examples
361 ///
362 /// ```rust
363 /// use cel_runtime::{DynClosure, DynSegment};
364 /// use std::any::TypeId;
365 ///
366 /// let mut body = DynSegment::new::<()>();
367 /// body.push_arg::<i32>(0);
368 /// body.push_arg::<i32>(1);
369 /// body.op2(|a: i32, b: i32| a + b).unwrap();
370 ///
371 /// let closure = DynClosure::new(
372 /// vec![TypeId::of::<i32>(), TypeId::of::<i32>()],
373 /// TypeId::of::<i32>(),
374 /// body,
375 /// );
376 /// assert_eq!(closure.param_types().len(), 2);
377 /// ```
378 #[must_use]
379 pub fn new(param_types: Vec<TypeId>, return_type: TypeId, body: DynSegment) -> Self {
380 DynClosure(
381 Rc::new(ClosureData {
382 param_types,
383 return_type,
384 body: RefCell::new(ClosureBody::Interpreted(body)),
385 frame: None,
386 signature: None,
387 trusted_bindings: Cell::new(0),
388 }),
389 None,
390 )
391 }
392
393 /// Wraps a checked CEL body while retaining its full callable shape.
394 ///
395 /// # Errors
396 /// Returns an error if the body does not produce exactly one value, requires preloaded
397 /// arguments or a nonzero ambient stack origin, reads an undeclared or differently shaped
398 /// argument, or reads a tuple whose leaves lack clone adapters. A zero-origin fragment is
399 /// valid because its operations can access only its local operands and declared arguments.
400 ///
401 /// - Complexity: O(number of parameters plus recursive nodes in body argument reads/result).
402 ///
403 /// # Examples
404 /// ```
405 /// use cel_runtime::{DynClosure, DynSegment, ValueType};
406 /// let mut body = DynSegment::new::<()>();
407 /// body.just(42i32);
408 /// let closure = DynClosure::typed(vec![], body).unwrap();
409 /// assert_eq!(closure.call::<i32>(&[]).unwrap(), 42);
410 /// ```
411 pub fn typed(parameters: Vec<ValueType>, body: DynSegment) -> anyhow::Result<Self> {
412 let result = Self::validate_body(¶meters, &body)?;
413 let tuple = ValueType::tuple(
414 parameters
415 .iter()
416 .map(|value_type| crate::AssociatedType {
417 offset: 0,
418 value_type: value_type.clone(),
419 })
420 .collect(),
421 )?;
422 let frame = FramePlan {
423 offsets: tuple
424 .tuple_elements()
425 .unwrap()
426 .iter()
427 .map(|f| f.offset)
428 .collect(),
429 result: crate::container_execution::ResultPlan::new(0, result.clone())?,
430 };
431 let param_types = parameters.iter().map(ValueType::type_id).collect();
432 Ok(Self(
433 Rc::new(ClosureData {
434 param_types,
435 return_type: result.type_id(),
436 signature: Some(std::sync::Arc::new(ClosureSignature { parameters, result })),
437 body: RefCell::new(ClosureBody::Interpreted(body)),
438 frame: Some(frame),
439 trusted_bindings: Cell::new(0),
440 }),
441 None,
442 ))
443 }
444
445 /// Returns a carrier whose clones retain a trusted freeze for their own lifetime.
446 ///
447 /// # Errors
448 /// Rejects an executing body or an unrepresentable additional lease.
449 pub(crate) fn bind_canonical(mut self) -> anyhow::Result<Self> {
450 if self.1.is_none() {
451 self.1 = Some(Rc::new(self.freeze_trusted_binding()?));
452 }
453 Ok(self)
454 }
455
456 /// Freezes arbitrary mutable body access across all aliases until the returned lease drops.
457 ///
458 /// This does not validate the signature; preparation must do so before binding.
459 /// Existing nonmutating execution paths remain available while leased.
460 ///
461 /// # Errors
462 /// Rejects a mutably borrowed body (including an active mutable dispatcher)
463 /// or an unrepresentable additional lease count without changing the counter.
464 pub(crate) fn freeze_trusted_binding(&self) -> anyhow::Result<TrustedBindingLease> {
465 let _body = self
466 .0
467 .body
468 .try_borrow()
469 .map_err(|_| anyhow::anyhow!("closure body is already executing"))?;
470 let count = self
471 .0
472 .trusted_bindings
473 .get()
474 .checked_add(1)
475 .ok_or_else(|| anyhow::anyhow!("too many trusted bindings"))?;
476 let lease = TrustedBindingLease {
477 closure: self.clone(),
478 };
479 self.0.trusted_bindings.set(count);
480 Ok(lease)
481 }
482
483 /// Validates independent execution and declared argument reads, returning the actual result.
484 ///
485 /// # Errors
486 /// Rejects non-single results, dependent bodies, undeclared or mismatched reads,
487 /// and tuple reads without clone adapters.
488 /// - Complexity: O(recursive nodes in body argument reads and result).
489 fn validate_body(parameters: &[ValueType], body: &DynSegment) -> anyhow::Result<ValueType> {
490 anyhow::ensure!(
491 body.stack_ids.len() == 1,
492 "closure body must produce exactly one value"
493 );
494 body.validate_independent_body()?;
495 for (index, read_type) in &body.argument_reads {
496 anyhow::ensure!(
497 parameters
498 .get(*index)
499 .is_some_and(|expected| expected.same_shape(read_type)),
500 "closure body argument {index} differs from its declared signature"
501 );
502 read_type.validate_clone()?;
503 }
504 Ok(body.peek_stack_infos(1)[0].value_type.clone())
505 }
506
507 /// Checks the current mutable body against its retained typed signature during preparation.
508 ///
509 /// An external mutable dispatcher may have replaced the body without updating its metadata.
510 ///
511 /// # Errors
512 /// Rejects unavailable signatures, an executing body, invalid independent bodies
513 /// or argument reads, and a result differing from the retained signature.
514 /// - Complexity: O(recursive nodes in body argument reads and result).
515 pub(crate) fn validate_body_signature(&self) -> anyhow::Result<()> {
516 let signature = self
517 .0
518 .signature
519 .as_ref()
520 .ok_or_else(|| anyhow::anyhow!("closure signature metadata is unavailable"))?;
521 let body = self
522 .0
523 .body
524 .try_borrow()
525 .map_err(|_| anyhow::anyhow!("closure body is already executing"))?;
526 let ClosureBody::Interpreted(body) = &*body else {
527 return Ok(());
528 };
529 let result = Self::validate_body(&signature.parameters, body)?;
530 anyhow::ensure!(
531 signature.result.same_shape(&result),
532 "closure body result differs from its declared signature"
533 );
534 Ok(())
535 }
536
537 /// Returns the metadata retained for a typed CEL closure.
538 pub(crate) fn signature(&self) -> Option<std::sync::Arc<ClosureSignature>> {
539 self.0.signature.clone()
540 }
541
542 /// Enriches a complete canonical signature with explicitly supplied leaf factories.
543 ///
544 /// `leaf` looks up descriptors built while each concrete Rust type was
545 /// known; a `TypeId` never synthesizes a Rust monomorphization.
546 /// Factory capabilities remain independent of identity. This changes metadata
547 /// only; [`Self::value_type`] needs no factories for the existing complete signature.
548 ///
549 /// # Errors
550 ///
551 /// Returns an error for an untyped closure (including [`Self::new`]), an
552 /// unknown nested callable signature, a missing or mismatched concrete leaf
553 /// factory, or an unrepresentable canonical tuple layout.
554 ///
555 /// - Complexity: O(n) traversal time and output space for n signature nodes,
556 /// excluding leaf factory costs.
557 ///
558 /// # Examples
559 ///
560 /// ```
561 /// use cel_runtime::{DynClosure, DynSegment, value_type::{ValueKind, ValueType}};
562 /// use std::any::TypeId;
563 /// let mut body = DynSegment::new::<()>();
564 /// body.just(42i32);
565 /// let closure = DynClosure::typed(vec![], body).unwrap();
566 /// let complete = closure.complete_value_type(|id| {
567 /// (id == TypeId::of::<i32>()).then(ValueType::cloneable_leaf::<i32>)
568 /// }).unwrap();
569 /// assert!(matches!(complete.kind(), ValueKind::Callable { .. }));
570 /// ```
571 pub fn complete_value_type(
572 &self,
573 mut leaf: impl FnMut(TypeId) -> Option<crate::value_type::ValueType>,
574 ) -> anyhow::Result<crate::value_type::ValueType> {
575 self.0
576 .signature
577 .as_ref()
578 .ok_or_else(|| anyhow::anyhow!("closure signature metadata is unavailable"))?
579 .complete(&mut leaf)
580 }
581
582 /// Evaluates a previously checked borrowed raw argument frame into an independent stack.
583 ///
584 /// # Safety
585 /// Every pointer must reference a live value matching `expected.parameters`, in order,
586 /// until execution returns. No pointer may be retained by an operation.
587 ///
588 /// # Errors
589 /// Returns signature mismatch, reentrant body borrow, or body evaluation errors.
590 /// - Complexity: body-dependent.
591 #[cfg(test)]
592 pub(crate) unsafe fn call_raw(
593 &self,
594 expected: &ClosureSignature,
595 arguments: &[*const u8],
596 ) -> anyhow::Result<crate::raw_stack::RawStack> {
597 debug_assert_eq!(expected.parameters.len(), arguments.len());
598 anyhow::ensure!(
599 same_signature(self.0.signature.as_deref(), Some(expected)),
600 "closure signature mismatch"
601 );
602 let mut body = self
603 .0
604 .body
605 .try_borrow_mut()
606 .map_err(|_| anyhow::anyhow!("closure body is already executing"))?;
607 unsafe { body.segment()?.execute_raw(arguments) }
608 }
609
610 /// Executes this carrier with a construction-validated, representation-matched frame.
611 ///
612 /// This path deliberately omits the external signature check. The prepared
613 /// invocation owns a clone of this exact carrier, preventing substitution.
614 ///
615 /// # Safety
616 ///
617 /// Preparation must validate the actual body's independent origin, argument reads,
618 /// and result against this closure's full signature and its canonical physical
619 /// frame. Every pointer must remain a live matching borrowed value
620 /// until execution returns; operations must not retain argument pointers.
621 /// A trusted-binding lease must protect the validated body from modification
622 /// throughout preparation and execution.
623 ///
624 /// # Errors
625 ///
626 /// Returns reentrant body borrow or body evaluation errors.
627 /// - Complexity: Body-dependent time and independent evaluation stack storage.
628 #[cfg(test)]
629 pub(crate) unsafe fn call_raw_trusted(
630 &self,
631 arguments: &[*const u8],
632 ) -> anyhow::Result<crate::raw_stack::RawStack> {
633 debug_assert_eq!(arguments.len(), self.0.param_types.len());
634 debug_assert!(self.0.signature.is_some());
635 let mut body = self
636 .0
637 .body
638 .try_borrow_mut()
639 .map_err(|_| anyhow::anyhow!("closure body is already executing"))?;
640 unsafe { body.segment()?.execute_raw(arguments) }
641 }
642
643 /// Returns this closure's declared parameter types, in order.
644 ///
645 /// # Examples
646 ///
647 /// ```rust
648 /// use cel_runtime::{DynClosure, DynSegment};
649 /// use std::any::TypeId;
650 ///
651 /// let mut body = DynSegment::new::<()>();
652 /// body.push_arg::<i32>(0);
653 /// body.push_arg::<i32>(1);
654 /// body.op2(|a: i32, b: i32| a + b).unwrap();
655 ///
656 /// let closure = DynClosure::new(
657 /// vec![TypeId::of::<i32>(), TypeId::of::<i32>()],
658 /// TypeId::of::<i32>(),
659 /// body,
660 /// );
661 /// assert_eq!(closure.param_types(), &[TypeId::of::<i32>(), TypeId::of::<i32>()]);
662 /// ```
663 #[must_use]
664 pub fn param_types(&self) -> &[TypeId] {
665 &self.0.param_types
666 }
667
668 /// Returns this closure's declared return type.
669 ///
670 /// # Examples
671 ///
672 /// ```rust
673 /// use cel_runtime::{DynClosure, DynSegment};
674 /// use std::any::TypeId;
675 ///
676 /// let mut body = DynSegment::new::<()>();
677 /// body.push_arg::<i32>(0);
678 /// body.push_arg::<i32>(1);
679 /// body.op2(|a: i32, b: i32| a + b).unwrap();
680 ///
681 /// let closure = DynClosure::new(
682 /// vec![TypeId::of::<i32>(), TypeId::of::<i32>()],
683 /// TypeId::of::<i32>(),
684 /// body,
685 /// );
686 /// assert_eq!(closure.return_type(), TypeId::of::<i32>());
687 /// ```
688 #[must_use]
689 pub fn return_type(&self) -> TypeId {
690 self.0.return_type
691 }
692
693 /// Invokes the closure's body with `args`, positionally matched against `param_types`.
694 ///
695 /// - Precondition: `args.len() == self.param_types().len()` and each `args[i]`'s runtime type
696 /// matches `param_types()[i]` — the caller (adam-lang's generated `Filter` wrapper, or any
697 /// future caller) must guarantee this ahead of time; a violation is a caller bug, not user
698 /// error (matches `DynSegment::push_arg`'s own existing precondition, which this delegates
699 /// to unchanged).
700 /// - Precondition: `TypeId::of::<R>() == self.return_type()`.
701 /// - Complexity: whatever the body's own evaluation complexity is.
702 ///
703 /// # Examples
704 ///
705 /// ```rust
706 /// use cel_runtime::{DynClosure, DynSegment};
707 /// use std::any::TypeId;
708 ///
709 /// let mut body = DynSegment::new::<()>();
710 /// body.push_arg::<i32>(0);
711 /// body.push_arg::<i32>(1);
712 /// body.op2(|a: i32, b: i32| a + b).unwrap();
713 ///
714 /// let closure = DynClosure::new(
715 /// vec![TypeId::of::<i32>(), TypeId::of::<i32>()],
716 /// TypeId::of::<i32>(),
717 /// body,
718 /// );
719 /// let (a, b) = (2i32, 3i32);
720 /// let result: i32 = closure.call(&[&a, &b]).unwrap();
721 /// assert_eq!(result, 5);
722 /// ```
723 pub fn call<R: 'static>(&self, args: &[&dyn Any]) -> anyhow::Result<R> {
724 debug_assert_eq!(args.len(), self.0.param_types.len());
725 self.0
726 .body
727 .try_borrow_mut()
728 .map_err(|_| anyhow::anyhow!("closure body is already executing"))?
729 .segment()?
730 .call_dyn::<R>(args)
731 }
732
733 /// Invokes the closure's body with `args`, like [`call`](Self::call), for a caller that only
734 /// knows the return type dynamically (as a `TypeId`) rather than as a static Rust generic —
735 /// `call_dyn_fn` is a monomorphized dispatcher the caller already has for that type (e.g.
736 /// `adam-lang`'s per-type `TypeRegistry::TypeEntry::call_dyn_fn`).
737 ///
738 /// - Precondition: `args.len() == self.param_types().len()` and each `args[i]`'s runtime type
739 /// matches `param_types()[i]`, exactly as for [`call`](Self::call).
740 /// - Precondition: `call_dyn_fn` is a dispatcher for `self.return_type()` (i.e. it calls
741 /// `DynSegment::call_dyn::<R>` for the same concrete `R` that `TypeId` names).
742 /// - Complexity: whatever the body's own evaluation complexity is.
743 ///
744 /// # Errors
745 ///
746 /// Returns an error without invoking `call_dyn_fn` while any trusted binding
747 /// leases this shared body, including through another clone. Mutable dispatch
748 /// resumes after the last lease drops. Otherwise propagates dispatcher errors.
749 ///
750 /// # Examples
751 ///
752 /// ```rust
753 /// use cel_runtime::{DynClosure, DynSegment};
754 /// use std::any::{Any, TypeId};
755 ///
756 /// let mut body = DynSegment::new::<()>();
757 /// body.push_arg::<i32>(0);
758 /// body.push_arg::<i32>(1);
759 /// body.op2(|a: i32, b: i32| a + b).unwrap();
760 ///
761 /// let closure = DynClosure::new(
762 /// vec![TypeId::of::<i32>(), TypeId::of::<i32>()],
763 /// TypeId::of::<i32>(),
764 /// body,
765 /// );
766 ///
767 /// fn dispatch_i32(seg: &mut DynSegment, inputs: &[&dyn Any]) -> anyhow::Result<Box<dyn Any>> {
768 /// let result: i32 = seg.call_dyn(inputs)?;
769 /// Ok(Box::new(result))
770 /// }
771 ///
772 /// let (a, b) = (4i32, 5i32);
773 /// let result = closure.call_boxed(&[&a, &b], dispatch_i32).unwrap();
774 /// assert_eq!(*result.downcast_ref::<i32>().unwrap(), 9);
775 /// ```
776 pub fn call_boxed(
777 &self,
778 args: &[&dyn Any],
779 call_dyn_fn: CallDynFn,
780 ) -> anyhow::Result<Box<dyn Any>> {
781 debug_assert_eq!(args.len(), self.0.param_types.len());
782 anyhow::ensure!(
783 self.0.trusted_bindings.get() == 0,
784 "closure body is frozen by a trusted binding"
785 );
786 let mut body = self
787 .0
788 .body
789 .try_borrow_mut()
790 .map_err(|_| anyhow::anyhow!("closure body is already executing"))?;
791 call_dyn_fn(body.segment()?, args)
792 }
793}
794
795#[cfg(test)]
796mod tests {
797 use super::*;
798 use crate::dyn_segment::DynSegment;
799
800 /// Clones and drops the existing shared carrier without erasing its signature.
801 #[test]
802 fn complete_factory_callable_owns_shared_carrier() {
803 use crate::value_type::ValueType as Complete;
804 use std::mem::{ManuallyDrop, MaybeUninit};
805 let mut body = DynSegment::new::<()>();
806 body.just(42i32);
807 let closure = DynClosure::typed(vec![], body).unwrap();
808 let weak = Rc::downgrade(&closure.0);
809 let descriptor = Complete::callable(vec![], Complete::leaf::<i32>());
810 assert_eq!(descriptor.layout(), std::alloc::Layout::new::<DynClosure>());
811 assert!(descriptor.can_clone());
812 assert!(descriptor.can_debug());
813 assert!(!descriptor.can_compare());
814 let mut source = ManuallyDrop::new(closure);
815 let source_ptr = (&mut *source as *mut DynClosure).cast::<u8>();
816 let mut destination = MaybeUninit::<DynClosure>::uninit();
817 // SAFETY: Both locations have the statically known carrier layout and
818 // the descriptor describes this closure's signature.
819 unsafe {
820 crate::owned_value::clone_raw(&descriptor, source_ptr, destination.as_mut_ptr().cast());
821 crate::owned_value::drop_raw(&descriptor, source_ptr);
822 assert!(weak.upgrade().is_some());
823 assert_eq!(destination.assume_init_ref().call::<i32>(&[]).unwrap(), 42);
824 crate::owned_value::drop_raw(&descriptor, destination.as_mut_ptr().cast());
825 }
826 assert!(weak.upgrade().is_none());
827 }
828
829 /// Keeps tuple and array identities in migrated signatures.
830 #[test]
831 fn complete_factory_preserves_structural_callable_signatures() {
832 use crate::value_type::{ValueKind, ValueType as Complete};
833 let mut body = DynSegment::new::<()>();
834 body.just(1i32);
835 body.make_tuple(1, 0).unwrap();
836 let parameter = ValueType::array(ValueType::cloneable_leaf::<i32>());
837 let closure = DynClosure::typed(vec![parameter], body).unwrap();
838 let descriptor = closure
839 .complete_value_type(|id| {
840 (id == TypeId::of::<i32>()).then(Complete::cloneable_leaf::<i32>)
841 })
842 .unwrap();
843 let ValueKind::Callable { parameters, result } = descriptor.kind() else {
844 panic!("expected complete callable metadata");
845 };
846 assert!(parameters[0].same_shape(&Complete::array(Complete::leaf::<i32>())));
847 assert!(!parameters[0].same_shape(&Complete::array(Complete::leaf::<u32>())));
848 assert!(matches!(result.kind(), ValueKind::Tuple(fields) if fields.len() == 1));
849 assert!(!result.same_shape(&Complete::leaf::<i32>()));
850 assert!(!descriptor.same_shape(&Complete::callable(
851 vec![Complete::array(Complete::leaf::<u32>())],
852 (**result).clone()
853 )));
854 assert!(!descriptor.same_shape(&Complete::callable(
855 parameters.clone(),
856 Complete::leaf::<i32>()
857 )));
858 }
859
860 /// Retains known nested callable results and distinguishes canonical source unit.
861 #[test]
862 fn complete_factory_preserves_nested_callable_and_unit() {
863 use crate::value_type::{Nil, ValueKind, ValueRepr, ValueType as Complete};
864 let mut inner_body = DynSegment::new::<()>();
865 inner_body.make_tuple(0, 0).unwrap();
866 let inner = DynClosure::typed(vec![ValueType::leaf::<()>()], inner_body).unwrap();
867 let mut body = DynSegment::new::<()>();
868 body.push_closure(inner).unwrap();
869 let outer = DynClosure::typed(vec![], body).unwrap();
870 let complete = outer
871 .complete_value_type(|id| (id == TypeId::of::<()>()).then(Complete::leaf::<()>))
872 .unwrap();
873 let ValueKind::Callable { result, .. } = complete.kind() else {
874 panic!("expected callable");
875 };
876 let ValueKind::Callable { parameters, result } = result.kind() else {
877 panic!("expected nested callable");
878 };
879 assert!(parameters[0].same_shape(&Complete::leaf::<()>()));
880 assert!(!parameters[0].same_shape(&Nil::value_type()));
881 assert!(result.same_shape(&Nil::value_type()));
882 assert!(!result.same_shape(&Complete::leaf::<()>()));
883 }
884
885 /// Rejects unavailable signatures and missing or incorrectly keyed leaf factories.
886 #[test]
887 fn complete_factory_rejects_unknown_callable_metadata() {
888 use crate::value_type::ValueType as Complete;
889 assert!(
890 adder_closure()
891 .complete_value_type(|_| Some(Complete::leaf::<i32>()))
892 .is_err()
893 );
894 let mut body = DynSegment::new::<()>();
895 body.just(1i32);
896 let closure = DynClosure::typed(vec![], body).unwrap();
897 assert!(closure.complete_value_type(|_| None).is_err());
898 assert!(
899 closure
900 .complete_value_type(|_| Some(Complete::leaf::<u32>()))
901 .is_err()
902 );
903 assert!(
904 closure
905 .complete_value_type(|_| Some(Complete::array(Complete::leaf::<i32>())))
906 .is_err()
907 );
908
909 let mut body = DynSegment::new::<()>();
910 body.just(adder_closure());
911 let closure = DynClosure::typed(vec![], body).unwrap();
912 assert!(
913 closure
914 .complete_value_type(|_| Some(Complete::leaf::<DynClosure>()))
915 .is_err()
916 );
917 }
918
919 fn adder_closure() -> DynClosure {
920 let mut body = DynSegment::new::<()>();
921 body.push_arg::<i32>(0);
922 body.push_arg::<i32>(1);
923 body.op2(|a: i32, b: i32| a + b).unwrap();
924 DynClosure::new(
925 vec![TypeId::of::<i32>(), TypeId::of::<i32>()],
926 TypeId::of::<i32>(),
927 body,
928 )
929 }
930
931 #[test]
932 fn call_invokes_body_with_positional_args() {
933 let closure = adder_closure();
934 let (a, b) = (2i32, 3i32);
935 let result: i32 = closure.call(&[&a, &b]).unwrap();
936 assert_eq!(result, 5);
937 }
938
939 #[test]
940 fn call_is_repeatable_with_different_args() {
941 let closure = adder_closure();
942 let (a1, b1) = (2i32, 3i32);
943 assert_eq!(closure.call::<i32>(&[&a1, &b1]).unwrap(), 5);
944 let (a2, b2) = (10i32, 20i32);
945 assert_eq!(closure.call::<i32>(&[&a2, &b2]).unwrap(), 30);
946 }
947
948 #[test]
949 fn clone_shares_the_same_body_and_both_remain_callable() {
950 let closure = adder_closure();
951 let cloned = closure.clone();
952 let (a, b) = (1i32, 1i32);
953 assert_eq!(closure.call::<i32>(&[&a, &b]).unwrap(), 2);
954 assert_eq!(cloned.call::<i32>(&[&a, &b]).unwrap(), 2);
955 }
956
957 #[test]
958 fn call_boxed_dispatches_through_a_supplied_call_dyn_fn() {
959 let closure = adder_closure();
960 fn call_dyn_fn(seg: &mut DynSegment, inputs: &[&dyn Any]) -> anyhow::Result<Box<dyn Any>> {
961 let v: i32 = seg.call_dyn(inputs)?;
962 Ok(Box::new(v))
963 }
964 let (a, b) = (4i32, 5i32);
965 let result = closure.call_boxed(&[&a, &b], call_dyn_fn).unwrap();
966 assert_eq!(*result.downcast_ref::<i32>().unwrap(), 9);
967 }
968
969 #[test]
970 fn param_types_and_return_type_are_queryable() {
971 let closure = adder_closure();
972 assert_eq!(
973 closure.param_types(),
974 &[TypeId::of::<i32>(), TypeId::of::<i32>()]
975 );
976 assert_eq!(closure.return_type(), TypeId::of::<i32>());
977 }
978
979 /// A typed body cannot reinterpret an argument through a different Rust representation.
980 #[test]
981 fn typed_rejects_incompatible_body_argument_reads() {
982 let mut body = DynSegment::new::<()>();
983 body.push_arg::<String>(0);
984 assert!(DynClosure::typed(vec![ValueType::leaf::<i32>()], body).is_err());
985 }
986
987 /// An undeclared positional argument cannot enter a typed raw call frame.
988 #[test]
989 fn typed_rejects_undeclared_body_argument_reads() {
990 let mut body = DynSegment::new::<()>();
991 body.push_arg::<i32>(1);
992 assert!(DynClosure::typed(vec![ValueType::leaf::<i32>()], body).is_err());
993 }
994
995 /// A shifted fragment cannot become a body evaluated on an empty stack.
996 #[test]
997 fn typed_rejects_ambient_dependent_body_without_execution() {
998 let mut inner = DynSegment::new::<()>();
999 inner.just(42i32);
1000 let closure = DynClosure::typed(vec![], inner).unwrap();
1001 let mut parent = DynSegment::new::<()>();
1002 parent.just(7u64);
1003 parent.just(true);
1004 let mut fragment = parent.new_fragment();
1005 fragment.push_closure(closure).unwrap();
1006 fragment.invoke_closure(0).unwrap();
1007
1008 assert!(DynClosure::typed(vec![], fragment).is_err());
1009 }
1010
1011 /// A zero-origin fragment contains only its local operands, even above ambient unit values.
1012 #[test]
1013 fn typed_accepts_zero_origin_body_with_nested_invocation() {
1014 let mut inner = DynSegment::new::<()>();
1015 inner.just(42i32);
1016 let closure = DynClosure::typed(vec![], inner).unwrap();
1017 let mut parent = DynSegment::new::<()>();
1018 parent.just(());
1019 parent.just(true);
1020 let mut fragment = parent.new_fragment();
1021 fragment.push_closure(closure).unwrap();
1022 fragment.invoke_closure(0).unwrap();
1023
1024 let closure = DynClosure::typed(vec![], fragment).unwrap();
1025 assert_eq!(closure.call::<i32>(&[]).unwrap(), 42);
1026 let mut caller = DynSegment::new::<()>();
1027 caller.push_closure(closure).unwrap();
1028 caller.invoke_closure(0).unwrap();
1029 assert_eq!(caller.call0::<i32>().unwrap(), 42);
1030 }
1031
1032 /// A root body cannot hide a shifted fragment behind a conditional join.
1033 #[test]
1034 fn typed_body_join_rejects_mismatched_fragment_origins_without_execution() {
1035 for shifted_first in [true, false] {
1036 let mut parent = DynSegment::new::<()>();
1037 parent.just(7u64);
1038 parent.just(true);
1039 let mut shifted = parent.new_fragment();
1040 shifted.just(42i32);
1041 let mut body = DynSegment::new::<()>();
1042 body.just(true);
1043 let mut alternate = body.new_fragment();
1044 alternate.just(0i32);
1045
1046 let result = if shifted_first {
1047 body.join2(shifted, alternate)
1048 } else {
1049 body.join2(alternate, shifted)
1050 };
1051 assert!(result.is_err());
1052 }
1053 }
1054
1055 /// A runtime closure value cannot masquerade as another known callable signature.
1056 #[test]
1057 fn invocation_checks_runtime_signature_before_body_execution() {
1058 let mut expected_body = DynSegment::new::<()>();
1059 expected_body.just(1i32);
1060 let expected = DynClosure::typed(vec![], expected_body).unwrap();
1061 let executed = Rc::new(std::cell::Cell::new(false));
1062 let observed = executed.clone();
1063 let mut actual_body = DynSegment::new::<()>();
1064 actual_body.op0(move || {
1065 observed.set(true);
1066 true
1067 });
1068 let actual = DynClosure::typed(vec![], actual_body).unwrap();
1069 // Both signatures have no parameters, so the borrowed argument frame is valid.
1070 let error = unsafe { actual.call_raw(&expected.signature().unwrap(), &[]) }.unwrap_err();
1071 assert!(error.to_string().contains("signature mismatch"));
1072 assert!(!executed.get());
1073 }
1074
1075 /// Argument reads inside alternate fragments participate in signature validation.
1076 #[test]
1077 fn typed_checks_conditional_argument_reads() {
1078 let mut body = DynSegment::new::<()>();
1079 body.just(true);
1080 let mut then_branch = body.new_fragment();
1081 then_branch.push_arg::<String>(0);
1082 let mut else_branch = body.new_fragment();
1083 else_branch.just(String::new());
1084 body.join2(then_branch, else_branch).unwrap();
1085 assert!(DynClosure::typed(vec![ValueType::leaf::<i32>()], body).is_err());
1086 }
1087
1088 /// Rejected invocation leaves the evaluated operand descriptors and operations unchanged.
1089 #[test]
1090 fn wrong_argument_type_is_rejected_before_emitting() {
1091 let mut body = DynSegment::new::<()>();
1092 body.push_arg::<i32>(0);
1093 let closure = DynClosure::typed(vec![ValueType::leaf::<i32>()], body).unwrap();
1094 let mut caller = DynSegment::new::<()>();
1095 caller.push_closure(closure).unwrap();
1096 caller.just(false);
1097 assert!(caller.invoke_closure(1).is_err());
1098 assert_eq!(caller.peek_stack_infos(2).len(), 2);
1099 caller.op1(|_: bool| 8i32).unwrap();
1100 caller.invoke_closure(1).unwrap();
1101 assert_eq!(caller.call0::<i32>().unwrap(), 8);
1102 }
1103
1104 /// Preloaded Rust argument layouts cannot enter the positional CEL invocation adapter.
1105 #[test]
1106 fn preloaded_arguments_fail_explicitly() {
1107 let mut body = DynSegment::new::<()>();
1108 body.just(1i32);
1109 let mut caller = DynSegment::new::<(i32,)>();
1110 caller
1111 .push_closure(DynClosure::typed(vec![], body).unwrap())
1112 .unwrap();
1113 assert!(caller.invoke_closure(0).is_err());
1114 }
1115}