Address soundness hole in cycle prevention
This commit is contained in:
1 parent
4ad88bcd68
commit
f82205baf4
80 files changed
+730
-717
No files matched your search
+53
-59
@@ -11,52 +11,60 @@ use crate::builder::TaskBuilder;
|
||||
use crate::task::Task;
|
||||
use crate::types::{NodeId, TaskHandle};
|
||||
|
||||
trait IntoNodeRef {
|
||||
type Output;
|
||||
fn into_node_ref(self) -> NodeId;
|
||||
}
|
||||
|
||||
impl<T> IntoNodeRef for &TaskHandle<T> {
|
||||
type Output = T;
|
||||
fn into_node_ref(self) -> NodeId {
|
||||
self.id
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoNodeRef for TaskHandle<T> {
|
||||
type Output = T;
|
||||
fn into_node_ref(self) -> NodeId {
|
||||
self.id
|
||||
}
|
||||
}
|
||||
|
||||
impl<'runner, Tk: Task, Deps> IntoNodeRef for TaskBuilder<'runner, Tk, Deps> {
|
||||
type Output = Tk::Output;
|
||||
|
||||
fn into_node_ref(self) -> NodeId {
|
||||
self.id
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait DepsTuple<Input> {
|
||||
fn to_node_ids(&self) -> Vec<NodeId>;
|
||||
fn to_node_ids(self) -> Vec<NodeId>;
|
||||
}
|
||||
|
||||
// Implementation for unit (no dependencies)
|
||||
impl DepsTuple<()> for () {
|
||||
fn to_node_ids(&self) -> Vec<NodeId> {
|
||||
fn to_node_ids(self) -> Vec<NodeId> {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation for single dependency (all forms)
|
||||
impl<T> DepsTuple<T> for &TaskHandle<T> {
|
||||
fn to_node_ids(&self) -> Vec<NodeId> {
|
||||
vec![self.id]
|
||||
impl<T> DepsTuple<T::Output> for T
|
||||
where
|
||||
T: IntoNodeRef,
|
||||
{
|
||||
fn to_node_ids(self) -> Vec<NodeId> {
|
||||
vec![self.into_node_ref()]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DepsTuple<T> for TaskHandle<T> {
|
||||
fn to_node_ids(&self) -> Vec<NodeId> {
|
||||
vec![self.id]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DepsTuple<T> for (&TaskHandle<T>,) {
|
||||
fn to_node_ids(&self) -> Vec<NodeId> {
|
||||
vec![self.0.id]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DepsTuple<T> for (TaskHandle<T>,) {
|
||||
fn to_node_ids(&self) -> Vec<NodeId> {
|
||||
vec![self.0.id]
|
||||
}
|
||||
}
|
||||
|
||||
// Support for &TaskBuilder
|
||||
impl<'a, Tk: Task, Deps> DepsTuple<Tk::Output> for &TaskBuilder<'a, Tk, Deps> {
|
||||
fn to_node_ids(&self) -> Vec<NodeId> {
|
||||
vec![self.id]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Tk: Task, Deps> DepsTuple<Tk::Output> for (&TaskBuilder<'a, Tk, Deps>,) {
|
||||
fn to_node_ids(&self) -> Vec<NodeId> {
|
||||
vec![self.0.id]
|
||||
impl<T> DepsTuple<T::Output> for (T,)
|
||||
where
|
||||
T: IntoNodeRef,
|
||||
{
|
||||
fn to_node_ids(self) -> Vec<NodeId> {
|
||||
vec![self.0.into_node_ref()]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,10 +73,6 @@ impl<'a, Tk: Task, Deps> DepsTuple<Tk::Output> for (&TaskBuilder<'a, Tk, Deps>,)
|
||||
/// This macro exists because Rust lacks variadic generics - we need separate implementations
|
||||
/// for each tuple size.
|
||||
///
|
||||
/// For each tuple size, generates two implementations:
|
||||
/// - `(&TaskHandle<A>, &TaskHandle<B>, ...)` → `DepsTuple<(A, B, ...)>`
|
||||
/// - `(&TaskBuilder<TkA, _>, &TaskBuilder<TkB, _>, ...)` → `DepsTuple<(TkA::Output, TkB::Output, ...)>`
|
||||
///
|
||||
/// This allows flexible dependency specification:
|
||||
/// ```ignore
|
||||
/// // Using handles
|
||||
@@ -78,37 +82,27 @@ impl<'a, Tk: Task, Deps> DepsTuple<Tk::Output> for (&TaskBuilder<'a, Tk, Deps>,)
|
||||
/// task.depends_on((&dag.add_task(TaskA), &dag.add_task(TaskB)))
|
||||
/// ```
|
||||
macro_rules! impl_deps_tuple {
|
||||
($($T:ident : $Tk:ident : $D:ident),+) => {
|
||||
// For &TaskHandle tuples
|
||||
impl<$($T),+> DepsTuple<($($T,)+)> for ($(&TaskHandle<$T>,)+) {
|
||||
($($T:ident),+) => {
|
||||
// For tuples of any combination of &TaskHandle, TaskHandle, and TaskBuilder
|
||||
impl<$($T: IntoNodeRef),+> DepsTuple<($($T::Output,)+)> for ($($T,)+) {
|
||||
#[allow(non_snake_case)]
|
||||
fn to_node_ids(&self) -> Vec<NodeId> {
|
||||
fn to_node_ids(self) -> Vec<NodeId> {
|
||||
let ($($T,)+) = self;
|
||||
vec![$($T.id,)+]
|
||||
}
|
||||
}
|
||||
|
||||
// For &TaskBuilder tuples
|
||||
impl<'a, $($Tk: Task, $D),+> DepsTuple<($($Tk::Output,)+)> for ($(&TaskBuilder<'a, $Tk, $D>,)+) {
|
||||
#[allow(non_snake_case)]
|
||||
fn to_node_ids(&self) -> Vec<NodeId> {
|
||||
let ($($T,)+) = self;
|
||||
vec![$($T.id,)+]
|
||||
vec![$($T.into_node_ref(),)+]
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Generate DepsTuple implementations for tuples of size 2-8.
|
||||
// Each invocation generates 2 implementations (TaskHandle refs and TaskBuilder refs).
|
||||
// Supporting up to 8 elements covers the vast majority of use cases.
|
||||
impl_deps_tuple!(A:TkA:DA, B:TkB:DB);
|
||||
impl_deps_tuple!(A:TkA:DA, B:TkB:DB, C:TkC:DC);
|
||||
impl_deps_tuple!(A:TkA:DA, B:TkB:DB, C:TkC:DC, D:TkD:DD);
|
||||
impl_deps_tuple!(A:TkA:DA, B:TkB:DB, C:TkC:DC, D:TkD:DD, E:TkE:DE);
|
||||
impl_deps_tuple!(A:TkA:DA, B:TkB:DB, C:TkC:DC, D:TkD:DD, E:TkE:DE, F:TkF:DF);
|
||||
impl_deps_tuple!(A:TkA:DA, B:TkB:DB, C:TkC:DC, D:TkD:DD, E:TkE:DE, F:TkF:DF, G:TkG:DG);
|
||||
impl_deps_tuple!(A:TkA:DA, B:TkB:DB, C:TkC:DC, D:TkD:DD, E:TkE:DE, F:TkF:DF, G:TkG:DG, H:TkH:DH);
|
||||
impl_deps_tuple!(T1, T2);
|
||||
impl_deps_tuple!(T1, T2, T3);
|
||||
impl_deps_tuple!(T1, T2, T3, T4);
|
||||
impl_deps_tuple!(T1, T2, T3, T4, T5);
|
||||
impl_deps_tuple!(T1, T2, T3, T4, T5, T6);
|
||||
impl_deps_tuple!(T1, T2, T3, T4, T5, T6, T7);
|
||||
impl_deps_tuple!(T1, T2, T3, T4, T5, T6, T7, T8);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
Reference in new issue
Block a user