bug: support partial wiring of multi-output sources#883
Conversation
There was a problem hiding this comment.
Pull request overview
Adds runtime projection support so downstream tasks can subscribe to only a subset of a multi-output source’s messages, fixing partial wiring for multi-output tasks in the proc-macro-generated runtime.
Changes:
- Introduces
CuOutputGetandCuOutputProjectto extract/project specificCuMsg<T>out of a source output pack (including tuples). - Updates derive macro generated execution to run the source into a “full output” buffer, then project into the connected downstream output slot.
- Adds a compile-pass test + config validating partial subscription (
i32only) from a(i32, bool)source.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| core/cu29_runtime/src/cutask.rs | Adds typed lookup + projection traits/functions enabling subset routing from multi-output sources. |
| core/cu29_derive/src/lib.rs | Changes generated execution to process into a full output pack and project into the connected output type. |
| core/cu29_derive/tests/config/multi_output_source_subset_valid.ron | Adds config wiring a multi-output source to a sink subscribing to only i32. |
| core/cu29_derive/tests/compile_pass/copper_runtime/multi_output_source_subset.rs | Adds compile-pass test verifying the partial subscription compiles. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| macro_rules! impl_cu_output_get_tuple { | ||
| ($($idx:tt => $name:ident),+) => { | ||
| impl<T, $($name),+> CuOutputGet<T> for ($(CuMsg<$name>,)+) | ||
| where | ||
| T: CuMsgPayload + 'static, | ||
| $($name: CuMsgPayload + 'static),+ | ||
| { | ||
| fn output_msg(&self) -> &CuMsg<T> { | ||
| $( | ||
| if TypeId::of::<T>() == TypeId::of::<$name>() { | ||
| let msg = &self.$idx; | ||
| // SAFETY: TypeId equality proves T == $name. | ||
| return unsafe { &*(msg as *const CuMsg<$name> as *const CuMsg<T>) }; | ||
| } | ||
| )+ | ||
| panic!( | ||
| "Requested output message type '{}' is not present in source output tuple", | ||
| type_name::<T>() | ||
| ); | ||
| } | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| impl_cu_output_get_tuple!(0 => T1, 1 => T2); | ||
| impl_cu_output_get_tuple!(0 => T1, 1 => T2, 2 => T3); | ||
| impl_cu_output_get_tuple!(0 => T1, 1 => T2, 2 => T3, 3 => T4); | ||
| impl_cu_output_get_tuple!(0 => T1, 1 => T2, 2 => T3, 3 => T4, 4 => T5); | ||
| impl_cu_output_get_tuple!(0 => T1, 1 => T2, 2 => T3, 3 => T4, 4 => T5, 5 => T6); | ||
| impl_cu_output_get_tuple!(0 => T1, 1 => T2, 2 => T3, 3 => T4, 4 => T5, 5 => T6, 6 => T7); | ||
| impl_cu_output_get_tuple!(0 => T1, 1 => T2, 2 => T3, 3 => T4, 4 => T5, 5 => T6, 6 => T7, 7 => T8); | ||
| impl_cu_output_get_tuple!(0 => T1, 1 => T2, 2 => T3, 3 => T4, 4 => T5, 5 => T6, 6 => T7, 7 => T8, 8 => T9); |
There was a problem hiding this comment.
The tuple CuOutputGet<T> impl is overly permissive: it makes (CuMsg<T1>, CuMsg<T2>, ...) implement CuOutputGet<T> for any T, so a missing type compiles and then panics at runtime. It also requires unsafe casting. Prefer generating per-element trait impls (e.g., implement CuOutputGet<T1> and CuOutputGet<T2> separately for the tuple, each returning &self.0 / &self.1), which removes TypeId/unsafe entirely and turns “type not present” into a compile-time error (and prevents ambiguous duplicate payload types via coherence).
| macro_rules! impl_cu_output_get_tuple { | |
| ($($idx:tt => $name:ident),+) => { | |
| impl<T, $($name),+> CuOutputGet<T> for ($(CuMsg<$name>,)+) | |
| where | |
| T: CuMsgPayload + 'static, | |
| $($name: CuMsgPayload + 'static),+ | |
| { | |
| fn output_msg(&self) -> &CuMsg<T> { | |
| $( | |
| if TypeId::of::<T>() == TypeId::of::<$name>() { | |
| let msg = &self.$idx; | |
| // SAFETY: TypeId equality proves T == $name. | |
| return unsafe { &*(msg as *const CuMsg<$name> as *const CuMsg<T>) }; | |
| } | |
| )+ | |
| panic!( | |
| "Requested output message type '{}' is not present in source output tuple", | |
| type_name::<T>() | |
| ); | |
| } | |
| } | |
| }; | |
| } | |
| impl_cu_output_get_tuple!(0 => T1, 1 => T2); | |
| impl_cu_output_get_tuple!(0 => T1, 1 => T2, 2 => T3); | |
| impl_cu_output_get_tuple!(0 => T1, 1 => T2, 2 => T3, 3 => T4); | |
| impl_cu_output_get_tuple!(0 => T1, 1 => T2, 2 => T3, 3 => T4, 4 => T5); | |
| impl_cu_output_get_tuple!(0 => T1, 1 => T2, 2 => T3, 3 => T4, 4 => T5, 5 => T6); | |
| impl_cu_output_get_tuple!(0 => T1, 1 => T2, 2 => T3, 3 => T4, 4 => T5, 5 => T6, 6 => T7); | |
| impl_cu_output_get_tuple!(0 => T1, 1 => T2, 2 => T3, 3 => T4, 4 => T5, 5 => T6, 6 => T7, 7 => T8); | |
| impl_cu_output_get_tuple!(0 => T1, 1 => T2, 2 => T3, 3 => T4, 4 => T5, 5 => T6, 6 => T7, 7 => T8, 8 => T9); | |
| impl<T1, T2> CuOutputGet<T1> for (CuMsg<T1>, CuMsg<T2>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T1> { | |
| &self.0 | |
| } | |
| } | |
| impl<T1, T2> CuOutputGet<T2> for (CuMsg<T1>, CuMsg<T2>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T2> { | |
| &self.1 | |
| } | |
| } | |
| impl<T1, T2, T3> CuOutputGet<T1> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T1> { | |
| &self.0 | |
| } | |
| } | |
| impl<T1, T2, T3> CuOutputGet<T2> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T2> { | |
| &self.1 | |
| } | |
| } | |
| impl<T1, T2, T3> CuOutputGet<T3> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T3> { | |
| &self.2 | |
| } | |
| } | |
| impl<T1, T2, T3, T4> CuOutputGet<T1> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T1> { | |
| &self.0 | |
| } | |
| } | |
| impl<T1, T2, T3, T4> CuOutputGet<T2> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T2> { | |
| &self.1 | |
| } | |
| } | |
| impl<T1, T2, T3, T4> CuOutputGet<T3> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T3> { | |
| &self.2 | |
| } | |
| } | |
| impl<T1, T2, T3, T4> CuOutputGet<T4> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T4> { | |
| &self.3 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5> CuOutputGet<T1> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T1> { | |
| &self.0 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5> CuOutputGet<T2> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T2> { | |
| &self.1 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5> CuOutputGet<T3> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T3> { | |
| &self.2 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5> CuOutputGet<T4> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T4> { | |
| &self.3 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5> CuOutputGet<T5> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T5> { | |
| &self.4 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5, T6> CuOutputGet<T1> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>, CuMsg<T6>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| T6: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T1> { | |
| &self.0 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5, T6> CuOutputGet<T2> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>, CuMsg<T6>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| T6: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T2> { | |
| &self.1 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5, T6> CuOutputGet<T3> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>, CuMsg<T6>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| T6: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T3> { | |
| &self.2 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5, T6> CuOutputGet<T4> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>, CuMsg<T6>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| T6: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T4> { | |
| &self.3 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5, T6> CuOutputGet<T5> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>, CuMsg<T6>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| T6: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T5> { | |
| &self.4 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5, T6> CuOutputGet<T6> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>, CuMsg<T6>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| T6: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T6> { | |
| &self.5 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5, T6, T7> CuOutputGet<T1> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>, CuMsg<T6>, CuMsg<T7>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| T6: CuMsgPayload + 'static, | |
| T7: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T1> { | |
| &self.0 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5, T6, T7> CuOutputGet<T2> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>, CuMsg<T6>, CuMsg<T7>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| T6: CuMsgPayload + 'static, | |
| T7: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T2> { | |
| &self.1 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5, T6, T7> CuOutputGet<T3> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>, CuMsg<T6>, CuMsg<T7>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| T6: CuMsgPayload + 'static, | |
| T7: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T3> { | |
| &self.2 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5, T6, T7> CuOutputGet<T4> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>, CuMsg<T6>, CuMsg<T7>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| T6: CuMsgPayload + 'static, | |
| T7: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T4> { | |
| &self.3 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5, T6, T7> CuOutputGet<T5> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>, CuMsg<T6>, CuMsg<T7>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| T6: CuMsgPayload + 'static, | |
| T7: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T5> { | |
| &self.4 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5, T6, T7> CuOutputGet<T6> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>, CuMsg<T6>, CuMsg<T7>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| T6: CuMsgPayload + 'static, | |
| T7: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T6> { | |
| &self.5 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5, T6, T7> CuOutputGet<T7> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>, CuMsg<T6>, CuMsg<T7>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| T6: CuMsgPayload + 'static, | |
| T7: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T7> { | |
| &self.6 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5, T6, T7, T8> CuOutputGet<T1> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>, CuMsg<T6>, CuMsg<T7>, CuMsg<T8>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| T6: CuMsgPayload + 'static, | |
| T7: CuMsgPayload + 'static, | |
| T8: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T1> { | |
| &self.0 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5, T6, T7, T8> CuOutputGet<T2> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>, CuMsg<T6>, CuMsg<T7>, CuMsg<T8>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| T6: CuMsgPayload + 'static, | |
| T7: CuMsgPayload + 'static, | |
| T8: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T2> { | |
| &self.1 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5, T6, T7, T8> CuOutputGet<T3> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>, CuMsg<T6>, CuMsg<T7>, CuMsg<T8>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| T6: CuMsgPayload + 'static, | |
| T7: CuMsgPayload + 'static, | |
| T8: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T3> { | |
| &self.2 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5, T6, T7, T8> CuOutputGet<T4> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>, CuMsg<T6>, CuMsg<T7>, CuMsg<T8>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| T6: CuMsgPayload + 'static, | |
| T7: CuMsgPayload + 'static, | |
| T8: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T4> { | |
| &self.3 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5, T6, T7, T8> CuOutputGet<T5> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>, CuMsg<T6>, CuMsg<T7>, CuMsg<T8>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| T6: CuMsgPayload + 'static, | |
| T7: CuMsgPayload + 'static, | |
| T8: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T5> { | |
| &self.4 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5, T6, T7, T8> CuOutputGet<T6> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>, CuMsg<T6>, CuMsg<T7>, CuMsg<T8>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| T6: CuMsgPayload + 'static, | |
| T7: CuMsgPayload + 'static, | |
| T8: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T6> { | |
| &self.5 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5, T6, T7, T8> CuOutputGet<T7> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>, CuMsg<T6>, CuMsg<T7>, CuMsg<T8>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| T6: CuMsgPayload + 'static, | |
| T7: CuMsgPayload + 'static, | |
| T8: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T7> { | |
| &self.6 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5, T6, T7, T8> CuOutputGet<T8> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>, CuMsg<T6>, CuMsg<T7>, CuMsg<T8>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| T6: CuMsgPayload + 'static, | |
| T7: CuMsgPayload + 'static, | |
| T8: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T8> { | |
| &self.7 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5, T6, T7, T8, T9> CuOutputGet<T1> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>, CuMsg<T6>, CuMsg<T7>, CuMsg<T8>, CuMsg<T9>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| T6: CuMsgPayload + 'static, | |
| T7: CuMsgPayload + 'static, | |
| T8: CuMsgPayload + 'static, | |
| T9: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T1> { | |
| &self.0 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5, T6, T7, T8, T9> CuOutputGet<T2> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>, CuMsg<T6>, CuMsg<T7>, CuMsg<T8>, CuMsg<T9>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| T6: CuMsgPayload + 'static, | |
| T7: CuMsgPayload + 'static, | |
| T8: CuMsgPayload + 'static, | |
| T9: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T2> { | |
| &self.1 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5, T6, T7, T8, T9> CuOutputGet<T3> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>, CuMsg<T6>, CuMsg<T7>, CuMsg<T8>, CuMsg<T9>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| T6: CuMsgPayload + 'static, | |
| T7: CuMsgPayload + 'static, | |
| T8: CuMsgPayload + 'static, | |
| T9: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T3> { | |
| &self.2 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5, T6, T7, T8, T9> CuOutputGet<T4> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>, CuMsg<T6>, CuMsg<T7>, CuMsg<T8>, CuMsg<T9>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| T6: CuMsgPayload + 'static, | |
| T7: CuMsgPayload + 'static, | |
| T8: CuMsgPayload + 'static, | |
| T9: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T4> { | |
| &self.3 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5, T6, T7, T8, T9> CuOutputGet<T5> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>, CuMsg<T6>, CuMsg<T7>, CuMsg<T8>, CuMsg<T9>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| T6: CuMsgPayload + 'static, | |
| T7: CuMsgPayload + 'static, | |
| T8: CuMsgPayload + 'static, | |
| T9: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T5> { | |
| &self.4 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5, T6, T7, T8, T9> CuOutputGet<T6> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>, CuMsg<T6>, CuMsg<T7>, CuMsg<T8>, CuMsg<T9>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| T6: CuMsgPayload + 'static, | |
| T7: CuMsgPayload + 'static, | |
| T8: CuMsgPayload + 'static, | |
| T9: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T6> { | |
| &self.5 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5, T6, T7, T8, T9> CuOutputGet<T7> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>, CuMsg<T6>, CuMsg<T7>, CuMsg<T8>, CuMsg<T9>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| T6: CuMsgPayload + 'static, | |
| T7: CuMsgPayload + 'static, | |
| T8: CuMsgPayload + 'static, | |
| T9: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T7> { | |
| &self.6 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5, T6, T7, T8, T9> CuOutputGet<T8> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>, CuMsg<T6>, CuMsg<T7>, CuMsg<T8>, CuMsg<T9>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| T6: CuMsgPayload + 'static, | |
| T7: CuMsgPayload + 'static, | |
| T8: CuMsgPayload + 'static, | |
| T9: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T8> { | |
| &self.7 | |
| } | |
| } | |
| impl<T1, T2, T3, T4, T5, T6, T7, T8, T9> CuOutputGet<T9> for (CuMsg<T1>, CuMsg<T2>, CuMsg<T3>, CuMsg<T4>, CuMsg<T5>, CuMsg<T6>, CuMsg<T7>, CuMsg<T8>, CuMsg<T9>) | |
| where | |
| T1: CuMsgPayload + 'static, | |
| T2: CuMsgPayload + 'static, | |
| T3: CuMsgPayload + 'static, | |
| T4: CuMsgPayload + 'static, | |
| T5: CuMsgPayload + 'static, | |
| T6: CuMsgPayload + 'static, | |
| T7: CuMsgPayload + 'static, | |
| T8: CuMsgPayload + 'static, | |
| T9: CuMsgPayload + 'static, | |
| { | |
| fn output_msg(&self) -> &CuMsg<T9> { | |
| &self.8 | |
| } | |
| } |
There was a problem hiding this comment.
That suggestion, as written, is not implementable on stable Rust.
It triggers coherence overlap (E0119) immediately, because these two impls overlap when T1 == T2:
- impl<T1, T2> CuOutputGet for (CuMsg, CuMsg)
- impl<T1, T2> CuOutputGet for (CuMsg, CuMsg)
| { | ||
| fn output_msg(&self) -> &CuMsg<T> { | ||
| $( | ||
| if TypeId::of::<T>() == TypeId::of::<$name>() { |
There was a problem hiding this comment.
If this runtime lookup approach stays, consider fully qualifying TypeId/type_name as core::any::TypeId and core::any::type_name (or importing them explicitly) to avoid relying on ambient imports and to keep the code no_std-friendly.
| "Requested output message type '{}' is not present in source output tuple", | ||
| type_name::<T>() |
There was a problem hiding this comment.
If this runtime lookup approach stays, consider fully qualifying TypeId/type_name as core::any::TypeId and core::any::type_name (or importing them explicitly) to avoid relying on ambient imports and to keep the code no_std-friendly.
| step: CuComponentState::Process, | ||
| culistid: Some(clid), | ||
| }); | ||
| let mut __cu_full_output = Default::default(); |
There was a problem hiding this comment.
The generated code now unconditionally constructs a fresh __cu_full_output via Default::default(). This introduces an implicit (and potentially macro-error-message-unfriendly) requirement that the task’s full Output type is Default, and it also adds per-call initialization overhead. Consider either (1) making the Default requirement explicit in the generated bounds / emitting a clearer compile-time error when it’s not satisfied, or (2) reusing an existing full-output buffer (if one exists in the runtime scaffolding) to avoid repeated initialization.
There was a problem hiding this comment.
this is already explicit, all mesages are default constructible WTH?
| #task_instance.process(&ctx, &mut __cu_full_output) | ||
| }; | ||
| cu29::cutask::project_output(&__cu_full_output, cumsg_output); |
There was a problem hiding this comment.
The generated code now unconditionally constructs a fresh __cu_full_output via Default::default(). This introduces an implicit (and potentially macro-error-message-unfriendly) requirement that the task’s full Output type is Default, and it also adds per-call initialization overhead. Consider either (1) making the Default requirement explicit in the generated bounds / emitting a clearer compile-time error when it’s not satisfied, or (2) reusing an existing full-output buffer (if one exists in the runtime scaffolding) to avoid repeated initialization.
|
ok this is a little bit fucked up this entire thing and can introduce some overhead. The problem is that you cannot ignore any output from a multi output task because it is constructed from the connections. if you don't connect them all the output is only a subset of the actual one. I am thinking about helping that with a proper declaration of outputs or maybe just "not connected" entry in the task. |
|
I completely reoverhalled this, the direction was BAD. no more overhead and added the notiong of |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3b58d7491e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Summary
The proc macro could not accomodate on multi output tasks a partial subscription to the set of output messages.
Related issues
Changes
Ask the user to confirm all the unused connection with a line like:
( src: "src", dst: "__nc__", msg: "bool", ),Testing
just fmtjust lintjust testjust std-ci(if std/runtime paths are impacted)just nostd-ci(if embedded/no_std paths are impacted)pro-tip:
justwith no parameters in the root defaults tojust fmt,just lint, andjust test.Checklist
Breaking changes (if any)
Additional context