enhancement: Unions in DSLX language
- Dominant language
- C++
- Stars
- 1.9k
- Forks
- 283
- Avg merge
- 2d 10h
- Merged PRs (30d)
- 135
Description
# Unions in DSLX language
While working on DSLX designs, we found that adding unions could simplify the description of some design patterns, that currently require more elaborate implementation.
We found that having unions would help as while working on a design that included a packet decoding pipeline. The Proc used in our design received information on its input, and dispatched the packets to dedicated processing blocks depending on the information from the headers available in the input stream. The processing blocks handled the packet properly and returned processed data to the next steps of the pipeline.

To handle this use case, we found C tagged union packet useful. We packed the data into a single structure, with an enum that allowed us to decode the block correctly in the following stages of the pipeline.
Due to a lack of union support, we created dedicated functions that can reinterpret the bit vector underneath. Unions could simplify this code.
For the mentioned case, the Dispatcher Proc without unions could look like this:
```rust
enum PacketType: u2 {
A = 0,
B = 1
}
struct Packet {
p_type: PacketType,
payload: bits[128],
}
struct PacketA {
id: u32,
base: s32,
pow: s32
}
struct PacketB {
id: u32,
var: u32,
a: u32,
b: u32,
}
fn convert_to_packetA(payload: bits[128]) -> PacketA {
let id = payload[0:32] as u32;
let base = payload[32:64] as s32;
let pow = payload[64:96] as s32;
PacketA {id, base, pow}
}
fn convert_to_packetB(payload: bits[128]) -> PacketB {
let id = payload[0:32];
let var = payload[32:64];
let a = payload[64:96];
let b = payload[96:128];
PacketB {id, var, a, b}
}
proc PacketDispatcher {
input_r: chan in;
outputA_s: chan out;
outputB_s: chan out;
init {}
config(
input_r: chan in,
outputA_s: chan out,
outputB_s: chan out,
) {
(input_r, outputA_s, outputB_s)
}
next(tok: token, state:()) {
let (tok, packet) = recv(tok, input_r);
let packetA = convert_to_packetA(packet.payload);
let tok = send_if(tok, outputA_s, packet.p_type == PacketType::A, packetA);
let packetB = convert_to_packetB(packet.payload);
let tok = send_if(tok, outputB_s, packet.p_type == PacketType::B, packetA);
}
}
```
The ability to use unions could simplify the code to:
```rust
enum PacketType: u2 {
A = 0,
B = 1
}
struct PacketA {
id: u32,
base: s32,
pow: s32
}
struct PacketB {
id: u32,
var: u32,
a: u32,
b: u32,
}
union PacketVariant {
variant_a: PacketA,
variant_b: PacketB
}
struct Packet {
type: PacketType,
payload: PacketVariant,
}
proc PacketDispatcher {
input_r: chan in;
outputA_s: chan out;
outputB_s: chan out;
init {}
config(
input_r: chan in,
outputA_s: chan out,
outputB_s: chan out,
) {
(input_r, outputA_s, outputB_s)
}
next(tok: token, state:()) {
let (tok, packet) = recv(tok, input_r);
let tok = send_if(tok, outputA_s, packet.p_type == PacketType::A, packet.variant_a);
let tok = send_if(tok, outputB_s, packet.p_type == PacketType::B, packet.variant_b);
}
}
```
Unions can be especially useful when we need to send different types of data through channels. They can also be convenient when we want to store information as part of a state, where each state requires a different type of data. Currently, these use cases can be handled by manually casting the data, but adding unions could make it easier to express these concepts
Contributor guide
Assessment
This issue has not been assessed yet.