Skip to main content

tenferro_runtime/runtime/
capability.rs

1use std::any::Any;
2use std::fmt;
3use std::hash::Hash;
4use std::sync::Arc;
5
6use crate::program::SemanticOperationView;
7use crate::ExtensionCacheStore;
8use tenferro_tensor::{BackendSession, Tensor, TensorRead};
9
10use super::{
11    ExecutionContextIdentity, ExecutionContextMismatch, HardwareClassId, InputSignature,
12    PrepareError, PrepareOptionsKey, RegistrationIdentity, ResolvedPlanningConfig,
13    ResolvedProgramPlacement, RuntimeEpoch, RuntimeId, SpecializationProjection,
14    SpecializationRequirements,
15};
16
17/// Core operation family handled by a direct runtime capability slot.
18///
19/// # Examples
20///
21/// ```
22/// use tenferro_runtime::CoreCapabilityKind;
23///
24/// assert_eq!(CoreCapabilityKind::Elementwise, CoreCapabilityKind::Elementwise);
25/// ```
26#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
27pub enum CoreCapabilityKind {
28    /// Elementwise core operations.
29    Elementwise,
30    /// Reduction core operations.
31    Reduction,
32    /// Indexing and slicing core operations.
33    Indexing,
34    /// Dot-general preparation.
35    DotGeneral,
36    /// Layout-only operations.
37    Layout,
38}
39
40/// Deterministic reason why a provider cannot prepare an operation.
41///
42/// # Examples
43///
44/// ```
45/// use tenferro_runtime::{CoreCapabilityKind, UnsupportedReason};
46///
47/// let reason = UnsupportedReason::MissingCapability {
48///     capability: CoreCapabilityKind::Elementwise,
49/// };
50/// assert!(reason.to_string().contains("Elementwise"));
51/// ```
52#[derive(Clone, Debug, Eq, Hash, PartialEq)]
53#[non_exhaustive]
54pub enum UnsupportedReason {
55    /// The runtime snapshot has no provider for this core capability.
56    MissingCapability {
57        /// Missing core capability kind.
58        capability: CoreCapabilityKind,
59    },
60    /// The named operation is unsupported by the selected provider.
61    Operation {
62        /// Stable operation diagnostic.
63        operation: &'static str,
64    },
65    /// The selected provider does not support the required storage class.
66    StorageClass {
67        /// Unsupported storage class.
68        storage_class: super::StorageClass,
69    },
70}
71
72impl fmt::Display for UnsupportedReason {
73    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
74        match self {
75            Self::MissingCapability { capability } => {
76                write!(formatter, "missing {capability:?} capability")
77            }
78            Self::Operation { operation } => {
79                write!(formatter, "unsupported operation {operation:?}")
80            }
81            Self::StorageClass { storage_class } => {
82                write!(
83                    formatter,
84                    "unsupported storage class {:?}",
85                    storage_class.as_str()
86                )
87            }
88        }
89    }
90}
91
92/// Bounded diagnostic summary of a preparation cache key.
93///
94/// A1 exposes only the summary shape. C1 owns the concrete key construction.
95///
96/// # Examples
97///
98/// ```
99/// use std::fmt::Debug;
100/// use tenferro_runtime::PreparationKeySummary;
101///
102/// fn requires_debug<T: Debug>() {}
103/// requires_debug::<PreparationKeySummary>();
104/// ```
105#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
106pub struct PreparationKeySummary {
107    compact_digest: u128,
108}
109
110impl PreparationKeySummary {
111    #[allow(dead_code)]
112    pub(crate) const fn new(compact_digest: u128) -> Self {
113        Self { compact_digest }
114    }
115
116    #[cfg(test)]
117    #[allow(dead_code)]
118    pub(crate) const fn for_test(value: u64) -> Self {
119        Self {
120            compact_digest: value as u128,
121        }
122    }
123}
124
125/// Runtime-created immutable binding for one prepared operation.
126///
127/// A1 has no public constructor. B0/C1 provide the runtime creation path.
128///
129/// # Examples
130///
131/// ```
132/// use std::fmt::Debug;
133/// use tenferro_runtime::PreparedOperationBinding;
134///
135/// fn requires_debug<T: Debug>() {}
136/// requires_debug::<PreparedOperationBinding>();
137/// ```
138#[derive(Clone, Debug, Eq, Hash, PartialEq)]
139pub struct PreparedOperationBinding {
140    runtime_id: RuntimeId,
141    epoch: RuntimeEpoch,
142    engine_id: super::EngineId,
143    registration_identity: RegistrationIdentity,
144    context_identity: ExecutionContextIdentity,
145    hardware_class: HardwareClassId,
146}
147
148impl PreparedOperationBinding {
149    #[allow(dead_code)]
150    pub(crate) fn new(
151        runtime_id: RuntimeId,
152        epoch: RuntimeEpoch,
153        engine_id: super::EngineId,
154        registration_identity: RegistrationIdentity,
155        context_identity: ExecutionContextIdentity,
156        hardware_class: HardwareClassId,
157    ) -> Self {
158        Self {
159            runtime_id,
160            epoch,
161            engine_id,
162            registration_identity,
163            context_identity,
164            hardware_class,
165        }
166    }
167
168    /// Return the runtime identity that created this binding.
169    ///
170    /// # Examples
171    ///
172    /// ```
173    /// use std::fmt::Debug;
174    /// use tenferro_runtime::PreparedOperationBinding;
175    ///
176    /// fn requires_debug<T: Debug>() {}
177    /// requires_debug::<PreparedOperationBinding>();
178    /// ```
179    pub fn runtime_id(&self) -> RuntimeId {
180        self.runtime_id
181    }
182
183    /// Return the immutable runtime epoch that created this binding.
184    ///
185    /// # Examples
186    ///
187    /// ```
188    /// use std::fmt::Debug;
189    /// use tenferro_runtime::PreparedOperationBinding;
190    ///
191    /// fn requires_debug<T: Debug>() {}
192    /// requires_debug::<PreparedOperationBinding>();
193    /// ```
194    pub fn epoch(&self) -> RuntimeEpoch {
195        self.epoch
196    }
197
198    /// Return the selected engine identifier.
199    ///
200    /// # Examples
201    ///
202    /// ```
203    /// use std::fmt::Debug;
204    /// use tenferro_runtime::PreparedOperationBinding;
205    ///
206    /// fn requires_debug<T: Debug>() {}
207    /// requires_debug::<PreparedOperationBinding>();
208    /// ```
209    pub fn engine_id(&self) -> &super::EngineId {
210        &self.engine_id
211    }
212
213    /// Return the runtime-local engine registration identity.
214    ///
215    /// # Examples
216    ///
217    /// ```
218    /// use std::fmt::Debug;
219    /// use tenferro_runtime::PreparedOperationBinding;
220    ///
221    /// fn requires_debug<T: Debug>() {}
222    /// requires_debug::<PreparedOperationBinding>();
223    /// ```
224    pub fn registration_identity(&self) -> RegistrationIdentity {
225        self.registration_identity
226    }
227
228    /// Return the execution-context type identity.
229    ///
230    /// # Examples
231    ///
232    /// ```
233    /// use std::fmt::Debug;
234    /// use tenferro_runtime::PreparedOperationBinding;
235    ///
236    /// fn requires_debug<T: Debug>() {}
237    /// requires_debug::<PreparedOperationBinding>();
238    /// ```
239    pub fn context_identity(&self) -> ExecutionContextIdentity {
240        self.context_identity
241    }
242
243    /// Return the selected hardware-class identifier.
244    ///
245    /// # Examples
246    ///
247    /// ```
248    /// use std::fmt::Debug;
249    /// use tenferro_runtime::PreparedOperationBinding;
250    ///
251    /// fn requires_debug<T: Debug>() {}
252    /// requires_debug::<PreparedOperationBinding>();
253    /// ```
254    pub fn hardware_class(&self) -> &HardwareClassId {
255        &self.hardware_class
256    }
257}
258
259/// Immutable metadata for a prepared provider operation.
260///
261/// # Examples
262///
263/// ```
264/// use tenferro_runtime::PreparedOperation;
265///
266/// fn accepts_prepared_operation(_: Option<&dyn PreparedOperation>) {}
267/// let _ = accepts_prepared_operation;
268/// ```
269pub trait PreparedOperation: fmt::Debug + Send + Sync + 'static {
270    /// Return the runtime-created binding carried by this operation.
271    fn binding(&self) -> &PreparedOperationBinding;
272    /// Return the specialization projection this operation depends on.
273    fn specialization(&self) -> &SpecializationProjection;
274    /// Return logical heap bytes owned exclusively by this operation.
275    fn retained_bytes(&self) -> usize;
276}
277
278#[doc(hidden)]
279/// Internal execution bridge for a prepared provider operation.
280///
281/// This remains a public trait only so out-of-crate extension providers can
282/// hand an executable plan back to the runtime. Public prepared-operation
283/// metadata stays on [`PreparedOperation`]; scheduled runtime execution owns
284/// when and how this bridge is invoked.
285pub trait PreparedOperationExecutor: fmt::Debug + Send + Sync + 'static {
286    /// Execute this prepared operation using a runtime-owned erased backend
287    /// context and extension cache store.
288    ///
289    /// # Errors
290    ///
291    /// Returns [`Error`] when the prepared operation is not executable for the
292    /// supplied context, input reads are invalid, or backend execution fails.
293    fn execute(
294        &self,
295        context: &mut ErasedExecutionContext<'_>,
296        extension_caches: &mut ExtensionCacheStore,
297        inputs: &[TensorRead<'_>],
298    ) -> crate::Result<Vec<Tensor>>;
299
300    /// Return whether this prepared operation can execute inside a borrowed
301    /// backend session.
302    ///
303    /// The scheduler uses this capability to form a session-compatible
304    /// admission region. A false result keeps the operation on the ordinary
305    /// backend-owned execution path; it is never silently retried from inside
306    /// a session.
307    fn supports_session(&self) -> bool {
308        false
309    }
310
311    /// Execute this prepared operation inside the scheduler-owned session.
312    ///
313    /// Implementations must use `session` directly and must not reacquire a
314    /// backend session. The borrowed session and any operation-local state
315    /// must not escape this call.
316    ///
317    /// # Errors
318    ///
319    /// The default implementation returns `Error::Unsupported` because the
320    /// prepared operation has no session-aware execution implementation.
321    /// Implementations may return the same typed error when the selected
322    /// backend session does not provide the required capability, or any other
323    /// typed execution error from the operation itself.
324    fn execute_in_session(
325        &self,
326        _session: &mut dyn BackendSession,
327        _extension_caches: &mut ExtensionCacheStore,
328        _inputs: &[TensorRead<'_>],
329    ) -> crate::Result<Vec<Tensor>> {
330        Err(crate::Error::unsupported(
331            "prepared_operation.execute_in_session",
332            crate::ErrorPhase::Execution,
333            "prepared operation does not support the scheduler-owned backend session",
334        ))
335    }
336}
337
338/// Shared prepared-operation handle.
339///
340/// # Examples
341///
342/// ```
343/// use std::fmt::Debug;
344/// use tenferro_runtime::PreparedOperationHandle;
345///
346/// fn requires_debug<T: Debug>() {}
347/// requires_debug::<PreparedOperationHandle>();
348/// ```
349pub type PreparedOperationHandle = Arc<dyn PreparedOperation>;
350
351#[doc(hidden)]
352/// Shared prepared-operation executor handle.
353///
354/// This alias is part of the runtime/provider bridge, not the user-facing
355/// prepared-operation metadata surface.
356pub type PreparedOperationExecutorHandle = Arc<dyn PreparedOperationExecutor>;
357
358/// Runtime-owned prepared operation plan with separated metadata and optional
359/// execution bridge.
360///
361/// Core operations are metadata-only because scheduled execution dispatches
362/// them through the selected engine's core executor. Extension operations carry
363/// an executor bridge that is invoked only by the scheduled runtime loop.
364///
365/// # Examples
366///
367/// ```
368/// use std::fmt::Debug;
369/// use tenferro_runtime::PreparedOperationPlan;
370///
371/// fn requires_debug<T: Debug>() {}
372/// requires_debug::<PreparedOperationPlan>();
373/// ```
374#[derive(Clone)]
375pub struct PreparedOperationPlan {
376    operation: PreparedOperationHandle,
377    executor: Option<PreparedOperationExecutorHandle>,
378}
379
380impl fmt::Debug for PreparedOperationPlan {
381    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
382        formatter
383            .debug_struct("PreparedOperationPlan")
384            .field("operation", &self.operation)
385            .field("has_executor", &self.executor.is_some())
386            .finish()
387    }
388}
389
390impl PreparedOperationPlan {
391    /// Construct a metadata-only prepared operation plan.
392    ///
393    /// # Examples
394    ///
395    /// ```
396    /// use tenferro_runtime::{PreparedOperationHandle, PreparedOperationPlan};
397    ///
398    /// fn provider_handle() -> PreparedOperationHandle {
399    ///     unreachable!("example is type-checked but not executed")
400    /// }
401    ///
402    /// if false {
403    ///     let plan = PreparedOperationPlan::metadata(provider_handle());
404    ///     assert!(plan.executor().is_none());
405    /// }
406    /// ```
407    #[must_use]
408    pub fn metadata(operation: PreparedOperationHandle) -> Self {
409        Self {
410            operation,
411            executor: None,
412        }
413    }
414
415    /// Construct an executable prepared operation plan.
416    ///
417    /// # Examples
418    ///
419    /// ```
420    /// use tenferro_runtime::{
421    ///     PreparedOperationExecutorHandle, PreparedOperationHandle, PreparedOperationPlan,
422    /// };
423    ///
424    /// fn provider_handle() -> PreparedOperationHandle {
425    ///     unreachable!("example is type-checked but not executed")
426    /// }
427    ///
428    /// fn executor_handle() -> PreparedOperationExecutorHandle {
429    ///     unreachable!("example is type-checked but not executed")
430    /// }
431    ///
432    /// if false {
433    ///     let plan = PreparedOperationPlan::executable(provider_handle(), executor_handle());
434    ///     assert!(plan.executor().is_some());
435    /// }
436    /// ```
437    #[must_use]
438    pub fn executable(
439        operation: PreparedOperationHandle,
440        executor: PreparedOperationExecutorHandle,
441    ) -> Self {
442        Self {
443            operation,
444            executor: Some(executor),
445        }
446    }
447
448    /// Return the prepared-operation metadata handle.
449    ///
450    /// # Examples
451    ///
452    /// ```
453    /// use tenferro_runtime::{PreparedOperationHandle, PreparedOperationPlan};
454    ///
455    /// fn provider_handle() -> PreparedOperationHandle {
456    ///     unreachable!("example is type-checked but not executed")
457    /// }
458    ///
459    /// if false {
460    ///     let plan = PreparedOperationPlan::metadata(provider_handle());
461    ///     let _operation = plan.operation();
462    /// }
463    /// ```
464    #[must_use]
465    pub fn operation(&self) -> &PreparedOperationHandle {
466        &self.operation
467    }
468
469    /// Return the optional prepared-operation executor bridge.
470    #[doc(hidden)]
471    #[must_use]
472    pub fn executor(&self) -> Option<&PreparedOperationExecutorHandle> {
473        self.executor.as_ref()
474    }
475
476    /// Return the runtime-created binding carried by the metadata operation.
477    ///
478    /// # Examples
479    ///
480    /// ```
481    /// use tenferro_runtime::{PreparedOperationHandle, PreparedOperationPlan};
482    ///
483    /// fn provider_handle() -> PreparedOperationHandle {
484    ///     unreachable!("example is type-checked but not executed")
485    /// }
486    ///
487    /// if false {
488    ///     let plan = PreparedOperationPlan::metadata(provider_handle());
489    ///     let _binding = plan.binding();
490    /// }
491    /// ```
492    #[must_use]
493    pub fn binding(&self) -> &PreparedOperationBinding {
494        self.operation.binding()
495    }
496
497    /// Return the specialization projection carried by the metadata operation.
498    ///
499    /// # Examples
500    ///
501    /// ```
502    /// use tenferro_runtime::{PreparedOperationHandle, PreparedOperationPlan};
503    ///
504    /// fn provider_handle() -> PreparedOperationHandle {
505    ///     unreachable!("example is type-checked but not executed")
506    /// }
507    ///
508    /// if false {
509    ///     let plan = PreparedOperationPlan::metadata(provider_handle());
510    ///     let _specialization = plan.specialization();
511    /// }
512    /// ```
513    #[must_use]
514    pub fn specialization(&self) -> &SpecializationProjection {
515        self.operation.specialization()
516    }
517
518    /// Return logical heap bytes owned by metadata and executor handles.
519    ///
520    /// # Examples
521    ///
522    /// ```
523    /// use tenferro_runtime::{PreparedOperationHandle, PreparedOperationPlan};
524    ///
525    /// fn provider_handle() -> PreparedOperationHandle {
526    ///     unreachable!("example is type-checked but not executed")
527    /// }
528    ///
529    /// if false {
530    ///     let plan = PreparedOperationPlan::metadata(provider_handle());
531    ///     let _bytes = plan.retained_bytes();
532    /// }
533    /// ```
534    #[must_use]
535    pub fn retained_bytes(&self) -> usize {
536        self.operation.retained_bytes()
537    }
538}
539
540/// Result of asking a provider to prepare an operation.
541///
542/// # Examples
543///
544/// ```
545/// use tenferro_runtime::{CoreCapabilityKind, PrepareCapability, UnsupportedReason};
546///
547/// let capability = PrepareCapability::Unsupported(UnsupportedReason::MissingCapability {
548///     capability: CoreCapabilityKind::Elementwise,
549/// });
550/// assert!(matches!(capability, PrepareCapability::Unsupported { .. }));
551/// ```
552#[derive(Clone, Debug)]
553pub enum PrepareCapability {
554    /// Provider returned an immutable prepared operation.
555    Prepared(PreparedOperationPlan),
556    /// Provider requires a wider specialization before it can prepare.
557    NeedsSpecialization(SpecializationRequirements),
558    /// Provider does not support this request.
559    Unsupported(UnsupportedReason),
560}
561
562/// Type-erased execution context with a checked identity.
563///
564/// # Examples
565///
566/// ```
567/// use tenferro_runtime::{ErasedExecutionContext, ExecutionContextIdentity};
568///
569/// let mut value = 1_u64;
570/// let erased = ErasedExecutionContext::new(&mut value);
571/// assert_eq!(erased.identity(), ExecutionContextIdentity::of::<u64>());
572/// ```
573pub struct ErasedExecutionContext<'a> {
574    identity: ExecutionContextIdentity,
575    value: &'a mut dyn Any,
576}
577
578impl<'a> ErasedExecutionContext<'a> {
579    /// Erase a concrete execution context while retaining its type identity.
580    ///
581    /// # Examples
582    ///
583    /// ```
584    /// use tenferro_runtime::ErasedExecutionContext;
585    ///
586    /// let mut value = 1_u64;
587    /// let erased = ErasedExecutionContext::new(&mut value);
588    /// assert_eq!(erased.identity().type_name(), std::any::type_name::<u64>());
589    /// ```
590    pub fn new<T: 'static>(value: &'a mut T) -> Self {
591        Self {
592            identity: ExecutionContextIdentity::of::<T>(),
593            value,
594        }
595    }
596
597    /// Return the stored execution-context type identity.
598    ///
599    /// # Examples
600    ///
601    /// ```
602    /// use tenferro_runtime::{ErasedExecutionContext, ExecutionContextIdentity};
603    ///
604    /// let mut value = 1_u64;
605    /// let erased = ErasedExecutionContext::new(&mut value);
606    /// assert_eq!(erased.identity(), ExecutionContextIdentity::of::<u64>());
607    /// ```
608    pub fn identity(&self) -> ExecutionContextIdentity {
609        self.identity
610    }
611
612    /// Downcast to the requested execution-context type after checking identity.
613    ///
614    /// # Examples
615    ///
616    /// ```
617    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
618    /// use tenferro_runtime::{ErasedExecutionContext, ExecutionContextIdentity};
619    ///
620    /// let mut value = 1_u64;
621    /// let mut erased = ErasedExecutionContext::new(&mut value);
622    /// *erased.downcast_mut::<u64>(ExecutionContextIdentity::of::<u64>())? = 2;
623    /// # Ok(())
624    /// # }
625    /// ```
626    ///
627    /// # Errors
628    ///
629    /// Returns [`ExecutionContextMismatch`] when the stored identity, supplied
630    /// expected identity, and requested type do not all agree.
631    pub fn downcast_mut<T: 'static>(
632        &mut self,
633        expected: ExecutionContextIdentity,
634    ) -> Result<&mut T, ExecutionContextMismatch> {
635        let requested = ExecutionContextIdentity::of::<T>();
636        if requested != expected {
637            return Err(ExecutionContextMismatch {
638                expected: requested,
639                actual: self.identity,
640            });
641        }
642        if self.identity != expected {
643            return Err(ExecutionContextMismatch {
644                expected,
645                actual: self.identity,
646            });
647        }
648        self.value
649            .downcast_mut::<T>()
650            .ok_or(ExecutionContextMismatch {
651                expected,
652                actual: self.identity,
653            })
654    }
655}
656
657impl fmt::Debug for ErasedExecutionContext<'_> {
658    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
659        formatter
660            .debug_struct("ErasedExecutionContext")
661            .field("identity", &self.identity)
662            .finish_non_exhaustive()
663    }
664}
665
666/// Runtime-created borrowed context passed to direct core providers.
667///
668/// # Examples
669///
670/// ```
671/// use std::fmt::Debug;
672/// use tenferro_runtime::CorePrepareContext;
673///
674/// fn requires_debug<T: Debug>() {}
675/// requires_debug::<CorePrepareContext<'_>>();
676/// ```
677pub struct CorePrepareContext<'a> {
678    binding: &'a PreparedOperationBinding,
679    inputs: &'a InputSignature,
680    resolved_placement: &'a ResolvedProgramPlacement,
681    planning: &'a ResolvedPlanningConfig,
682    prepare_options_key: &'a PrepareOptionsKey,
683    specialization: &'a SpecializationProjection,
684}
685
686impl<'a> CorePrepareContext<'a> {
687    #[allow(dead_code)]
688    pub(crate) fn new(
689        binding: &'a PreparedOperationBinding,
690        inputs: &'a InputSignature,
691        resolved_placement: &'a ResolvedProgramPlacement,
692        planning: &'a ResolvedPlanningConfig,
693        prepare_options_key: &'a PrepareOptionsKey,
694        specialization: &'a SpecializationProjection,
695    ) -> Self {
696        Self {
697            binding,
698            inputs,
699            resolved_placement,
700            planning,
701            prepare_options_key,
702            specialization,
703        }
704    }
705
706    /// Return the runtime-created binding.
707    pub fn binding(&self) -> &'a PreparedOperationBinding {
708        self.binding
709    }
710
711    /// Return value-free input metadata.
712    pub fn inputs(&self) -> &'a InputSignature {
713        self.inputs
714    }
715
716    /// Return the selected program placement.
717    pub fn resolved_placement(&self) -> &'a ResolvedProgramPlacement {
718        self.resolved_placement
719    }
720
721    /// Return resolved planning policy.
722    pub fn planning(&self) -> &'a ResolvedPlanningConfig {
723        self.planning
724    }
725
726    /// Return the normalized prepare-options key.
727    pub fn prepare_options_key(&self) -> &'a PrepareOptionsKey {
728        self.prepare_options_key
729    }
730
731    /// Return the concrete specialization projection.
732    pub fn specialization(&self) -> &'a SpecializationProjection {
733        self.specialization
734    }
735}
736
737impl fmt::Debug for CorePrepareContext<'_> {
738    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
739        formatter
740            .debug_struct("CorePrepareContext")
741            .field("binding", self.binding)
742            .field("inputs", &self.inputs.entries().len())
743            .field("resolved_placement", self.resolved_placement)
744            .field("planning", self.planning)
745            .field("prepare_options_key", self.prepare_options_key)
746            .field("specialization", self.specialization)
747            .finish()
748    }
749}
750
751macro_rules! request_type {
752    ($name:ident) => {
753        /// Runtime-created direct core preparation request.
754        pub struct $name<'a> {
755            operation: SemanticOperationView<'a>,
756            context: &'a CorePrepareContext<'a>,
757        }
758
759        impl<'a> $name<'a> {
760            #[allow(dead_code)]
761            pub(crate) fn new(
762                operation: SemanticOperationView<'a>,
763                context: &'a CorePrepareContext<'a>,
764            ) -> Self {
765                Self { operation, context }
766            }
767
768            /// Return the immutable semantic operation view.
769            pub fn operation(&self) -> SemanticOperationView<'a> {
770                self.operation
771            }
772
773            /// Return the shared runtime preparation context.
774            pub fn context(&self) -> &'a CorePrepareContext<'a> {
775                self.context
776            }
777        }
778
779        impl fmt::Debug for $name<'_> {
780            fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
781                formatter
782                    .debug_struct(stringify!($name))
783                    .field("operation", &self.operation)
784                    .field("context", &self.context)
785                    .finish()
786            }
787        }
788    };
789}
790
791request_type!(ElementwisePrepareRequest);
792request_type!(ReductionPrepareRequest);
793request_type!(IndexingPrepareRequest);
794request_type!(DotGeneralPrepareRequest);
795request_type!(LayoutPrepareRequest);
796
797/// Direct runtime provider for elementwise core operations.
798pub trait ElementwiseRuntime: fmt::Debug + Send + Sync + 'static {
799    /// Prepare one elementwise operation.
800    ///
801    /// # Errors
802    ///
803    /// Returns [`PrepareError`] when provider preparation fails.
804    fn prepare(
805        &self,
806        request: ElementwisePrepareRequest<'_>,
807    ) -> Result<PrepareCapability, PrepareError>;
808}
809
810/// Direct runtime provider for reduction core operations.
811pub trait ReductionRuntime: fmt::Debug + Send + Sync + 'static {
812    /// Prepare one reduction operation.
813    ///
814    /// # Errors
815    ///
816    /// Returns [`PrepareError`] when provider preparation fails.
817    fn prepare(
818        &self,
819        request: ReductionPrepareRequest<'_>,
820    ) -> Result<PrepareCapability, PrepareError>;
821}
822
823/// Direct runtime provider for indexing core operations.
824pub trait IndexingRuntime: fmt::Debug + Send + Sync + 'static {
825    /// Prepare one indexing operation.
826    ///
827    /// # Errors
828    ///
829    /// Returns [`PrepareError`] when provider preparation fails.
830    fn prepare(
831        &self,
832        request: IndexingPrepareRequest<'_>,
833    ) -> Result<PrepareCapability, PrepareError>;
834}
835
836/// Preparation-only direct runtime provider for dot-general operations.
837pub trait DotGeneralPreparation: fmt::Debug + Send + Sync + 'static {
838    /// Prepare one dot-general operation.
839    ///
840    /// # Errors
841    ///
842    /// Returns [`PrepareError`] when provider preparation fails.
843    fn prepare(
844        &self,
845        request: DotGeneralPrepareRequest<'_>,
846    ) -> Result<PrepareCapability, PrepareError>;
847}
848
849/// Direct runtime provider for layout core operations.
850pub trait LayoutRuntime: fmt::Debug + Send + Sync + 'static {
851    /// Prepare one layout operation.
852    ///
853    /// # Errors
854    ///
855    /// Returns [`PrepareError`] when provider preparation fails.
856    fn prepare(&self, request: LayoutPrepareRequest<'_>)
857        -> Result<PrepareCapability, PrepareError>;
858}
859
860#[derive(Clone, Copy, Debug)]
861enum ReservedSubgraphSlot {}
862
863/// Direct core capability slots captured by a runtime snapshot.
864///
865/// # Examples
866///
867/// ```
868/// use tenferro_runtime::CoreCapabilityBundle;
869///
870/// assert!(CoreCapabilityBundle::builder().build().elementwise().is_none());
871/// ```
872#[derive(Clone, Default)]
873pub struct CoreCapabilityBundle {
874    elementwise: Option<Arc<dyn ElementwiseRuntime>>,
875    reduction: Option<Arc<dyn ReductionRuntime>>,
876    indexing: Option<Arc<dyn IndexingRuntime>>,
877    dot_general: Option<Arc<dyn DotGeneralPreparation>>,
878    layout: Option<Arc<dyn LayoutRuntime>>,
879    reserved_subgraph: Option<ReservedSubgraphSlot>,
880}
881
882impl CoreCapabilityBundle {
883    /// Return an empty direct-capability builder.
884    pub fn builder() -> CoreCapabilityBundleBuilder {
885        CoreCapabilityBundleBuilder::new()
886    }
887
888    /// Return the elementwise provider slot.
889    pub fn elementwise(&self) -> Option<&Arc<dyn ElementwiseRuntime>> {
890        self.elementwise.as_ref()
891    }
892
893    /// Return the reduction provider slot.
894    pub fn reduction(&self) -> Option<&Arc<dyn ReductionRuntime>> {
895        self.reduction.as_ref()
896    }
897
898    /// Return the indexing provider slot.
899    pub fn indexing(&self) -> Option<&Arc<dyn IndexingRuntime>> {
900        self.indexing.as_ref()
901    }
902
903    /// Return the dot-general preparation slot.
904    pub fn dot_general(&self) -> Option<&Arc<dyn DotGeneralPreparation>> {
905        self.dot_general.as_ref()
906    }
907
908    /// Return the layout provider slot.
909    pub fn layout(&self) -> Option<&Arc<dyn LayoutRuntime>> {
910        self.layout.as_ref()
911    }
912}
913
914impl fmt::Debug for CoreCapabilityBundle {
915    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
916        formatter
917            .debug_struct("CoreCapabilityBundle")
918            .field("elementwise", &self.elementwise.is_some())
919            .field("reduction", &self.reduction.is_some())
920            .field("indexing", &self.indexing.is_some())
921            .field("dot_general", &self.dot_general.is_some())
922            .field("layout", &self.layout.is_some())
923            .field("reserved_subgraph", &self.reserved_subgraph.is_some())
924            .finish()
925    }
926}
927
928/// Builder for direct core capability slots.
929///
930/// Setters replace any previous value for the same slot and therefore are
931/// infallible.
932///
933/// # Examples
934///
935/// ```
936/// use tenferro_runtime::CoreCapabilityBundleBuilder;
937///
938/// assert!(CoreCapabilityBundleBuilder::new().build().layout().is_none());
939/// ```
940#[derive(Clone, Default)]
941pub struct CoreCapabilityBundleBuilder {
942    elementwise: Option<Arc<dyn ElementwiseRuntime>>,
943    reduction: Option<Arc<dyn ReductionRuntime>>,
944    indexing: Option<Arc<dyn IndexingRuntime>>,
945    dot_general: Option<Arc<dyn DotGeneralPreparation>>,
946    layout: Option<Arc<dyn LayoutRuntime>>,
947}
948
949impl CoreCapabilityBundleBuilder {
950    /// Return an empty builder.
951    pub fn new() -> Self {
952        Self::default()
953    }
954
955    /// Set or replace the elementwise slot.
956    pub fn elementwise(&mut self, capability: Arc<dyn ElementwiseRuntime>) -> &mut Self {
957        self.elementwise = Some(capability);
958        self
959    }
960
961    /// Set or replace the reduction slot.
962    pub fn reduction(&mut self, capability: Arc<dyn ReductionRuntime>) -> &mut Self {
963        self.reduction = Some(capability);
964        self
965    }
966
967    /// Set or replace the indexing slot.
968    pub fn indexing(&mut self, capability: Arc<dyn IndexingRuntime>) -> &mut Self {
969        self.indexing = Some(capability);
970        self
971    }
972
973    /// Set or replace the dot-general preparation slot.
974    pub fn dot_general(&mut self, capability: Arc<dyn DotGeneralPreparation>) -> &mut Self {
975        self.dot_general = Some(capability);
976        self
977    }
978
979    /// Set or replace the layout slot.
980    pub fn layout(&mut self, capability: Arc<dyn LayoutRuntime>) -> &mut Self {
981        self.layout = Some(capability);
982        self
983    }
984
985    /// Build an immutable capability bundle.
986    pub fn build(self) -> CoreCapabilityBundle {
987        CoreCapabilityBundle {
988            elementwise: self.elementwise,
989            reduction: self.reduction,
990            indexing: self.indexing,
991            dot_general: self.dot_general,
992            layout: self.layout,
993            reserved_subgraph: None,
994        }
995    }
996}
997
998impl fmt::Debug for CoreCapabilityBundleBuilder {
999    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1000        formatter
1001            .debug_struct("CoreCapabilityBundleBuilder")
1002            .field("elementwise", &self.elementwise.is_some())
1003            .field("reduction", &self.reduction.is_some())
1004            .field("indexing", &self.indexing.is_some())
1005            .field("dot_general", &self.dot_general.is_some())
1006            .field("layout", &self.layout.is_some())
1007            .finish()
1008    }
1009}