1use std::{
2 fmt::{Debug, Display},
3 future::Future,
4 num::{NonZeroU32, NonZeroU64},
5 pin::Pin,
6 sync::Arc,
7 task::{Poll, ready},
8};
9
10use anyhow::Result;
11use bincode::{Decode, Encode};
12use serde::{Deserialize, Serialize};
13
14use crate::{
15 ReadCellOptions, ReadConsistency, ReadOutputOptions, TaskId, TaskPersistence, TraitTypeId,
16 ValueTypeId,
17 backend::TypedCellContent,
18 event::EventListener,
19 id::{ExecutionId, LocalTaskId, TASK_ID_MAX},
20 manager::{
21 ReadCellTracking, ReadTracking, SUPPRESS_EVENTUAL_CONSISTENCY_TOP_LEVEL_TASK_CHECK,
22 TurboTasksApi, read_local_output, with_turbo_tasks,
23 },
24 registry::get_value_type,
25 turbo_tasks,
26};
27
28#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Encode, Decode)]
38pub struct CellId(NonZeroU32);
39
40const CELL_INDEX_BITS: u32 = 22;
42const CELL_INDEX_MASK: u32 = (1 << CELL_INDEX_BITS) - 1;
44
45impl CellId {
46 pub const MAX_VALUE_TYPE_ID: u16 = (1 << (u32::BITS - CELL_INDEX_BITS)) as u16 - 1;
48 pub const MAX_CELL_INDEX: u32 = CELL_INDEX_MASK;
50
51 pub fn new(type_id: ValueTypeId, index: u32) -> Self {
53 let type_id = *type_id;
54 debug_assert!(
55 type_id <= Self::MAX_VALUE_TYPE_ID,
56 "ValueTypeId {} exceeds the {} cap packed into CellId",
57 type_id,
58 Self::MAX_VALUE_TYPE_ID,
59 );
60 debug_assert!(
61 index <= Self::MAX_CELL_INDEX,
62 "cell index {} exceeds the {} cap packed into CellId",
63 index,
64 Self::MAX_CELL_INDEX,
65 );
66 let packed = ((type_id as u32) << CELL_INDEX_BITS) | (index & CELL_INDEX_MASK);
67 CellId(unsafe { NonZeroU32::new_unchecked(packed) })
69 }
70
71 pub fn type_id(self) -> ValueTypeId {
72 let type_id = (self.0.get() >> CELL_INDEX_BITS) as u16;
73 unsafe { ValueTypeId::new_unchecked(type_id) }
75 }
76
77 pub fn index(self) -> u32 {
78 self.0.get() & CELL_INDEX_MASK
79 }
80
81 pub(crate) fn raw(self) -> u32 {
83 self.0.get()
84 }
85
86 pub(crate) unsafe fn from_raw(raw: u32) -> Self {
93 debug_assert!(raw != 0);
94 CellId(unsafe { NonZeroU32::new_unchecked(raw) })
96 }
97}
98
99impl Debug for CellId {
100 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101 f.debug_struct("CellId")
102 .field("type_id", &self.type_id())
103 .field("index", &self.index())
104 .finish()
105 }
106}
107
108impl Display for CellId {
109 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110 write!(
111 f,
112 "{}#{}",
113 get_value_type(self.type_id()).ty.name,
114 self.index()
115 )
116 }
117}
118
119#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Encode, Decode)]
145pub struct RawVc(NonZeroU64);
146
147#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
149pub enum RawVcUnpacked {
150 TaskOutput(TaskId),
153 TaskCell(TaskId, CellId),
158 LocalOutput(ExecutionId, LocalTaskId, TaskPersistence),
168}
169
170const RAW_VC_LOCAL_FLAG: u64 = 1 << 31;
174
175const RAW_VC_TASK_MASK: u64 = TASK_ID_MAX as u64;
180const RAW_VC_CELL_SHIFT: u64 = 32;
183
184const RAW_VC_LOCAL_TRANSIENT_SHIFT: u64 = 0;
186const RAW_VC_LOCAL_EXECUTION_SHIFT: u64 = 1;
187const RAW_VC_LOCAL_TASK_SHIFT: u64 = 32;
188
189impl RawVc {
190 pub fn task_output(task: TaskId) -> Self {
194 let task = *task as u64;
195 debug_assert!(task <= RAW_VC_TASK_MASK, "TaskId exceeds 31 bits");
196 Self::from_bits(task)
197 }
198
199 pub fn task_cell(task: TaskId, cell: CellId) -> Self {
202 let task = *task as u64;
203 debug_assert!(task <= RAW_VC_TASK_MASK, "TaskId exceeds 31 bits");
204 let cell = cell.raw() as u64;
205 Self::from_bits(task | (cell << RAW_VC_CELL_SHIFT))
206 }
207
208 pub fn local_output(
210 execution_id: ExecutionId,
211 local_task_id: LocalTaskId,
212 persistence: TaskPersistence,
213 ) -> Self {
214 let transient = (persistence == TaskPersistence::Transient) as u64;
215 let execution_id = *execution_id as u64;
216 let local_task_id = *local_task_id as u64;
217 Self::from_bits(
218 RAW_VC_LOCAL_FLAG
219 | (transient << RAW_VC_LOCAL_TRANSIENT_SHIFT)
220 | (execution_id << RAW_VC_LOCAL_EXECUTION_SHIFT)
221 | (local_task_id << RAW_VC_LOCAL_TASK_SHIFT),
222 )
223 }
224
225 #[inline]
226 fn from_bits(bits: u64) -> Self {
227 RawVc(unsafe { NonZeroU64::new_unchecked(bits) })
231 }
232
233 #[inline]
234 pub(crate) fn bits(self) -> u64 {
235 self.0.get()
236 }
237
238 #[inline]
241 fn cell_word(self) -> u32 {
242 (self.bits() >> RAW_VC_CELL_SHIFT) as u32
243 }
244
245 #[inline]
248 fn is_task_cell(self) -> bool {
249 !self.is_local_output() && self.cell_word() != 0
250 }
251
252 #[inline]
255 fn is_task_output(self) -> bool {
256 !self.is_local_output() && self.cell_word() == 0
257 }
258
259 #[inline]
263 fn read_task_id(self) -> TaskId {
264 let id = (self.bits() & RAW_VC_TASK_MASK) as u32;
265 unsafe { TaskId::new_unchecked(id) }
267 }
268
269 #[inline]
273 fn read_cell(self) -> CellId {
274 unsafe { CellId::from_raw(self.cell_word()) }
276 }
277
278 pub fn unpack(self) -> RawVcUnpacked {
280 if self.is_local_output() {
281 let (execution_id, local_task_id, persistence) = self.decode_local_output();
282 RawVcUnpacked::LocalOutput(execution_id, local_task_id, persistence)
283 } else {
284 let task_id = self.read_task_id();
285 let cell_word = self.cell_word();
286 if cell_word != 0 {
287 RawVcUnpacked::TaskCell(task_id, unsafe { CellId::from_raw(self.cell_word()) })
288 } else {
289 RawVcUnpacked::TaskOutput(task_id)
290 }
291 }
292 }
293
294 pub fn as_task_output(self) -> Option<TaskId> {
299 self.is_task_output().then(|| self.read_task_id())
300 }
301
302 pub fn as_task_cell(self) -> Option<(TaskId, CellId)> {
309 self.is_task_cell()
310 .then(|| (self.read_task_id(), self.read_cell()))
311 }
312
313 pub fn as_local_output(self) -> Option<(ExecutionId, LocalTaskId, TaskPersistence)> {
319 self.is_local_output().then(|| self.decode_local_output())
320 }
321
322 fn decode_local_output(self) -> (ExecutionId, LocalTaskId, TaskPersistence) {
325 let bits = self.bits();
326 let persistence = if (bits >> RAW_VC_LOCAL_TRANSIENT_SHIFT) & 1 == 1 {
327 TaskPersistence::Transient
328 } else {
329 TaskPersistence::Persistent
330 };
331 let execution_id = ((bits >> RAW_VC_LOCAL_EXECUTION_SHIFT) & 0xFFFF) as u16;
332 let local_task_id = ((bits >> RAW_VC_LOCAL_TASK_SHIFT) & 0xFFFF_FFFF) as u32;
333 (
335 unsafe { ExecutionId::new_unchecked(execution_id) },
336 unsafe { LocalTaskId::new_unchecked(local_task_id) },
337 persistence,
338 )
339 }
340}
341
342impl Debug for RawVc {
343 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
344 match self.unpack() {
345 RawVcUnpacked::TaskOutput(task_id) => {
346 f.debug_tuple("RawVc::TaskOutput").field(&*task_id).finish()
347 }
348 RawVcUnpacked::TaskCell(task_id, cell_id) => f
349 .debug_tuple("RawVc::TaskCell")
350 .field(&*task_id)
351 .field(&cell_id.to_string())
352 .finish(),
353 RawVcUnpacked::LocalOutput(execution_id, local_task_id, task_persistence) => f
354 .debug_tuple("RawVc::LocalOutput")
355 .field(&*execution_id)
356 .field(&*local_task_id)
357 .field(&task_persistence)
358 .finish(),
359 }
360 }
361}
362
363impl Display for RawVc {
364 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
365 match self.unpack() {
366 RawVcUnpacked::TaskOutput(task_id) => write!(f, "output of task {}", *task_id),
367 RawVcUnpacked::TaskCell(task_id, cell_id) => {
368 write!(f, "{} of task {}", cell_id, *task_id)
369 }
370 RawVcUnpacked::LocalOutput(execution_id, local_task_id, task_persistence) => write!(
371 f,
372 "output of local task {} ({}, {})",
373 *local_task_id, *execution_id, task_persistence
374 ),
375 }
376 }
377}
378
379impl RawVc {
380 pub fn is_resolved(&self) -> bool {
381 self.is_task_cell()
382 }
383
384 pub fn is_local_output(&self) -> bool {
385 self.bits() & RAW_VC_LOCAL_FLAG != 0
386 }
387
388 pub fn is_transient(&self) -> bool {
393 if self.is_local_output() {
394 (self.bits() >> RAW_VC_LOCAL_TRANSIENT_SHIFT) & 1 == 1
396 } else {
397 self.read_task_id().is_transient()
399 }
400 }
401
402 pub(crate) fn into_read(self) -> ReadRawVcFuture {
403 ReadRawVcFuture::new(self)
406 }
407
408 pub(crate) fn resolve(self) -> ResolveRawVcFuture {
410 ResolveRawVcFuture::new(self)
411 }
412
413 pub async fn to_non_local(self) -> Result<RawVc> {
416 let Some((execution_id, local_task_id, ..)) = self.as_local_output() else {
417 return Ok(self);
418 };
419 let tt = turbo_tasks();
420 let local_output = read_local_output(&*tt, execution_id, local_task_id).await?;
421 debug_assert!(
422 !local_output.is_local_output(),
423 "a LocalOutput cannot point at other LocalOutputs"
424 );
425 Ok(local_output)
426 }
427
428 pub(crate) fn to_non_local_unchecked_sync(self, tt: &dyn TurboTasksApi) -> Result<RawVc> {
434 let Some((execution_id, local_task_id, ..)) = self.as_local_output() else {
435 return Ok(self);
436 };
437 let local_output = match tt.try_read_local_output(execution_id, local_task_id)? {
438 Ok(raw_vc) => raw_vc,
439 Err(_event_listener) => unreachable!("local output is not ready yet"),
440 };
441 debug_assert!(
442 !local_output.is_local_output(),
443 "a LocalOutput cannot point at other LocalOutputs"
444 );
445 Ok(local_output)
446 }
447
448 pub fn try_get_task_id(&self) -> Option<TaskId> {
449 (!self.is_local_output()).then(|| self.read_task_id())
450 }
451
452 pub fn try_get_type_id(&self) -> Option<ValueTypeId> {
453 self.is_task_cell().then(|| self.read_cell().type_id())
454 }
455
456 pub(crate) fn resolved_has_trait(&self, trait_id: TraitTypeId) -> bool {
459 debug_assert!(
460 self.is_task_cell(),
461 "resolved_has_trait must be called with a RawVc::TaskCell"
462 );
463 get_value_type(self.read_cell().type_id()).has_trait(&trait_id)
464 }
465
466 pub(crate) fn resolved_is_type(&self, type_id: ValueTypeId) -> bool {
469 debug_assert!(
470 self.is_task_cell(),
471 "resolved_is_type must be called with a RawVc::TaskCell"
472 );
473 self.read_cell().type_id() == type_id
474 }
475}
476
477fn poll_listener(
480 listener: &mut Option<EventListener>,
481 cx: &mut std::task::Context<'_>,
482) -> Poll<()> {
483 if let Some(l) = listener {
484 ready!(Pin::new(l).poll(cx));
485 *listener = None;
486 }
487 Poll::Ready(())
488}
489
490fn suppress_top_level_task_check<R>(strongly_consistent: bool, f: impl FnOnce() -> R) -> R {
497 if cfg!(debug_assertions) && strongly_consistent {
498 SUPPRESS_EVENTUAL_CONSISTENCY_TOP_LEVEL_TASK_CHECK.sync_scope(true, f)
500 } else {
501 f()
502 }
503}
504
505#[must_use]
506pub struct ResolveRawVcFuture {
507 current: RawVc,
508 read_output_options: ReadOutputOptions,
509 strongly_consistent: bool,
512 listener: Option<EventListener>,
513}
514
515impl ResolveRawVcFuture {
516 fn new(vc: RawVc) -> Self {
517 ResolveRawVcFuture {
518 current: vc,
519 read_output_options: ReadOutputOptions::default(),
520 strongly_consistent: false,
521 listener: None,
522 }
523 }
524
525 pub fn strongly_consistent(mut self) -> Self {
526 self.strongly_consistent = true;
527 self.read_output_options.consistency = ReadConsistency::Strong;
528 self
529 }
530
531 pub(crate) fn track_with_key(mut self) -> Self {
534 self.read_output_options.tracking = ReadTracking::Tracked;
535 self
536 }
537
538 pub(crate) fn untracked(mut self) -> Self {
541 self.read_output_options.tracking = ReadTracking::TrackOnlyError;
542 self
543 }
544}
545
546impl Future for ResolveRawVcFuture {
547 type Output = Result<RawVc>;
548
549 #[inline(never)]
550 fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
551 let this = unsafe { self.get_unchecked_mut() };
553
554 let poll_fn = |tt: &Arc<dyn TurboTasksApi>| -> Poll<Self::Output> {
555 'outer: loop {
556 ready!(poll_listener(&mut this.listener, cx));
557 let listener = match this.current.unpack() {
558 RawVcUnpacked::TaskOutput(task) => {
559 let read_result = tt.try_read_task_output(task, this.read_output_options);
560 match read_result {
561 Ok(Ok(vc)) => {
562 this.read_output_options.consistency = ReadConsistency::Eventual;
571 this.current = vc;
572 continue 'outer;
573 }
574 Ok(Err(listener)) => listener,
575 Err(err) => return Poll::Ready(Err(err)),
576 }
577 }
578 RawVcUnpacked::TaskCell(_, _) => return Poll::Ready(Ok(this.current)),
579 RawVcUnpacked::LocalOutput(execution_id, local_task_id, ..) => {
580 debug_assert_eq!(
581 this.read_output_options.consistency,
582 ReadConsistency::Eventual
583 );
584 let read_result = tt.try_read_local_output(execution_id, local_task_id);
585 match read_result {
586 Ok(Ok(vc)) => {
587 this.current = vc;
588 continue 'outer;
589 }
590 Ok(Err(listener)) => listener,
591 Err(err) => return Poll::Ready(Err(err)),
592 }
593 }
594 };
595 this.listener = Some(listener);
596 }
597 };
598
599 suppress_top_level_task_check(this.strongly_consistent, || with_turbo_tasks(poll_fn))
606 }
607}
608
609impl Unpin for ResolveRawVcFuture {}
610
611#[must_use]
612pub struct ReadRawVcFuture {
613 read_cell_options: ReadCellOptions,
614 state: ReadRawVcState,
615}
616
617enum ReadRawVcState {
620 Resolving(ResolveRawVcFuture),
622 Reading {
624 task: TaskId,
625 index: CellId,
626 strongly_consistent: bool,
631 listener: Option<EventListener>,
632 },
633}
634
635impl ReadRawVcFuture {
636 pub(crate) fn new(vc: RawVc) -> Self {
637 ReadRawVcFuture {
638 read_cell_options: ReadCellOptions::default(),
639 state: ReadRawVcState::Resolving(ResolveRawVcFuture::new(vc)),
640 }
641 }
642
643 fn map_resolve(mut self, f: impl FnOnce(ResolveRawVcFuture) -> ResolveRawVcFuture) -> Self {
644 match self.state {
645 ReadRawVcState::Resolving(resolve) => {
646 self.state = ReadRawVcState::Resolving(f(resolve));
647 }
648 ReadRawVcState::Reading { .. } => {
649 unreachable!("builder methods are only called before polling");
650 }
651 }
652 self
653 }
654
655 pub fn strongly_consistent(self) -> Self {
657 self.map_resolve(|r| r.strongly_consistent())
658 }
659
660 pub fn track_with_key(mut self, key: u64) -> Self {
662 self.read_cell_options.tracking = ReadCellTracking::Tracked { key: Some(key) };
663 self.map_resolve(|r| r.track_with_key())
664 }
665
666 pub fn untracked(mut self) -> Self {
672 self.read_cell_options.tracking = ReadCellTracking::TrackOnlyError;
673 self.map_resolve(|r| r.untracked())
674 }
675
676 pub fn final_read_hint(mut self) -> Self {
678 self.read_cell_options.final_read_hint = true;
679 self
680 }
681}
682
683impl Future for ReadRawVcFuture {
684 type Output = Result<TypedCellContent>;
685
686 #[inline(never)]
687 fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
688 let this = unsafe { self.get_unchecked_mut() };
690
691 if let ReadRawVcState::Resolving(resolve) = &mut this.state {
696 let strongly_consistent = resolve.strongly_consistent;
697 match ready!(Pin::new(resolve).poll(cx)) {
698 Err(err) => return Poll::Ready(Err(err)),
699 Ok(resolved) => {
700 let Some((task, index)) = resolved.as_task_cell() else {
701 unreachable!("ResolveRawVcFuture always resolves to a TaskCell")
702 };
703 this.state = ReadRawVcState::Reading {
704 task,
705 index,
706 strongly_consistent,
707 listener: None,
708 };
709 }
710 }
711 }
712
713 let ReadRawVcState::Reading {
715 task,
716 index,
717 strongly_consistent,
718 listener,
719 } = &mut this.state
720 else {
721 unreachable!("phase 1 transitioned to Reading above");
722 };
723 let task = *task;
724 let index = *index;
725 let read_cell_options = this.read_cell_options;
726
727 let poll_fn = |tt: &Arc<dyn TurboTasksApi>| -> Poll<Self::Output> {
728 loop {
729 ready!(poll_listener(listener, cx));
730 let new_listener = match tt.try_read_task_cell(task, index, read_cell_options) {
731 Ok(Ok(content)) => return Poll::Ready(Ok(content)),
732 Ok(Err(l)) => l,
733 Err(err) => return Poll::Ready(Err(err)),
734 };
735 *listener = Some(new_listener);
736 }
737 };
738
739 suppress_top_level_task_check(*strongly_consistent, || with_turbo_tasks(poll_fn))
744 }
745}
746
747impl Unpin for ReadRawVcFuture {}
748
749#[cfg(test)]
750mod tests {
751 use super::*;
752
753 #[test]
756 fn cell_id_is_four_bytes() {
757 assert_eq!(size_of::<CellId>(), 4);
758 assert_eq!(size_of::<Option<CellId>>(), 4);
759 }
760
761 #[test]
764 fn cell_id_pack_unpack_round_trip() {
765 let type_ids = [1u16, 2, 100, CellId::MAX_VALUE_TYPE_ID];
766 let indices = [0u32, 1, 12345, CellId::MAX_CELL_INDEX];
767 for &raw_ty in &type_ids {
768 let type_id = unsafe { ValueTypeId::new_unchecked(raw_ty) };
770 for &index in &indices {
771 let cell = CellId::new(type_id, index);
772 assert_eq!(cell.type_id(), type_id, "type_id round-trip for {raw_ty}");
773 assert_eq!(cell.index(), index, "index round-trip for {index}");
774 }
775 }
776 }
777
778 #[test]
781 fn cell_id_packing_is_bijective() {
782 let a = CellId::new(unsafe { ValueTypeId::new_unchecked(1) }, 0);
784 let b = CellId::new(unsafe { ValueTypeId::new_unchecked(1) }, 1);
785 let c = CellId::new(unsafe { ValueTypeId::new_unchecked(2) }, 0);
786 assert_ne!(a, b);
787 assert_ne!(a, c);
788 assert_ne!(b, c);
789 }
790
791 #[test]
793 fn raw_vc_is_eight_bytes() {
794 assert_eq!(size_of::<RawVc>(), 8);
795 assert_eq!(size_of::<Option<RawVc>>(), 8);
796 }
797
798 #[test]
803 fn raw_vc_pack_unpack_round_trip() {
804 let tasks = [
806 1u32,
807 2,
808 crate::TRANSIENT_TASK_BIT - 1,
809 crate::TRANSIENT_TASK_BIT,
810 TASK_ID_MAX,
811 ];
812 for &t in &tasks {
813 let task = unsafe { TaskId::new_unchecked(t) };
814
815 let vc = RawVc::task_output(task);
817 assert_eq!(vc.unpack(), RawVcUnpacked::TaskOutput(task));
818 assert!(!vc.is_resolved() && !vc.is_local_output());
819 assert_eq!(vc.is_transient(), task.is_transient());
820 assert_eq!(vc.try_get_task_id(), Some(task));
821 assert_eq!(vc.as_task_output(), Some(task));
823 assert_eq!(vc.as_task_cell(), None);
824 assert_eq!(vc.as_local_output(), None);
825
826 for cell in [
828 CellId::new(unsafe { ValueTypeId::new_unchecked(1) }, 0),
829 CellId::new(
830 unsafe { ValueTypeId::new_unchecked(CellId::MAX_VALUE_TYPE_ID) },
831 CellId::MAX_CELL_INDEX,
832 ),
833 ] {
834 let vc = RawVc::task_cell(task, cell);
835 assert_eq!(vc.unpack(), RawVcUnpacked::TaskCell(task, cell));
836 assert!(vc.is_resolved());
837 assert_eq!(vc.try_get_task_id(), Some(task));
838 assert_eq!(vc.try_get_type_id(), Some(cell.type_id()));
839 assert_eq!(vc.as_task_cell(), Some((task, cell)));
841 assert_eq!(vc.as_task_output(), None);
842 assert_eq!(vc.as_local_output(), None);
843 }
844 }
845
846 for persistence in [TaskPersistence::Persistent, TaskPersistence::Transient] {
848 for (e, l) in [(1u16, 1u32), (u16::MAX, u32::MAX)] {
849 let exec = unsafe { ExecutionId::new_unchecked(e) };
850 let local = unsafe { LocalTaskId::new_unchecked(l) };
851 let vc = RawVc::local_output(exec, local, persistence);
852 assert_eq!(
853 vc.unpack(),
854 RawVcUnpacked::LocalOutput(exec, local, persistence)
855 );
856 assert!(vc.is_local_output());
857 assert_eq!(vc.is_transient(), persistence == TaskPersistence::Transient);
858 assert_eq!(vc.try_get_task_id(), None);
859 assert_eq!(vc.as_local_output(), Some((exec, local, persistence)));
861 assert_eq!(vc.as_task_output(), None);
862 assert_eq!(vc.as_task_cell(), None);
863 }
864 }
865 }
866
867 #[test]
873 fn raw_vc_discriminator_is_unambiguous() {
874 let task = unsafe { TaskId::new_unchecked(123) };
876 let cell = CellId::new(unsafe { ValueTypeId::new_unchecked(1) }, 0);
877
878 let output = RawVc::task_output(task);
879 let task_cell = RawVc::task_cell(task, cell);
880 assert!(output.is_task_output() && !output.is_task_cell() && !output.is_local_output());
881 assert!(
882 task_cell.is_task_cell() && !task_cell.is_task_output() && !task_cell.is_local_output()
883 );
884 assert_ne!(output, task_cell);
886 assert_eq!(output.read_task_id(), task_cell.read_task_id());
887
888 let local = RawVc::local_output(
891 unsafe { ExecutionId::new_unchecked(u16::MAX) },
892 unsafe { LocalTaskId::new_unchecked(u32::MAX) },
893 TaskPersistence::Persistent,
894 );
895 assert!(local.is_local_output() && !local.is_task_cell() && !local.is_task_output());
896 }
897
898 #[test]
899 #[cfg(debug_assertions)]
900 #[should_panic(expected = "TaskId exceeds 31 bits")]
901 fn task_output_panics_on_out_of_range_task_id() {
902 let task = unsafe { TaskId::new_unchecked(TASK_ID_MAX + 1) };
905 let _ = RawVc::task_output(task);
906 }
907
908 #[test]
909 #[cfg(debug_assertions)]
910 #[should_panic(expected = "TaskId exceeds 31 bits")]
911 fn task_cell_panics_on_out_of_range_task_id() {
912 let task = unsafe { TaskId::new_unchecked(TASK_ID_MAX + 1) };
914 let cell = CellId::new(unsafe { ValueTypeId::new_unchecked(1) }, 0);
915 let _ = RawVc::task_cell(task, cell);
916 }
917
918 #[test]
919 #[cfg(debug_assertions)]
920 #[should_panic(expected = "exceeds")]
921 fn cell_id_panics_on_out_of_range_type_id() {
922 let type_id = unsafe { ValueTypeId::new_unchecked(CellId::MAX_VALUE_TYPE_ID + 1) };
924 let _ = CellId::new(type_id, 0);
925 }
926 #[test]
927 #[cfg(debug_assertions)]
928 #[should_panic(expected = "exceeds")]
929 fn cell_id_panics_on_out_of_range_index() {
930 let type_id = unsafe { ValueTypeId::new_unchecked(1) };
931 let _ = CellId::new(type_id, CellId::MAX_CELL_INDEX + 1);
932 }
933}