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, enc::Encoder, error::EncodeError};
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 ScheduleKey, TurboTasksApi, execute_read_target_inline, read_local_output,
23 with_turbo_tasks,
24 },
25 read_options::ReadOutcome,
26 registry::get_value_type,
27 turbo_tasks,
28};
29
30#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Encode, Decode)]
40pub struct CellId(NonZeroU32);
41
42const CELL_INDEX_BITS: u32 = 22;
44const CELL_INDEX_MASK: u32 = (1 << CELL_INDEX_BITS) - 1;
46
47impl CellId {
48 pub const MAX_VALUE_TYPE_ID: u16 = (1 << (u32::BITS - CELL_INDEX_BITS)) as u16 - 1;
50 pub const MAX_CELL_INDEX: u32 = CELL_INDEX_MASK;
52
53 pub fn new(type_id: ValueTypeId, index: u32) -> Self {
55 let type_id = *type_id;
56 debug_assert!(
57 type_id <= Self::MAX_VALUE_TYPE_ID,
58 "ValueTypeId {} exceeds the {} cap packed into CellId",
59 type_id,
60 Self::MAX_VALUE_TYPE_ID,
61 );
62 debug_assert!(
63 index <= Self::MAX_CELL_INDEX,
64 "cell index {} exceeds the {} cap packed into CellId",
65 index,
66 Self::MAX_CELL_INDEX,
67 );
68 let packed = ((type_id as u32) << CELL_INDEX_BITS) | (index & CELL_INDEX_MASK);
69 CellId(unsafe { NonZeroU32::new_unchecked(packed) })
71 }
72
73 pub fn type_id(self) -> ValueTypeId {
74 let type_id = (self.0.get() >> CELL_INDEX_BITS) as u16;
75 unsafe { ValueTypeId::new_unchecked(type_id) }
77 }
78
79 pub fn index(self) -> u32 {
80 self.0.get() & CELL_INDEX_MASK
81 }
82
83 pub(crate) fn raw(self) -> u32 {
85 self.0.get()
86 }
87
88 pub(crate) unsafe fn from_raw(raw: u32) -> Self {
95 debug_assert!(raw != 0);
96 CellId(unsafe { NonZeroU32::new_unchecked(raw) })
98 }
99}
100
101impl Debug for CellId {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 f.debug_struct("CellId")
104 .field("type_id", &self.type_id())
105 .field("index", &self.index())
106 .finish()
107 }
108}
109
110impl Display for CellId {
111 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112 write!(
113 f,
114 "{}#{}",
115 get_value_type(self.type_id()).ty.name,
116 self.index()
117 )
118 }
119}
120
121#[derive(Clone, Copy, PartialEq, Eq, Hash, Deserialize, Decode)]
147pub struct RawVc(NonZeroU64);
148
149impl Serialize for RawVc {
152 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
153 debug_assert!(
154 !self.is_transient(),
155 "transient RawVc must not be serialized"
156 );
157 serializer.serialize_newtype_struct("RawVc", &self.0)
158 }
159}
160
161impl Encode for RawVc {
162 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
163 debug_assert!(!self.is_transient(), "transient RawVc must not be encoded");
164 self.0.encode(encoder)
165 }
166}
167
168#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
170pub enum RawVcUnpacked {
171 TaskOutput(TaskId),
174 TaskCell(TaskId, CellId),
179 LocalOutput(ExecutionId, LocalTaskId, TaskPersistence),
189}
190
191const RAW_VC_LOCAL_FLAG: u64 = 1 << 31;
195
196const RAW_VC_TASK_MASK: u64 = TASK_ID_MAX as u64;
201const RAW_VC_CELL_SHIFT: u64 = 32;
204
205const RAW_VC_LOCAL_TRANSIENT_SHIFT: u64 = 0;
207const RAW_VC_LOCAL_EXECUTION_SHIFT: u64 = 1;
208const RAW_VC_LOCAL_TASK_SHIFT: u64 = 32;
209
210impl RawVc {
211 pub fn task_output(task: TaskId) -> Self {
215 let task = *task as u64;
216 debug_assert!(task <= RAW_VC_TASK_MASK, "TaskId exceeds 31 bits");
217 Self::from_bits(task)
218 }
219
220 pub fn task_cell(task: TaskId, cell: CellId) -> Self {
223 let task = *task as u64;
224 debug_assert!(task <= RAW_VC_TASK_MASK, "TaskId exceeds 31 bits");
225 let cell = cell.raw() as u64;
226 Self::from_bits(task | (cell << RAW_VC_CELL_SHIFT))
227 }
228
229 pub fn local_output(
231 execution_id: ExecutionId,
232 local_task_id: LocalTaskId,
233 persistence: TaskPersistence,
234 ) -> Self {
235 let transient = (persistence == TaskPersistence::Transient) as u64;
236 let execution_id = *execution_id as u64;
237 let local_task_id = *local_task_id as u64;
238 Self::from_bits(
239 RAW_VC_LOCAL_FLAG
240 | (transient << RAW_VC_LOCAL_TRANSIENT_SHIFT)
241 | (execution_id << RAW_VC_LOCAL_EXECUTION_SHIFT)
242 | (local_task_id << RAW_VC_LOCAL_TASK_SHIFT),
243 )
244 }
245
246 #[inline]
247 fn from_bits(bits: u64) -> Self {
248 RawVc(unsafe { NonZeroU64::new_unchecked(bits) })
252 }
253
254 #[inline]
255 pub(crate) fn bits(self) -> u64 {
256 self.0.get()
257 }
258
259 #[inline]
262 fn cell_word(self) -> u32 {
263 (self.bits() >> RAW_VC_CELL_SHIFT) as u32
264 }
265
266 #[inline]
269 fn is_task_cell(self) -> bool {
270 !self.is_local_output() && self.cell_word() != 0
271 }
272
273 #[inline]
276 fn is_task_output(self) -> bool {
277 !self.is_local_output() && self.cell_word() == 0
278 }
279
280 #[inline]
284 fn read_task_id(self) -> TaskId {
285 let id = (self.bits() & RAW_VC_TASK_MASK) as u32;
286 unsafe { TaskId::new_unchecked(id) }
288 }
289
290 #[inline]
294 fn read_cell(self) -> CellId {
295 unsafe { CellId::from_raw(self.cell_word()) }
297 }
298
299 pub fn unpack(self) -> RawVcUnpacked {
301 if self.is_local_output() {
302 let (execution_id, local_task_id, persistence) = self.decode_local_output();
303 RawVcUnpacked::LocalOutput(execution_id, local_task_id, persistence)
304 } else {
305 let task_id = self.read_task_id();
306 let cell_word = self.cell_word();
307 if cell_word != 0 {
308 RawVcUnpacked::TaskCell(task_id, unsafe { CellId::from_raw(self.cell_word()) })
309 } else {
310 RawVcUnpacked::TaskOutput(task_id)
311 }
312 }
313 }
314
315 pub fn as_task_output(self) -> Option<TaskId> {
320 self.is_task_output().then(|| self.read_task_id())
321 }
322
323 pub fn as_task_cell(self) -> Option<(TaskId, CellId)> {
330 self.is_task_cell()
331 .then(|| (self.read_task_id(), self.read_cell()))
332 }
333
334 pub fn as_local_output(self) -> Option<(ExecutionId, LocalTaskId, TaskPersistence)> {
340 self.is_local_output().then(|| self.decode_local_output())
341 }
342
343 fn decode_local_output(self) -> (ExecutionId, LocalTaskId, TaskPersistence) {
346 let bits = self.bits();
347 let persistence = if (bits >> RAW_VC_LOCAL_TRANSIENT_SHIFT) & 1 == 1 {
348 TaskPersistence::Transient
349 } else {
350 TaskPersistence::Persistent
351 };
352 let execution_id = ((bits >> RAW_VC_LOCAL_EXECUTION_SHIFT) & 0xFFFF) as u16;
353 let local_task_id = ((bits >> RAW_VC_LOCAL_TASK_SHIFT) & 0xFFFF_FFFF) as u32;
354 (
356 unsafe { ExecutionId::new_unchecked(execution_id) },
357 unsafe { LocalTaskId::new_unchecked(local_task_id) },
358 persistence,
359 )
360 }
361}
362
363impl Debug for RawVc {
364 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
365 match self.unpack() {
366 RawVcUnpacked::TaskOutput(task_id) => {
367 f.debug_tuple("RawVc::TaskOutput").field(&*task_id).finish()
368 }
369 RawVcUnpacked::TaskCell(task_id, cell_id) => f
370 .debug_tuple("RawVc::TaskCell")
371 .field(&*task_id)
372 .field(&cell_id.to_string())
373 .finish(),
374 RawVcUnpacked::LocalOutput(execution_id, local_task_id, task_persistence) => f
375 .debug_tuple("RawVc::LocalOutput")
376 .field(&*execution_id)
377 .field(&*local_task_id)
378 .field(&task_persistence)
379 .finish(),
380 }
381 }
382}
383
384impl Display for RawVc {
385 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
386 match self.unpack() {
387 RawVcUnpacked::TaskOutput(task_id) => write!(f, "output of task {}", *task_id),
388 RawVcUnpacked::TaskCell(task_id, cell_id) => {
389 write!(f, "{} of task {}", cell_id, *task_id)
390 }
391 RawVcUnpacked::LocalOutput(execution_id, local_task_id, task_persistence) => write!(
392 f,
393 "output of local task {} ({}, {})",
394 *local_task_id, *execution_id, task_persistence
395 ),
396 }
397 }
398}
399
400impl RawVc {
401 pub fn is_resolved(&self) -> bool {
402 self.is_task_cell()
403 }
404
405 pub fn is_local_output(&self) -> bool {
406 self.bits() & RAW_VC_LOCAL_FLAG != 0
407 }
408
409 pub fn is_transient(&self) -> bool {
414 if self.is_local_output() {
415 (self.bits() >> RAW_VC_LOCAL_TRANSIENT_SHIFT) & 1 == 1
417 } else {
418 self.read_task_id().is_transient()
420 }
421 }
422
423 pub(crate) fn into_read(self) -> ReadRawVcFuture {
424 ReadRawVcFuture::new(self)
427 }
428
429 pub(crate) fn resolve(self) -> ResolveRawVcFuture {
431 ResolveRawVcFuture::new(self)
432 }
433
434 pub async fn to_non_local(self) -> Result<RawVc> {
437 let Some((execution_id, local_task_id, ..)) = self.as_local_output() else {
438 return Ok(self);
439 };
440 let tt = turbo_tasks();
441 let local_output = read_local_output(&*tt, execution_id, local_task_id).await?;
442 debug_assert!(
443 !local_output.is_local_output(),
444 "a LocalOutput cannot point at other LocalOutputs"
445 );
446 Ok(local_output)
447 }
448
449 pub(crate) fn to_non_local_unchecked_sync(self, tt: &dyn TurboTasksApi) -> Result<RawVc> {
455 let Some((execution_id, local_task_id, ..)) = self.as_local_output() else {
456 return Ok(self);
457 };
458 let local_output = match tt.try_read_local_output(execution_id, local_task_id)? {
459 Ok(raw_vc) => raw_vc,
460 Err(_event_listener) => unreachable!("local output is not ready yet"),
461 };
462 debug_assert!(
463 !local_output.is_local_output(),
464 "a LocalOutput cannot point at other LocalOutputs"
465 );
466 Ok(local_output)
467 }
468
469 pub fn try_get_task_id(&self) -> Option<TaskId> {
470 (!self.is_local_output()).then(|| self.read_task_id())
471 }
472
473 pub fn try_get_type_id(&self) -> Option<ValueTypeId> {
474 self.is_task_cell().then(|| self.read_cell().type_id())
475 }
476
477 pub(crate) fn resolved_has_trait(&self, trait_id: TraitTypeId) -> bool {
480 debug_assert!(
481 self.is_task_cell(),
482 "resolved_has_trait must be called with a RawVc::TaskCell"
483 );
484 get_value_type(self.read_cell().type_id()).has_trait(&trait_id)
485 }
486
487 pub(crate) fn resolved_is_type(&self, type_id: ValueTypeId) -> bool {
490 debug_assert!(
491 self.is_task_cell(),
492 "resolved_is_type must be called with a RawVc::TaskCell"
493 );
494 self.read_cell().type_id() == type_id
495 }
496}
497
498fn poll_listener(
501 listener: &mut Option<EventListener>,
502 cx: &mut std::task::Context<'_>,
503) -> Poll<()> {
504 if let Some(l) = listener {
505 ready!(Pin::new(l).poll(cx));
506 *listener = None;
507 }
508 Poll::Ready(())
509}
510
511fn suppress_top_level_task_check<R>(strongly_consistent: bool, f: impl FnOnce() -> R) -> R {
518 if cfg!(debug_assertions) && strongly_consistent {
519 SUPPRESS_EVENTUAL_CONSISTENCY_TOP_LEVEL_TASK_CHECK.sync_scope(true, f)
521 } else {
522 f()
523 }
524}
525
526fn execute_inline(key: ScheduleKey) {
532 execute_read_target_inline(&*turbo_tasks(), key);
533}
534
535#[must_use]
536pub struct ResolveRawVcFuture {
537 current: RawVc,
538 read_output_options: ReadOutputOptions,
539 strongly_consistent: bool,
542 listener: Option<EventListener>,
543}
544
545impl ResolveRawVcFuture {
546 fn new(vc: RawVc) -> Self {
547 ResolveRawVcFuture {
548 current: vc,
549 read_output_options: ReadOutputOptions::default(),
550 strongly_consistent: false,
551 listener: None,
552 }
553 }
554
555 pub fn strongly_consistent(mut self) -> Self {
556 self.strongly_consistent = true;
557 self.read_output_options.consistency = ReadConsistency::Strong;
558 self
559 }
560
561 pub(crate) fn track_with_key(mut self) -> Self {
564 self.read_output_options.tracking = ReadTracking::Tracked;
565 self
566 }
567
568 pub(crate) fn untracked(mut self) -> Self {
571 self.read_output_options.tracking = ReadTracking::TrackOnlyError;
572 self
573 }
574}
575
576impl Future for ResolveRawVcFuture {
577 type Output = Result<RawVc>;
578
579 #[inline(never)]
580 fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
581 let this = unsafe { self.get_unchecked_mut() };
583
584 let strongly_consistent = this.strongly_consistent;
585 let mut poll_fn = |tt: &Arc<dyn TurboTasksApi>,
589 execute_inline: &mut Option<ScheduleKey>|
590 -> Poll<Self::Output> {
591 'outer: loop {
592 ready!(poll_listener(&mut this.listener, cx));
593 let (listener, key) = match this.current.unpack() {
594 RawVcUnpacked::TaskOutput(task) => {
595 let read_result = tt.try_read_task_output(task, this.read_output_options);
596 match read_result {
597 Ok(ReadOutcome::Value(vc)) => {
598 this.read_output_options.consistency = ReadConsistency::Eventual;
607 this.current = vc;
608 continue 'outer;
609 }
610 Ok(ReadOutcome::Scheduled(listener)) => {
612 (listener, Some(ScheduleKey::Task(task)))
613 }
614 Ok(ReadOutcome::InProgress(listener)) => {
615 #[cfg(feature = "inline_execution_stats")]
619 tt.note_waited_for_in_progress_task();
620 this.listener = Some(listener);
621 continue 'outer;
622 }
623 Err(err) => return Poll::Ready(Err(err)),
624 }
625 }
626 RawVcUnpacked::TaskCell(_, _) => return Poll::Ready(Ok(this.current)),
627 RawVcUnpacked::LocalOutput(execution_id, local_task_id, ..) => {
628 debug_assert_eq!(
629 this.read_output_options.consistency,
630 ReadConsistency::Eventual
631 );
632 let read_result = tt.try_read_local_output(execution_id, local_task_id);
633 match read_result {
634 Ok(Ok(vc)) => {
635 this.current = vc;
636 continue 'outer;
637 }
638 Ok(Err(listener)) => (
639 listener,
640 Some(ScheduleKey::current_local_task(execution_id, local_task_id)),
641 ),
642 Err(err) => return Poll::Ready(Err(err)),
643 }
644 }
645 };
646 this.listener = Some(listener);
649 *execute_inline = key;
650 return Poll::Pending;
651 }
652 };
653
654 loop {
655 let mut execute_inline_key = None;
656 let result = suppress_top_level_task_check(strongly_consistent, || {
663 with_turbo_tasks(|tt| poll_fn(tt, &mut execute_inline_key))
664 });
665 if let Some(key) = execute_inline_key {
666 execute_inline(key);
668 continue;
669 }
670 return result;
671 }
672 }
673}
674
675impl Unpin for ResolveRawVcFuture {}
676
677#[must_use]
678pub struct ReadRawVcFuture {
679 read_cell_options: ReadCellOptions,
680 state: ReadRawVcState,
681}
682
683enum ReadRawVcState {
686 Resolving(ResolveRawVcFuture),
688 Reading {
690 task: TaskId,
691 index: CellId,
692 strongly_consistent: bool,
697 listener: Option<EventListener>,
698 },
699}
700
701impl ReadRawVcFuture {
702 pub(crate) fn new(vc: RawVc) -> Self {
703 ReadRawVcFuture {
704 read_cell_options: ReadCellOptions::default(),
705 state: ReadRawVcState::Resolving(ResolveRawVcFuture::new(vc)),
706 }
707 }
708
709 fn map_resolve(mut self, f: impl FnOnce(ResolveRawVcFuture) -> ResolveRawVcFuture) -> Self {
710 match self.state {
711 ReadRawVcState::Resolving(resolve) => {
712 self.state = ReadRawVcState::Resolving(f(resolve));
713 }
714 ReadRawVcState::Reading { .. } => {
715 unreachable!("builder methods are only called before polling");
716 }
717 }
718 self
719 }
720
721 pub fn strongly_consistent(self) -> Self {
723 self.map_resolve(|r| r.strongly_consistent())
724 }
725
726 pub fn track_with_key(mut self, key: u64) -> Self {
728 self.read_cell_options.tracking = ReadCellTracking::Tracked { key: Some(key) };
729 self.map_resolve(|r| r.track_with_key())
730 }
731
732 pub fn untracked(mut self) -> Self {
738 self.read_cell_options.tracking = ReadCellTracking::TrackOnlyError;
739 self.map_resolve(|r| r.untracked())
740 }
741
742 pub fn final_read_hint(mut self) -> Self {
744 self.read_cell_options.final_read_hint = true;
745 self
746 }
747}
748
749impl Future for ReadRawVcFuture {
750 type Output = Result<TypedCellContent>;
751
752 #[inline(never)]
753 fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
754 let this = unsafe { self.get_unchecked_mut() };
756
757 if let ReadRawVcState::Resolving(resolve) = &mut this.state {
762 let strongly_consistent = resolve.strongly_consistent;
763 match ready!(Pin::new(resolve).poll(cx)) {
764 Err(err) => return Poll::Ready(Err(err)),
765 Ok(resolved) => {
766 let Some((task, index)) = resolved.as_task_cell() else {
767 unreachable!("ResolveRawVcFuture always resolves to a TaskCell")
768 };
769 this.state = ReadRawVcState::Reading {
770 task,
771 index,
772 strongly_consistent,
773 listener: None,
774 };
775 }
776 }
777 }
778
779 let ReadRawVcState::Reading {
781 task,
782 index,
783 strongly_consistent,
784 listener,
785 } = &mut this.state
786 else {
787 unreachable!("phase 1 transitioned to Reading above");
788 };
789 let task = *task;
790 let index = *index;
791 let read_cell_options = this.read_cell_options;
792
793 let strongly_consistent = *strongly_consistent;
794
795 let mut poll_fn = |tt: &Arc<dyn TurboTasksApi>,
796 execute_inline: &mut Option<ScheduleKey>|
797 -> Poll<Self::Output> {
798 loop {
799 ready!(poll_listener(listener, cx));
800 let (new_listener, key) =
801 match tt.try_read_task_cell(task, index, read_cell_options) {
802 Ok(ReadOutcome::Value(content)) => return Poll::Ready(Ok(content)),
803 Ok(ReadOutcome::Scheduled(l)) => (l, Some(ScheduleKey::Task(task))),
804 Ok(ReadOutcome::InProgress(l)) => {
805 #[cfg(feature = "inline_execution_stats")]
809 tt.note_waited_for_in_progress_task();
810 *listener = Some(l);
811 continue;
812 }
813 Err(err) => return Poll::Ready(Err(err)),
814 };
815 *listener = Some(new_listener);
819 *execute_inline = key;
820 return Poll::Pending;
821 }
822 };
823
824 loop {
825 let mut execute_inline_key = None;
826 let result = suppress_top_level_task_check(strongly_consistent, || {
831 with_turbo_tasks(|tt| poll_fn(tt, &mut execute_inline_key))
832 });
833 if let Some(key) = execute_inline_key {
834 execute_inline(key);
836 continue;
837 }
838 return result;
839 }
840 }
841}
842
843impl Unpin for ReadRawVcFuture {}
844
845#[cfg(test)]
846mod tests {
847 use super::*;
848
849 #[test]
852 fn cell_id_is_four_bytes() {
853 assert_eq!(size_of::<CellId>(), 4);
854 assert_eq!(size_of::<Option<CellId>>(), 4);
855 }
856
857 #[test]
860 fn cell_id_pack_unpack_round_trip() {
861 let type_ids = [1u16, 2, 100, CellId::MAX_VALUE_TYPE_ID];
862 let indices = [0u32, 1, 12345, CellId::MAX_CELL_INDEX];
863 for &raw_ty in &type_ids {
864 let type_id = unsafe { ValueTypeId::new_unchecked(raw_ty) };
866 for &index in &indices {
867 let cell = CellId::new(type_id, index);
868 assert_eq!(cell.type_id(), type_id, "type_id round-trip for {raw_ty}");
869 assert_eq!(cell.index(), index, "index round-trip for {index}");
870 }
871 }
872 }
873
874 #[test]
877 fn cell_id_packing_is_bijective() {
878 let a = CellId::new(unsafe { ValueTypeId::new_unchecked(1) }, 0);
880 let b = CellId::new(unsafe { ValueTypeId::new_unchecked(1) }, 1);
881 let c = CellId::new(unsafe { ValueTypeId::new_unchecked(2) }, 0);
882 assert_ne!(a, b);
883 assert_ne!(a, c);
884 assert_ne!(b, c);
885 }
886
887 #[test]
889 fn raw_vc_is_eight_bytes() {
890 assert_eq!(size_of::<RawVc>(), 8);
891 assert_eq!(size_of::<Option<RawVc>>(), 8);
892 }
893
894 #[test]
899 fn raw_vc_pack_unpack_round_trip() {
900 let tasks = [
902 1u32,
903 2,
904 crate::TRANSIENT_TASK_BIT - 1,
905 crate::TRANSIENT_TASK_BIT,
906 TASK_ID_MAX,
907 ];
908 for &t in &tasks {
909 let task = unsafe { TaskId::new_unchecked(t) };
910
911 let vc = RawVc::task_output(task);
913 assert_eq!(vc.unpack(), RawVcUnpacked::TaskOutput(task));
914 assert!(!vc.is_resolved() && !vc.is_local_output());
915 assert_eq!(vc.is_transient(), task.is_transient());
916 assert_eq!(vc.try_get_task_id(), Some(task));
917 assert_eq!(vc.as_task_output(), Some(task));
919 assert_eq!(vc.as_task_cell(), None);
920 assert_eq!(vc.as_local_output(), None);
921
922 for cell in [
924 CellId::new(unsafe { ValueTypeId::new_unchecked(1) }, 0),
925 CellId::new(
926 unsafe { ValueTypeId::new_unchecked(CellId::MAX_VALUE_TYPE_ID) },
927 CellId::MAX_CELL_INDEX,
928 ),
929 ] {
930 let vc = RawVc::task_cell(task, cell);
931 assert_eq!(vc.unpack(), RawVcUnpacked::TaskCell(task, cell));
932 assert!(vc.is_resolved());
933 assert_eq!(vc.try_get_task_id(), Some(task));
934 assert_eq!(vc.try_get_type_id(), Some(cell.type_id()));
935 assert_eq!(vc.as_task_cell(), Some((task, cell)));
937 assert_eq!(vc.as_task_output(), None);
938 assert_eq!(vc.as_local_output(), None);
939 }
940 }
941
942 for persistence in [TaskPersistence::Persistent, TaskPersistence::Transient] {
944 for (e, l) in [(1u16, 1u32), (u16::MAX, u32::MAX)] {
945 let exec = unsafe { ExecutionId::new_unchecked(e) };
946 let local = unsafe { LocalTaskId::new_unchecked(l) };
947 let vc = RawVc::local_output(exec, local, persistence);
948 assert_eq!(
949 vc.unpack(),
950 RawVcUnpacked::LocalOutput(exec, local, persistence)
951 );
952 assert!(vc.is_local_output());
953 assert_eq!(vc.is_transient(), persistence == TaskPersistence::Transient);
954 assert_eq!(vc.try_get_task_id(), None);
955 assert_eq!(vc.as_local_output(), Some((exec, local, persistence)));
957 assert_eq!(vc.as_task_output(), None);
958 assert_eq!(vc.as_task_cell(), None);
959 }
960 }
961 }
962
963 #[test]
969 fn raw_vc_discriminator_is_unambiguous() {
970 let task = unsafe { TaskId::new_unchecked(123) };
972 let cell = CellId::new(unsafe { ValueTypeId::new_unchecked(1) }, 0);
973
974 let output = RawVc::task_output(task);
975 let task_cell = RawVc::task_cell(task, cell);
976 assert!(output.is_task_output() && !output.is_task_cell() && !output.is_local_output());
977 assert!(
978 task_cell.is_task_cell() && !task_cell.is_task_output() && !task_cell.is_local_output()
979 );
980 assert_ne!(output, task_cell);
982 assert_eq!(output.read_task_id(), task_cell.read_task_id());
983
984 let local = RawVc::local_output(
987 unsafe { ExecutionId::new_unchecked(u16::MAX) },
988 unsafe { LocalTaskId::new_unchecked(u32::MAX) },
989 TaskPersistence::Persistent,
990 );
991 assert!(local.is_local_output() && !local.is_task_cell() && !local.is_task_output());
992 }
993
994 #[test]
995 #[cfg(debug_assertions)]
996 #[should_panic(expected = "TaskId exceeds 31 bits")]
997 #[cfg_attr(
998 target_family = "wasm",
999 ignore = "no unwinding on wasm: std is built panic=abort, so catch_unwind cannot catch"
1000 )]
1001 fn task_output_panics_on_out_of_range_task_id() {
1002 let task = unsafe { TaskId::new_unchecked(TASK_ID_MAX + 1) };
1005 let _ = RawVc::task_output(task);
1006 }
1007
1008 #[test]
1009 #[cfg(debug_assertions)]
1010 #[should_panic(expected = "TaskId exceeds 31 bits")]
1011 #[cfg_attr(
1012 target_family = "wasm",
1013 ignore = "no unwinding on wasm: std is built panic=abort, so catch_unwind cannot catch"
1014 )]
1015 fn task_cell_panics_on_out_of_range_task_id() {
1016 let task = unsafe { TaskId::new_unchecked(TASK_ID_MAX + 1) };
1018 let cell = CellId::new(unsafe { ValueTypeId::new_unchecked(1) }, 0);
1019 let _ = RawVc::task_cell(task, cell);
1020 }
1021
1022 #[test]
1023 #[cfg(debug_assertions)]
1024 #[should_panic(expected = "exceeds")]
1025 #[cfg_attr(
1026 target_family = "wasm",
1027 ignore = "no unwinding on wasm: std is built panic=abort, so catch_unwind cannot catch"
1028 )]
1029 fn cell_id_panics_on_out_of_range_type_id() {
1030 let type_id = unsafe { ValueTypeId::new_unchecked(CellId::MAX_VALUE_TYPE_ID + 1) };
1032 let _ = CellId::new(type_id, 0);
1033 }
1034 #[test]
1035 #[cfg(debug_assertions)]
1036 #[should_panic(expected = "exceeds")]
1037 #[cfg_attr(
1038 target_family = "wasm",
1039 ignore = "no unwinding on wasm: std is built panic=abort, so catch_unwind cannot catch"
1040 )]
1041 fn cell_id_panics_on_out_of_range_index() {
1042 let type_id = unsafe { ValueTypeId::new_unchecked(1) };
1043 let _ = CellId::new(type_id, CellId::MAX_CELL_INDEX + 1);
1044 }
1045}