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 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, Serialize, Deserialize, Encode, Decode)]
147pub struct RawVc(NonZeroU64);
148
149#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
151pub enum RawVcUnpacked {
152 TaskOutput(TaskId),
155 TaskCell(TaskId, CellId),
160 LocalOutput(ExecutionId, LocalTaskId, TaskPersistence),
170}
171
172const RAW_VC_LOCAL_FLAG: u64 = 1 << 31;
176
177const RAW_VC_TASK_MASK: u64 = TASK_ID_MAX as u64;
182const RAW_VC_CELL_SHIFT: u64 = 32;
185
186const RAW_VC_LOCAL_TRANSIENT_SHIFT: u64 = 0;
188const RAW_VC_LOCAL_EXECUTION_SHIFT: u64 = 1;
189const RAW_VC_LOCAL_TASK_SHIFT: u64 = 32;
190
191impl RawVc {
192 pub fn task_output(task: TaskId) -> Self {
196 let task = *task as u64;
197 debug_assert!(task <= RAW_VC_TASK_MASK, "TaskId exceeds 31 bits");
198 Self::from_bits(task)
199 }
200
201 pub fn task_cell(task: TaskId, cell: CellId) -> Self {
204 let task = *task as u64;
205 debug_assert!(task <= RAW_VC_TASK_MASK, "TaskId exceeds 31 bits");
206 let cell = cell.raw() as u64;
207 Self::from_bits(task | (cell << RAW_VC_CELL_SHIFT))
208 }
209
210 pub fn local_output(
212 execution_id: ExecutionId,
213 local_task_id: LocalTaskId,
214 persistence: TaskPersistence,
215 ) -> Self {
216 let transient = (persistence == TaskPersistence::Transient) as u64;
217 let execution_id = *execution_id as u64;
218 let local_task_id = *local_task_id as u64;
219 Self::from_bits(
220 RAW_VC_LOCAL_FLAG
221 | (transient << RAW_VC_LOCAL_TRANSIENT_SHIFT)
222 | (execution_id << RAW_VC_LOCAL_EXECUTION_SHIFT)
223 | (local_task_id << RAW_VC_LOCAL_TASK_SHIFT),
224 )
225 }
226
227 #[inline]
228 fn from_bits(bits: u64) -> Self {
229 RawVc(unsafe { NonZeroU64::new_unchecked(bits) })
233 }
234
235 #[inline]
236 pub(crate) fn bits(self) -> u64 {
237 self.0.get()
238 }
239
240 #[inline]
243 fn cell_word(self) -> u32 {
244 (self.bits() >> RAW_VC_CELL_SHIFT) as u32
245 }
246
247 #[inline]
250 fn is_task_cell(self) -> bool {
251 !self.is_local_output() && self.cell_word() != 0
252 }
253
254 #[inline]
257 fn is_task_output(self) -> bool {
258 !self.is_local_output() && self.cell_word() == 0
259 }
260
261 #[inline]
265 fn read_task_id(self) -> TaskId {
266 let id = (self.bits() & RAW_VC_TASK_MASK) as u32;
267 unsafe { TaskId::new_unchecked(id) }
269 }
270
271 #[inline]
275 fn read_cell(self) -> CellId {
276 unsafe { CellId::from_raw(self.cell_word()) }
278 }
279
280 pub fn unpack(self) -> RawVcUnpacked {
282 if self.is_local_output() {
283 let (execution_id, local_task_id, persistence) = self.decode_local_output();
284 RawVcUnpacked::LocalOutput(execution_id, local_task_id, persistence)
285 } else {
286 let task_id = self.read_task_id();
287 let cell_word = self.cell_word();
288 if cell_word != 0 {
289 RawVcUnpacked::TaskCell(task_id, unsafe { CellId::from_raw(self.cell_word()) })
290 } else {
291 RawVcUnpacked::TaskOutput(task_id)
292 }
293 }
294 }
295
296 pub fn as_task_output(self) -> Option<TaskId> {
301 self.is_task_output().then(|| self.read_task_id())
302 }
303
304 pub fn as_task_cell(self) -> Option<(TaskId, CellId)> {
311 self.is_task_cell()
312 .then(|| (self.read_task_id(), self.read_cell()))
313 }
314
315 pub fn as_local_output(self) -> Option<(ExecutionId, LocalTaskId, TaskPersistence)> {
321 self.is_local_output().then(|| self.decode_local_output())
322 }
323
324 fn decode_local_output(self) -> (ExecutionId, LocalTaskId, TaskPersistence) {
327 let bits = self.bits();
328 let persistence = if (bits >> RAW_VC_LOCAL_TRANSIENT_SHIFT) & 1 == 1 {
329 TaskPersistence::Transient
330 } else {
331 TaskPersistence::Persistent
332 };
333 let execution_id = ((bits >> RAW_VC_LOCAL_EXECUTION_SHIFT) & 0xFFFF) as u16;
334 let local_task_id = ((bits >> RAW_VC_LOCAL_TASK_SHIFT) & 0xFFFF_FFFF) as u32;
335 (
337 unsafe { ExecutionId::new_unchecked(execution_id) },
338 unsafe { LocalTaskId::new_unchecked(local_task_id) },
339 persistence,
340 )
341 }
342}
343
344impl Debug for RawVc {
345 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
346 match self.unpack() {
347 RawVcUnpacked::TaskOutput(task_id) => {
348 f.debug_tuple("RawVc::TaskOutput").field(&*task_id).finish()
349 }
350 RawVcUnpacked::TaskCell(task_id, cell_id) => f
351 .debug_tuple("RawVc::TaskCell")
352 .field(&*task_id)
353 .field(&cell_id.to_string())
354 .finish(),
355 RawVcUnpacked::LocalOutput(execution_id, local_task_id, task_persistence) => f
356 .debug_tuple("RawVc::LocalOutput")
357 .field(&*execution_id)
358 .field(&*local_task_id)
359 .field(&task_persistence)
360 .finish(),
361 }
362 }
363}
364
365impl Display for RawVc {
366 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
367 match self.unpack() {
368 RawVcUnpacked::TaskOutput(task_id) => write!(f, "output of task {}", *task_id),
369 RawVcUnpacked::TaskCell(task_id, cell_id) => {
370 write!(f, "{} of task {}", cell_id, *task_id)
371 }
372 RawVcUnpacked::LocalOutput(execution_id, local_task_id, task_persistence) => write!(
373 f,
374 "output of local task {} ({}, {})",
375 *local_task_id, *execution_id, task_persistence
376 ),
377 }
378 }
379}
380
381impl RawVc {
382 pub fn is_resolved(&self) -> bool {
383 self.is_task_cell()
384 }
385
386 pub fn is_local_output(&self) -> bool {
387 self.bits() & RAW_VC_LOCAL_FLAG != 0
388 }
389
390 pub fn is_transient(&self) -> bool {
395 if self.is_local_output() {
396 (self.bits() >> RAW_VC_LOCAL_TRANSIENT_SHIFT) & 1 == 1
398 } else {
399 self.read_task_id().is_transient()
401 }
402 }
403
404 pub(crate) fn into_read(self) -> ReadRawVcFuture {
405 ReadRawVcFuture::new(self)
408 }
409
410 pub(crate) fn resolve(self) -> ResolveRawVcFuture {
412 ResolveRawVcFuture::new(self)
413 }
414
415 pub async fn to_non_local(self) -> Result<RawVc> {
418 let Some((execution_id, local_task_id, ..)) = self.as_local_output() else {
419 return Ok(self);
420 };
421 let tt = turbo_tasks();
422 let local_output = read_local_output(&*tt, execution_id, local_task_id).await?;
423 debug_assert!(
424 !local_output.is_local_output(),
425 "a LocalOutput cannot point at other LocalOutputs"
426 );
427 Ok(local_output)
428 }
429
430 pub(crate) fn to_non_local_unchecked_sync(self, tt: &dyn TurboTasksApi) -> Result<RawVc> {
436 let Some((execution_id, local_task_id, ..)) = self.as_local_output() else {
437 return Ok(self);
438 };
439 let local_output = match tt.try_read_local_output(execution_id, local_task_id)? {
440 Ok(raw_vc) => raw_vc,
441 Err(_event_listener) => unreachable!("local output is not ready yet"),
442 };
443 debug_assert!(
444 !local_output.is_local_output(),
445 "a LocalOutput cannot point at other LocalOutputs"
446 );
447 Ok(local_output)
448 }
449
450 pub fn try_get_task_id(&self) -> Option<TaskId> {
451 (!self.is_local_output()).then(|| self.read_task_id())
452 }
453
454 pub fn try_get_type_id(&self) -> Option<ValueTypeId> {
455 self.is_task_cell().then(|| self.read_cell().type_id())
456 }
457
458 pub(crate) fn resolved_has_trait(&self, trait_id: TraitTypeId) -> bool {
461 debug_assert!(
462 self.is_task_cell(),
463 "resolved_has_trait must be called with a RawVc::TaskCell"
464 );
465 get_value_type(self.read_cell().type_id()).has_trait(&trait_id)
466 }
467
468 pub(crate) fn resolved_is_type(&self, type_id: ValueTypeId) -> bool {
471 debug_assert!(
472 self.is_task_cell(),
473 "resolved_is_type must be called with a RawVc::TaskCell"
474 );
475 self.read_cell().type_id() == type_id
476 }
477}
478
479fn poll_listener(
482 listener: &mut Option<EventListener>,
483 cx: &mut std::task::Context<'_>,
484) -> Poll<()> {
485 if let Some(l) = listener {
486 ready!(Pin::new(l).poll(cx));
487 *listener = None;
488 }
489 Poll::Ready(())
490}
491
492fn suppress_top_level_task_check<R>(strongly_consistent: bool, f: impl FnOnce() -> R) -> R {
499 if cfg!(debug_assertions) && strongly_consistent {
500 SUPPRESS_EVENTUAL_CONSISTENCY_TOP_LEVEL_TASK_CHECK.sync_scope(true, f)
502 } else {
503 f()
504 }
505}
506
507fn execute_inline(key: ScheduleKey) {
513 execute_read_target_inline(&*turbo_tasks(), key);
514}
515
516#[must_use]
517pub struct ResolveRawVcFuture {
518 current: RawVc,
519 read_output_options: ReadOutputOptions,
520 strongly_consistent: bool,
523 listener: Option<EventListener>,
524}
525
526impl ResolveRawVcFuture {
527 fn new(vc: RawVc) -> Self {
528 ResolveRawVcFuture {
529 current: vc,
530 read_output_options: ReadOutputOptions::default(),
531 strongly_consistent: false,
532 listener: None,
533 }
534 }
535
536 pub fn strongly_consistent(mut self) -> Self {
537 self.strongly_consistent = true;
538 self.read_output_options.consistency = ReadConsistency::Strong;
539 self
540 }
541
542 pub(crate) fn track_with_key(mut self) -> Self {
545 self.read_output_options.tracking = ReadTracking::Tracked;
546 self
547 }
548
549 pub(crate) fn untracked(mut self) -> Self {
552 self.read_output_options.tracking = ReadTracking::TrackOnlyError;
553 self
554 }
555}
556
557impl Future for ResolveRawVcFuture {
558 type Output = Result<RawVc>;
559
560 #[inline(never)]
561 fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
562 let this = unsafe { self.get_unchecked_mut() };
564
565 let strongly_consistent = this.strongly_consistent;
566 let mut poll_fn = |tt: &Arc<dyn TurboTasksApi>,
570 execute_inline: &mut Option<ScheduleKey>|
571 -> Poll<Self::Output> {
572 'outer: loop {
573 ready!(poll_listener(&mut this.listener, cx));
574 let (listener, key) = match this.current.unpack() {
575 RawVcUnpacked::TaskOutput(task) => {
576 let read_result = tt.try_read_task_output(task, this.read_output_options);
577 match read_result {
578 Ok(ReadOutcome::Value(vc)) => {
579 this.read_output_options.consistency = ReadConsistency::Eventual;
588 this.current = vc;
589 continue 'outer;
590 }
591 Ok(ReadOutcome::Scheduled(listener)) => {
593 (listener, Some(ScheduleKey::Task(task)))
594 }
595 Ok(ReadOutcome::InProgress(listener)) => {
596 #[cfg(feature = "inline_execution_stats")]
600 tt.note_waited_for_in_progress_task();
601 this.listener = Some(listener);
602 continue 'outer;
603 }
604 Err(err) => return Poll::Ready(Err(err)),
605 }
606 }
607 RawVcUnpacked::TaskCell(_, _) => return Poll::Ready(Ok(this.current)),
608 RawVcUnpacked::LocalOutput(execution_id, local_task_id, ..) => {
609 debug_assert_eq!(
610 this.read_output_options.consistency,
611 ReadConsistency::Eventual
612 );
613 let read_result = tt.try_read_local_output(execution_id, local_task_id);
614 match read_result {
615 Ok(Ok(vc)) => {
616 this.current = vc;
617 continue 'outer;
618 }
619 Ok(Err(listener)) => (
620 listener,
621 Some(ScheduleKey::LocalTask(execution_id, local_task_id)),
622 ),
623 Err(err) => return Poll::Ready(Err(err)),
624 }
625 }
626 };
627 this.listener = Some(listener);
630 *execute_inline = key;
631 return Poll::Pending;
632 }
633 };
634
635 loop {
636 let mut execute_inline_key = None;
637 let result = suppress_top_level_task_check(strongly_consistent, || {
644 with_turbo_tasks(|tt| poll_fn(tt, &mut execute_inline_key))
645 });
646 if let Some(key) = execute_inline_key {
647 execute_inline(key);
649 continue;
650 }
651 return result;
652 }
653 }
654}
655
656impl Unpin for ResolveRawVcFuture {}
657
658#[must_use]
659pub struct ReadRawVcFuture {
660 read_cell_options: ReadCellOptions,
661 state: ReadRawVcState,
662}
663
664enum ReadRawVcState {
667 Resolving(ResolveRawVcFuture),
669 Reading {
671 task: TaskId,
672 index: CellId,
673 strongly_consistent: bool,
678 listener: Option<EventListener>,
679 },
680}
681
682impl ReadRawVcFuture {
683 pub(crate) fn new(vc: RawVc) -> Self {
684 ReadRawVcFuture {
685 read_cell_options: ReadCellOptions::default(),
686 state: ReadRawVcState::Resolving(ResolveRawVcFuture::new(vc)),
687 }
688 }
689
690 fn map_resolve(mut self, f: impl FnOnce(ResolveRawVcFuture) -> ResolveRawVcFuture) -> Self {
691 match self.state {
692 ReadRawVcState::Resolving(resolve) => {
693 self.state = ReadRawVcState::Resolving(f(resolve));
694 }
695 ReadRawVcState::Reading { .. } => {
696 unreachable!("builder methods are only called before polling");
697 }
698 }
699 self
700 }
701
702 pub fn strongly_consistent(self) -> Self {
704 self.map_resolve(|r| r.strongly_consistent())
705 }
706
707 pub fn track_with_key(mut self, key: u64) -> Self {
709 self.read_cell_options.tracking = ReadCellTracking::Tracked { key: Some(key) };
710 self.map_resolve(|r| r.track_with_key())
711 }
712
713 pub fn untracked(mut self) -> Self {
719 self.read_cell_options.tracking = ReadCellTracking::TrackOnlyError;
720 self.map_resolve(|r| r.untracked())
721 }
722
723 pub fn final_read_hint(mut self) -> Self {
725 self.read_cell_options.final_read_hint = true;
726 self
727 }
728}
729
730impl Future for ReadRawVcFuture {
731 type Output = Result<TypedCellContent>;
732
733 #[inline(never)]
734 fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
735 let this = unsafe { self.get_unchecked_mut() };
737
738 if let ReadRawVcState::Resolving(resolve) = &mut this.state {
743 let strongly_consistent = resolve.strongly_consistent;
744 match ready!(Pin::new(resolve).poll(cx)) {
745 Err(err) => return Poll::Ready(Err(err)),
746 Ok(resolved) => {
747 let Some((task, index)) = resolved.as_task_cell() else {
748 unreachable!("ResolveRawVcFuture always resolves to a TaskCell")
749 };
750 this.state = ReadRawVcState::Reading {
751 task,
752 index,
753 strongly_consistent,
754 listener: None,
755 };
756 }
757 }
758 }
759
760 let ReadRawVcState::Reading {
762 task,
763 index,
764 strongly_consistent,
765 listener,
766 } = &mut this.state
767 else {
768 unreachable!("phase 1 transitioned to Reading above");
769 };
770 let task = *task;
771 let index = *index;
772 let read_cell_options = this.read_cell_options;
773
774 let strongly_consistent = *strongly_consistent;
775
776 let mut poll_fn = |tt: &Arc<dyn TurboTasksApi>,
777 execute_inline: &mut Option<ScheduleKey>|
778 -> Poll<Self::Output> {
779 loop {
780 ready!(poll_listener(listener, cx));
781 let (new_listener, key) =
782 match tt.try_read_task_cell(task, index, read_cell_options) {
783 Ok(ReadOutcome::Value(content)) => return Poll::Ready(Ok(content)),
784 Ok(ReadOutcome::Scheduled(l)) => (l, Some(ScheduleKey::Task(task))),
785 Ok(ReadOutcome::InProgress(l)) => {
786 #[cfg(feature = "inline_execution_stats")]
790 tt.note_waited_for_in_progress_task();
791 *listener = Some(l);
792 continue;
793 }
794 Err(err) => return Poll::Ready(Err(err)),
795 };
796 *listener = Some(new_listener);
800 *execute_inline = key;
801 return Poll::Pending;
802 }
803 };
804
805 loop {
806 let mut execute_inline_key = None;
807 let result = suppress_top_level_task_check(strongly_consistent, || {
812 with_turbo_tasks(|tt| poll_fn(tt, &mut execute_inline_key))
813 });
814 if let Some(key) = execute_inline_key {
815 execute_inline(key);
817 continue;
818 }
819 return result;
820 }
821 }
822}
823
824impl Unpin for ReadRawVcFuture {}
825
826#[cfg(test)]
827mod tests {
828 use super::*;
829
830 #[test]
833 fn cell_id_is_four_bytes() {
834 assert_eq!(size_of::<CellId>(), 4);
835 assert_eq!(size_of::<Option<CellId>>(), 4);
836 }
837
838 #[test]
841 fn cell_id_pack_unpack_round_trip() {
842 let type_ids = [1u16, 2, 100, CellId::MAX_VALUE_TYPE_ID];
843 let indices = [0u32, 1, 12345, CellId::MAX_CELL_INDEX];
844 for &raw_ty in &type_ids {
845 let type_id = unsafe { ValueTypeId::new_unchecked(raw_ty) };
847 for &index in &indices {
848 let cell = CellId::new(type_id, index);
849 assert_eq!(cell.type_id(), type_id, "type_id round-trip for {raw_ty}");
850 assert_eq!(cell.index(), index, "index round-trip for {index}");
851 }
852 }
853 }
854
855 #[test]
858 fn cell_id_packing_is_bijective() {
859 let a = CellId::new(unsafe { ValueTypeId::new_unchecked(1) }, 0);
861 let b = CellId::new(unsafe { ValueTypeId::new_unchecked(1) }, 1);
862 let c = CellId::new(unsafe { ValueTypeId::new_unchecked(2) }, 0);
863 assert_ne!(a, b);
864 assert_ne!(a, c);
865 assert_ne!(b, c);
866 }
867
868 #[test]
870 fn raw_vc_is_eight_bytes() {
871 assert_eq!(size_of::<RawVc>(), 8);
872 assert_eq!(size_of::<Option<RawVc>>(), 8);
873 }
874
875 #[test]
880 fn raw_vc_pack_unpack_round_trip() {
881 let tasks = [
883 1u32,
884 2,
885 crate::TRANSIENT_TASK_BIT - 1,
886 crate::TRANSIENT_TASK_BIT,
887 TASK_ID_MAX,
888 ];
889 for &t in &tasks {
890 let task = unsafe { TaskId::new_unchecked(t) };
891
892 let vc = RawVc::task_output(task);
894 assert_eq!(vc.unpack(), RawVcUnpacked::TaskOutput(task));
895 assert!(!vc.is_resolved() && !vc.is_local_output());
896 assert_eq!(vc.is_transient(), task.is_transient());
897 assert_eq!(vc.try_get_task_id(), Some(task));
898 assert_eq!(vc.as_task_output(), Some(task));
900 assert_eq!(vc.as_task_cell(), None);
901 assert_eq!(vc.as_local_output(), None);
902
903 for cell in [
905 CellId::new(unsafe { ValueTypeId::new_unchecked(1) }, 0),
906 CellId::new(
907 unsafe { ValueTypeId::new_unchecked(CellId::MAX_VALUE_TYPE_ID) },
908 CellId::MAX_CELL_INDEX,
909 ),
910 ] {
911 let vc = RawVc::task_cell(task, cell);
912 assert_eq!(vc.unpack(), RawVcUnpacked::TaskCell(task, cell));
913 assert!(vc.is_resolved());
914 assert_eq!(vc.try_get_task_id(), Some(task));
915 assert_eq!(vc.try_get_type_id(), Some(cell.type_id()));
916 assert_eq!(vc.as_task_cell(), Some((task, cell)));
918 assert_eq!(vc.as_task_output(), None);
919 assert_eq!(vc.as_local_output(), None);
920 }
921 }
922
923 for persistence in [TaskPersistence::Persistent, TaskPersistence::Transient] {
925 for (e, l) in [(1u16, 1u32), (u16::MAX, u32::MAX)] {
926 let exec = unsafe { ExecutionId::new_unchecked(e) };
927 let local = unsafe { LocalTaskId::new_unchecked(l) };
928 let vc = RawVc::local_output(exec, local, persistence);
929 assert_eq!(
930 vc.unpack(),
931 RawVcUnpacked::LocalOutput(exec, local, persistence)
932 );
933 assert!(vc.is_local_output());
934 assert_eq!(vc.is_transient(), persistence == TaskPersistence::Transient);
935 assert_eq!(vc.try_get_task_id(), None);
936 assert_eq!(vc.as_local_output(), Some((exec, local, persistence)));
938 assert_eq!(vc.as_task_output(), None);
939 assert_eq!(vc.as_task_cell(), None);
940 }
941 }
942 }
943
944 #[test]
950 fn raw_vc_discriminator_is_unambiguous() {
951 let task = unsafe { TaskId::new_unchecked(123) };
953 let cell = CellId::new(unsafe { ValueTypeId::new_unchecked(1) }, 0);
954
955 let output = RawVc::task_output(task);
956 let task_cell = RawVc::task_cell(task, cell);
957 assert!(output.is_task_output() && !output.is_task_cell() && !output.is_local_output());
958 assert!(
959 task_cell.is_task_cell() && !task_cell.is_task_output() && !task_cell.is_local_output()
960 );
961 assert_ne!(output, task_cell);
963 assert_eq!(output.read_task_id(), task_cell.read_task_id());
964
965 let local = RawVc::local_output(
968 unsafe { ExecutionId::new_unchecked(u16::MAX) },
969 unsafe { LocalTaskId::new_unchecked(u32::MAX) },
970 TaskPersistence::Persistent,
971 );
972 assert!(local.is_local_output() && !local.is_task_cell() && !local.is_task_output());
973 }
974
975 #[test]
976 #[cfg(debug_assertions)]
977 #[should_panic(expected = "TaskId exceeds 31 bits")]
978 fn task_output_panics_on_out_of_range_task_id() {
979 let task = unsafe { TaskId::new_unchecked(TASK_ID_MAX + 1) };
982 let _ = RawVc::task_output(task);
983 }
984
985 #[test]
986 #[cfg(debug_assertions)]
987 #[should_panic(expected = "TaskId exceeds 31 bits")]
988 fn task_cell_panics_on_out_of_range_task_id() {
989 let task = unsafe { TaskId::new_unchecked(TASK_ID_MAX + 1) };
991 let cell = CellId::new(unsafe { ValueTypeId::new_unchecked(1) }, 0);
992 let _ = RawVc::task_cell(task, cell);
993 }
994
995 #[test]
996 #[cfg(debug_assertions)]
997 #[should_panic(expected = "exceeds")]
998 fn cell_id_panics_on_out_of_range_type_id() {
999 let type_id = unsafe { ValueTypeId::new_unchecked(CellId::MAX_VALUE_TYPE_ID + 1) };
1001 let _ = CellId::new(type_id, 0);
1002 }
1003 #[test]
1004 #[cfg(debug_assertions)]
1005 #[should_panic(expected = "exceeds")]
1006 fn cell_id_panics_on_out_of_range_index() {
1007 let type_id = unsafe { ValueTypeId::new_unchecked(1) };
1008 let _ = CellId::new(type_id, CellId::MAX_CELL_INDEX + 1);
1009 }
1010}