rust-lang / rust-lang/rust

[ICE]: Tried making buffers use #![feature(generic_const_exprs)] for A+B sized arrays

Open
#160,242 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

C-bug F-generic_const_exprs I-ICE needs-triage T-compiler
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

Code
[package]
name = "lcp"
version = "0.1.0"
edition = "2024"

[dependencies]
async-channel = "2.5.0"
codec = "0.0.9"
embassy-futures = "0.1.2"
embassy-time = "0.5.1"
embedded-io-async = "0.7.0"
heapless = "0.9.3"
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4

[[package]]
name = "async-channel"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2"
dependencies = [
 "concurrent-queue",
 "event-listener-strategy",
 "futures-core",
 "pin-project-lite",
]

[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"

[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"

[[package]]
name = "codec"
version = "0.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a947cf08398fd4d78b71a878580a78612f2ac5c3a2a69b63d042d37cf1140a8"
dependencies = [
 "heapless",
]

[[package]]
name = "concurrent-queue"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
dependencies = [
 "crossbeam-utils",
]

[[package]]
name = "critical-section"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"

[[package]]
name = "crossbeam-utils"
version = "0.8.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17"

[[package]]
name = "document-features"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61"
dependencies = [
 "litrs",
]

[[package]]
name = "embassy-futures"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc2d050bdc5c21e0862a89256ed8029ae6c290a93aecefc73084b3002cdebb01"

[[package]]
name = "embassy-time"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "592b0c143ec626e821d4d90da51a2bd91d559d6c442b7c74a47d368c9e23d97a"
dependencies = [
 "cfg-if",
 "critical-section",
 "document-features",
 "embassy-time-driver",
 "embedded-hal 0.2.7",
 "embedded-hal 1.0.0",
 "embedded-hal-async",
 "futures-core",
]

[[package]]
name = "embassy-time-driver"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ee71af1b3a0deaa53eaf2d39252f83504c853646e472400b763060389b9fcc9"
dependencies = [
 "document-features",
]

[[package]]
name = "embedded-hal"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
dependencies = [
 "nb 0.1.3",
 "void",
]

[[package]]
name = "embedded-hal"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"

[[package]]
name = "embedded-hal-async"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884"
dependencies = [
 "embedded-hal 1.0.0",
]

[[package]]
name = "embedded-io"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9eb1aa714776b75c7e67e1da744b81a129b3ff919c8712b5e1b32252c1f07cc7"

[[package]]
name = "embedded-io-async"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2564b9f813c544241430e147d8bc454815ef9ac998878d30cc3055449f7fd4c0"
dependencies = [
 "embedded-io",
]

[[package]]
name = "event-listener"
version = "5.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab"
dependencies = [
 "concurrent-queue",
 "parking",
 "pin-project-lite",
]

[[package]]
name = "event-listener-strategy"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
dependencies = [
 "event-listener",
 "pin-project-lite",
]

[[package]]
name = "futures-core"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7"

[[package]]
name = "hash32"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
dependencies = [
 "byteorder",
]

[[package]]
name = "heapless"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25ba4bd83f9415b58b4ed8dc5714c76e626a105be4646c02630ad730ad3b5aa4"
dependencies = [
 "hash32",
 "stable_deref_trait",
]

[[package]]
name = "lcp"
version = "0.1.0"
dependencies = [
 "async-channel",
 "codec",
 "embassy-futures",
 "embassy-time",
 "embedded-io-async",
 "heapless",
]

[[package]]
name = "litrs"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"

[[package]]
name = "nb"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
dependencies = [
 "nb 1.1.0",
]

[[package]]
name = "nb"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"

[[package]]
name = "parking"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"

[[package]]
name = "pin-project-lite"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"

[[package]]
name = "stable_deref_trait"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"

[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"

#![feature(generic_const_exprs)]
use core::cmp::min;

use async_channel::{Receiver, Sender, bounded};
use codec::{Codec, DecodeError, EncodeBuffer, EncodeError, SizeContext, StaticBuffer};
use embassy_futures::select::{Either, select};
use embassy_time::{Duration, Instant, Timer};
use embedded_io_async::{Read, Write};
use heapless::{Deque, LenType, Vec};

use codec::{DecodeBuffer};

pub trait GetStaticView<'a, View: 'a> {
    fn get_static_view(&'a mut self) -> View;
}

impl<'a, const N: usize> GetStaticView<'a, StaticDequeView<'a, N>> for Deque<u8, N> {
    fn get_static_view(&'a mut self) -> StaticDequeView<'a, N> {
        StaticDequeView {
            dq: self,
            pointer: 0,
        }
    }
}

pub struct StaticDequeView<'a, const N: usize> {
    dq: &'a mut Deque<u8, N>,
    pointer: usize,
}

impl<'a, const N: usize> StaticDequeView<'a, N> {
    pub fn new(source: &'a mut Deque<u8, N>) -> StaticDequeView<'a, N> {
        StaticDequeView {
            dq: source,
            pointer: 0,
        }
    }
    /// Commit the read bytes permanently.
    pub fn commit(&mut self) {
        for _ in 0..self.pointer {
            self.dq.pop_front();
        }
    }
}

impl<'a, const N: usize> DecodeBuffer for StaticDequeView<'a, N> {
    fn read(&mut self) -> Result<u8, codec::DecodeError> {
        let data = self.dq.get(self.pointer).copied();
        self.pointer += 1;
        data.ok_or(DecodeError::Incomplete)
    }
    fn read_slice(&mut self, buf: &mut [u8]) -> Result<(), DecodeError> {
        if self.dq.len() - self.pointer < buf.len() {
            return Err(DecodeError::Incomplete);
        }

        for item in buf.iter_mut() {
            // SAFETY: Checked len at start of function
            *item = unsafe { *self.dq.get_unchecked(self.pointer) };
            self.pointer += 1;
        }

        return Ok(());
    }
    fn get_read_len(&self) -> usize {
        0
    }
}

const MAGIC_ID: u8 = 0x97;
const PACKET_RETRY_TIME: u64 = 5; // milliseconds


// TODO: configuration parameters should be provided to user
// TODO: buffer sizes should be pre-computed... somehow.

enum FillState {
    Empty,
    Partial,
    Full,
}
trait GetFillState {
    fn fill_state(&self) -> FillState;
}
impl<T, const S: usize, L: LenType> GetFillState for Vec<T, S, L> {
    fn fill_state(&self) -> FillState {
        match (self.is_empty(), self.is_full()) {
            (true,false) => FillState::Empty,
            (false,true) => FillState::Full,
            (false, false) => FillState::Partial,
            (true,true) => panic!("Can't be empty and full"),
        }
    }
}

#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Clone)]
pub struct SequenceId {
    raw: u8,
}
impl SequenceId {
    pub fn reset(&mut self) {
        self.raw = 1;
    }
    pub fn init() -> SequenceId {
        SequenceId { raw: 1 }
    }
    pub fn none() -> SequenceId {
        SequenceId { raw: 0 }
    }
    pub fn is_none(self) -> bool {self.raw == 0}
    pub fn is_some(self) -> bool {self.raw != 0}
    pub fn get_next(&mut self) -> SequenceId {
        // TODO: Verify branchless is faster.
        // branchless programming for
        // if none, stay none, else wrapping add 1->255->1
        let original = self.raw;
        let raw = self.raw;
        let is_some: bool = raw != 0;
        let new = raw.wrapping_add(1);
        let new = new + ((new == 0) as u8);
        
        // if started none, stay none
        self.raw = new * (is_some as u8);
        
        SequenceId { raw: original }
    }
    fn get_n_away(mut self, n: u8) -> SequenceId {
        for _ in 0..n {
            self.get_next();
        }
        self
    }
    pub fn is_within(self, last_received: SequenceId, within: u8) -> bool {
        /*
        let positions_ahead = SequenceId::distance(last_received, self);
        println!("A->B: {positions_ahead}");
        positions_ahead <= within*/

        let end = last_received.get_n_away(within);

        ((self.raw <= end.raw) &
        (self.raw >= last_received.raw) &
        (last_received.raw <= end.raw))
        |
        ((self.raw >= end.raw) &
        (self.raw >= last_received.raw) &
        (last_received.raw > end.raw))
        |
        ((self.raw <= end.raw) &
        (self.raw <= last_received.raw) &
        (last_received.raw > end.raw))
    }
}

impl Codec<()> for SequenceId {
    fn encode(&self, buf: &mut impl EncodeBuffer, _: ()) -> Result<(), EncodeError> {
        self.raw.encode(buf, ())
    }
    fn decode(buf: &mut impl codec::DecodeBuffer, _: ()) -> Result<Self, DecodeError> {
        Ok(Self {raw: u8::decode(buf, ())?})
    }
}


// TODO: Separate local max payload from transports max payload

pub fn build_transport<Reader: Read, Writer: Write, const MAX_PAYLOAD: usize>(
    reader: Reader,
    writer: Writer,
) -> (
    TransportReader<Reader, MAX_PAYLOAD>,
    TransportWriter<Writer, MAX_PAYLOAD>,
    Sender<Vec<u8, MAX_PAYLOAD, u8>>,
    Receiver<Vec<u8, MAX_PAYLOAD, u8>>,
)
where [(); HEADER_SIZE+MAX_PAYLOAD]: {
    // TODO: Does received or ack channels need to be larger to prevent starving the
    // serial read connection.
    let (received, rx) = bounded(3);
    let (tx, to_send) = bounded(3);
    let (ack_tx, ack_rx) = bounded(3);
    let tr = TransportReader::new(reader, ack_tx, received);
    let tw = TransportWriter::new(writer, to_send, ack_rx);
    (tr, tw, tx, rx)
}

const RECV_Q_SIZE: usize = 5;
const RESET_INVALID_BYTE_COUNT: Option<usize> = None;
const RESET_OUT_OF_RANGE_COUNT: usize = 100;

struct RecvErrorProcessor {
    byte_errors: usize,
    last_process_good: bool,
    out_of_range_errs: usize,
}
enum RecvErrorCmd {
    None,
    Reset,
    Request,
}
impl RecvErrorProcessor {
    pub fn new() -> Self {
        Self { byte_errors: 0, last_process_good: true, out_of_range_errs: 0 }
    }
    pub fn byte_error(&mut self) -> RecvErrorCmd {
        self.byte_errors += 1;

        if self.last_process_good {
            self.last_process_good = false;
            return RecvErrorCmd::Request;
        }

        if let Some(count) = RESET_INVALID_BYTE_COUNT {
            if self.byte_errors >= count {
                return RecvErrorCmd::Reset;
            }
        }

        RecvErrorCmd::None
    }
    pub fn good_packet(&mut self) {
        self.last_process_good = true;
        self.byte_errors = 0;
    }
    pub fn good_packet_index(&mut self) {
        self.out_of_range_errs = 0;
    }
    pub fn out_of_range(&mut self) -> RecvErrorCmd {
        self.out_of_range_errs += 1;
        if self.out_of_range_errs >= RESET_OUT_OF_RANGE_COUNT {
            RecvErrorCmd::Reset
        } else {
            RecvErrorCmd::None
        }
    }
    pub fn reset(&mut self) {
        *self = RecvErrorProcessor::new();
    }
}

enum RecvQCmd {
    Ack{acked: SequenceId, to_ack: SequenceId},
    SeqReq(SequenceId),
    None,
}
struct ReceiveQueue<const DATA_SIZE: usize> {
    next_seq_id: SequenceId,
    q: Vec<Packet<DATA_SIZE>, RECV_Q_SIZE>
}

impl<const DATA_SIZE: usize> ReceiveQueue<DATA_SIZE> {
    fn new() -> Self {
        Self { next_seq_id: SequenceId::init(), q: Vec::new() }
    }
    fn reset(&mut self) {
        self.q.clear();
        self.next_seq_id = SequenceId::init();
    }
    fn process_recv(&mut self, pkt: Packet<DATA_SIZE>) -> RecvQCmd {
        if pkt.sequence_id
            .is_within(self.next_seq_id, self.q.capacity() as u8) {
            let ack_id = pkt.ack_id;
            let seq_id = pkt.sequence_id;
            self.q.push(pkt).unwrap();
            RecvQCmd::Ack { acked: ack_id, to_ack: seq_id }
        } else if self.next_seq_id
            .is_within(pkt.sequence_id, self.q.capacity() as u8) {
            // Packet already received recently. The retransmit went out before
            // we could acknowledge the first transmit. Ignore
            RecvQCmd::None
        }else {
            // Request next sequence if we started getting values out of range.
            RecvQCmd::SeqReq(self.next_seq_id)
        }
    }
    fn get_ready_data(&mut self) -> Option<Packet<DATA_SIZE>> {
        for index in 0..self.q.len() {
            if self.q.get(index).is_some_and(|pkt| pkt.sequence_id == self.next_seq_id) {
                self.next_seq_id.get_next();
                return Some(self.q.swap_remove(index))
            }
        }

        None
    }
}

#[derive(PartialEq, Eq)]
enum RxData {
    Ack{acked: SequenceId, to_ack: SequenceId},
    WeRequest(SequenceId),
    TheyRequested(SequenceId),
    WeReset,
    TheyReset,
    Started,
}
pub struct TransportReader<Reader: Read, const DATA_SIZE: usize> {
    reader: Reader,
    ack_tx: Sender<RxData>,
    received: Sender<Vec<u8, DATA_SIZE, u8>>,
    recv_q: ReceiveQueue<DATA_SIZE>,
    err_proc: RecvErrorProcessor,
}
impl<Reader: Read, const DATA_SIZE: usize> TransportReader<Reader, DATA_SIZE> {
    fn new(
        reader: Reader,
        ack_tx: Sender<RxData>,
        received: Sender<Vec<u8, DATA_SIZE, u8>>
    ) -> TransportReader<Reader, DATA_SIZE> {
        TransportReader {
            reader, ack_tx, received, recv_q: ReceiveQueue::new(),
            err_proc: RecvErrorProcessor::new()
        }
    }
    async fn fill_buf<const SIZE: usize>(&mut self, buf: &mut Deque<u8, SIZE>) {
        // TODO: pre-calculate buffer size
        let max_fill = buf.storage_capacity() - buf.len();
        const WORKING_BUF_SIZE: usize = 50;
        let mut working_buf = [0u8; WORKING_BUF_SIZE];
        if let Ok(read) = self.reader.read(&mut working_buf[0..max_fill]).await {
            for item in working_buf.iter().take(read) {
                buf.push_back(*item);
            }
        }
    }
    fn reset(&mut self) {
        self.recv_q.reset();
        self.err_proc.reset();
    }
    async fn process_pkt(&mut self, packet: Packet<DATA_SIZE>) {
        match packet.cmd {
            PacketCmd::Reset => {
                self.reset();
                self.ack_tx.send(RxData::TheyReset).await.unwrap();
            },
            PacketCmd::Start => {
                self.reset();
                self.ack_tx.send(RxData::Started).await.unwrap();
            }
            PacketCmd::Request => {
                self.ack_tx
                    .send(RxData::TheyRequested(packet.sequence_id))
                    .await
                    .unwrap();
            }
            PacketCmd::None => {
                // Only use ack_id, since we don't ack none packets
                self.ack_tx
                    .send(RxData::Ack { acked: packet.ack_id, to_ack: SequenceId::none() })
                    .await
                    .unwrap();
            }
            PacketCmd::Data => {
                match self.recv_q.process_recv(packet) {
                    RecvQCmd::SeqReq(seq_id) => {
                        if let RecvErrorCmd::Reset = self.err_proc.out_of_range() {
                            self.reset();
                            self.ack_tx.send(RxData::WeReset).await.unwrap();
                        }
                        self.ack_tx
                        .send(RxData::WeRequest(seq_id))
                        .await
                        .unwrap();
                    }
                    RecvQCmd::Ack { acked, to_ack } => {
                        self.err_proc.good_packet_index();
                        self.ack_tx
                            .send(RxData::Ack { acked, to_ack })
                            .await
                            .unwrap();
                    }
                    RecvQCmd::None => {
                        // No action necessary
                    }
                }
                while let Some(pkt) = self.recv_q.get_ready_data() {
                    self.received.send(pkt.data).await.unwrap();
                }
            }
        }
    }
    pub async fn run(&mut self) -> ! 
    where [(); HEADER_SIZE + DATA_SIZE]: {
        let mut buf = Deque::<_, { HEADER_SIZE + DATA_SIZE }>::new();
        loop {
            self.fill_buf(&mut buf).await;
            let mut static_view = buf.get_static_view();
            match Packet::decode(&mut static_view, ()) {
                Ok(packet) => {
                    self.err_proc.good_packet();
                    static_view.commit();
                    self.process_pkt(packet).await;
                },
                Err(DecodeError::Incomplete) => continue,
                Err(DecodeError::Invalid) => {
                    match self.err_proc.byte_error() {
                        RecvErrorCmd::None => (),
                        RecvErrorCmd::Reset => {
                            self.reset();
                            self.ack_tx.send(RxData::WeReset).await.unwrap();
                        },
                        RecvErrorCmd::Request => {
                            self.ack_tx.send(RxData::WeRequest(self.recv_q.next_seq_id)).await.unwrap();
                        },
                    }
                    buf.pop_front();
                    continue;
                }
            };
        }
    }
}


const SEND_Q_SIZE: usize = 5;
struct WriteQueue<const DATA_SIZE: usize> {
    q: Vec<PacketInfo<DATA_SIZE>, SEND_Q_SIZE>,
}
impl<const DATA_SIZE: usize> WriteQueue<DATA_SIZE> {
    fn new() -> Self {
        Self { q: Vec::new() }
    }
    fn clear(&mut self) {
        self.q.clear();
    }
    fn fill_state(&self) -> FillState {
        self.q.fill_state()
    }
    fn ack(&mut self, ack_id: SequenceId) {
        // Remove data which was confirmed as sent.
        self.q.retain(|pkt| pkt.pkt.sequence_id != ack_id);
    }
    fn get_mut(&mut self, index: usize) -> Option<&mut PacketInfo<DATA_SIZE>> {
        self.q.get_mut(index)
    }
    fn get_next_to_send(&mut self) -> Option<usize> {
        self.q.iter_mut().enumerate().reduce(|a, b|{
            if a.1.retry < b.1.retry {
                a
            } else {
                b
            }
        }).map(|pair|pair.0)
    }
    fn add(&mut self, to_add: PacketInfo<DATA_SIZE>) -> usize {
        self.q.push(to_add).expect("Fill state checked before calling");
        self.q.len() - 1 // index of last element
    }
    fn index_from_id(&self, seq_id: SequenceId) -> Option<usize> {
        for (index, item) in self.q.iter().enumerate() {
            if item.pkt.sequence_id == seq_id {return Some(index)}
        }

        None
    }
}

#[derive(Debug)]
struct PacketInfo<const DATA_SIZE: usize> {
    pkt: Packet<DATA_SIZE>,
    retry: Instant
}
pub struct TransportWriter<Writer: Write, const DATA_SIZE: usize> {
    writer: Writer,
    to_send: Receiver<Vec<u8, DATA_SIZE, u8>>,
    ack_rx: Receiver<RxData>,
    write_buf: Deque<u8, 100>,
    tx_q: WriteQueue<DATA_SIZE>,
    to_ack: Vec<SequenceId, 10>,
    current_id: SequenceId,
}

impl<Writer: Write, const DATA_SIZE: usize> TransportWriter<Writer, DATA_SIZE> 
where [(); HEADER_SIZE+DATA_SIZE]: {
    fn new(
        writer: Writer,
        to_send: Receiver<Vec<u8, DATA_SIZE, u8>>,
        ack_rx: Receiver<RxData>
    ) -> TransportWriter<Writer, DATA_SIZE> {
        TransportWriter {
            writer, write_buf: Deque::new(),
            to_send, ack_rx,
            tx_q: WriteQueue::new(),
            to_ack: Vec::new(),
            current_id: SequenceId::init()
        }
    }
    async fn write_index(&mut self, index: usize) {
        let mut working_buf = [0u8; HEADER_SIZE+DATA_SIZE];
        let mut static_buf = StaticBuffer::new(&mut working_buf);
        let pkt = &self.tx_q.get_mut(index).expect("Can only call write_tx with valid packet").pkt;
        println!("Write index: {index} {pkt:?}");
        pkt.encode(&mut static_buf, ())
            .expect("Must have enough space, and be valid packet");
        let len = static_buf.get_encoded_len();
        self.writer
            .write_all(&mut working_buf[0..len])
            .await
            .expect("Must be writeable")
    }
    async fn write(&mut self, pkt: &Packet<DATA_SIZE>) {
        let mut working_buf = [0u8; HEADER_SIZE+DATA_SIZE];
        let mut static_buf = StaticBuffer::new(&mut working_buf);
        pkt.encode(&mut static_buf, ())
            .expect("Must have enough space, and be valid packet");
        let len = static_buf.get_encoded_len();
        self.writer
            .write_all(&mut working_buf[0..len])
            .await
            .expect("Must be writeable");
        println!("Write: {:?}", pkt);
    }
    // Should only be called with self.rx_pkt as None
    async fn process_rx_data(&mut self, mut pkt: RxData) {
        if self.to_ack.is_full() {panic!("Must not be full before process rx is called")}
        match &mut pkt {
            RxData::Ack { acked, to_ack } => {
                self.tx_q.ack(*acked);
                if to_ack.is_some() {
                    self.to_ack.push(*to_ack).expect("Already confirmed size");
                }
            }
            RxData::WeRequest(id) => {
                println!("We didn't receive \'{:?}\' request to send again", id);
                self.write(&Packet::request(*id)).await
            }
            RxData::TheyRequested(id) => {
                println!("They didn't receive \'{:?}\' we are sending again", id);
                if let Some(index) = self.tx_q.index_from_id(*id) {
                    // Resend if reported lost
                    self.write_index(index).await
                } else if *id == self.current_id {
                    // They want the next ID we don't have yet
                    // ignore
                }else {
                    println!("current_id: {:?}", self.current_id);
                    todo!("Can consistency be recovered?");
                }
            }
            RxData::WeReset => {
                println!("Reset Write");
                self.current_id.reset();
                self.to_ack.clear();
                self.tx_q.clear();
                self.process_reset();
            }
            RxData::Started => {
                // Ignore. Processed in WeReset when needed
            }
            RxData::TheyReset => {
                println!("Reset Write");
                self.current_id.reset();
                self.to_ack.clear();
                self.tx_q.clear();
                self.write(&Packet::start()).await;
            }
        }
    }
    async fn process_reset(&mut self) {
        self.write(&Packet::reset()).await;
        loop {
            match select(
                self.ack_rx.recv(),
                Timer::after(Duration::from_millis(100))
            ).await {
                Either::First(pkt) => {
                    let pkt = pkt.expect("Communication must be maintained");
                    if let RxData::TheyReset = pkt {
                        // If we are both in the reset state, ack their reset
                        self.write(&Packet::start()).await
                    }
                    if let RxData::Started = pkt {
                        // Reset Acked, we are now synced
                        return;
                    }
                }
                Either::Second(_) => {
                    self.write(&Packet::reset()).await;
                }
            }
        }
        while self.ack_rx.recv().await.unwrap() != RxData::Started {
            // Wait for packet with Started. Discard everything else.
        }
    }
    fn has_sized_room(&self) -> bool {
        self.write_buf.capacity() - self.write_buf.len() >= DATA_SIZE
    }
    fn has_sendable_data(&self) -> bool {
        ! self.write_buf.is_empty()
    }
    fn create_packet(&mut self) -> usize {
        let mut data = Vec::new();
        for _ in 0..min(DATA_SIZE, self.write_buf.len()) {
            data.push(
                self.write_buf
                        .pop_front()
                        .expect("Data len must be within buffer")
            ).expect("Data must fit in packet")
        }

        let ack = match self.to_ack.pop() {
            Some(ack) => ack,
            None => SequenceId::none(),
        };

        let info = PacketInfo {
            pkt: Packet {
                sequence_id: self.current_id.get_next(),
                ack_id: ack,
                data, cmd: PacketCmd::Data
            }, retry: Self::get_retry_instant()
        };
        self.tx_q.add(info)
    }
    fn get_retry_instant() -> Instant {
        Instant::now() + Duration::from_millis(PACKET_RETRY_TIME)
    }
    pub async fn run(mut self) -> ! {
        // TODO: Does rx packet get taken into account immediately.
        // Sequence invalidated, must start from scratch
        // TODO: Make actual state machine to more easily represent
        // and communicate graph of behavior
        self.process_reset().await;
        loop {
            match (
                self.to_ack.fill_state(),
                self.tx_q.fill_state(),
                self.has_sized_room(),
                self.has_sendable_data()
            ) {
                (_, _, false, false) => {
                    panic!("Can't have no room, and also have no data");
                }
                (_, FillState::Empty | FillState::Partial, _, true) => {
                    // Send packet with possible ack as well
                    let new_index = self.create_packet();
                    self.write_index(new_index).await;
                }
                (FillState::Partial | FillState::Full, _, _, false) => {
                    // Send ack when no data ready
                    let pkt = Packet::ack(
                        self.to_ack.pop().expect("Can't be empty")
                    );
                    self.write(&pkt).await;
                }
                (FillState::Empty, FillState::Empty, true, false) => {
                    // wait for something to do
                    match select(self.ack_rx.recv(), self.to_send.recv()).await {
                        Either::First(rx) => {
                            self.process_rx_data(rx.expect("Must maintain communication")).await;
                        },
                        Either::Second(data) => {
                            let data = data.expect("Must maintain communication");
                            self.write_buf.extend(data);
                        },
                    }
                }
                (FillState::Empty, FillState::Partial | FillState::Full, _, _) => {
                    // Wait for packet retransmit or ack_rx
                    let retransmit_index = self.tx_q.get_next_to_send().unwrap();
                    let retransmit = self.tx_q.get_mut(retransmit_index).unwrap();
                    match select(Timer::at(retransmit.retry), self.ack_rx.recv()).await {
                        Either::First(_) => {
                            self.write_index(retransmit_index).await;
                            // TODO: Can't guarantee same index. Fix this
                            self.tx_q.get_mut(retransmit_index).unwrap().retry = Self::get_retry_instant();
                        },
                        Either::Second(rx_data) => {
                            self.process_rx_data(rx_data.expect("Must maintain communication")).await;
                        },
                    }
                }
                (FillState::Partial, FillState::Full, true, true) => {
                    todo!();
                }
                (FillState::Partial, FillState::Full, false, true) => {
                    todo!();
                }
                (FillState::Full, FillState::Full, _, _) => {
                    // TODO: Can we get stuck in this state? How would we get out?
                    // Can't do anything with full buffers besides wait to send again.
                    todo!("Temp");
                    /*Timer::at(pkt.retry).await;
                    self.write_index().await;
                    self.tx_q.as_mut().expect("matched on exists").retry = Self::get_retry_intant();*/
                }
            }
        }
    }
}

/// Packet(SIZE):
///     magic_id(u8): ?
///     cmd(u8)
///     sequence_id(u8)
///     size(u8?)
///     checksum(u16)
///     checksum(u16)
///     data[SIZE]
///

const HEADER_SIZE: usize = 8;
const CHECKSUM_SIZE: usize = 4;

#[derive(Debug)]
pub enum PacketCmd {
    None, // Used for empty ack
    Data,
    Request,
    Reset, // Got out of sync, and can't fix
    Start,
}

impl Codec<()> for PacketCmd {
    fn encode(&self, buf: &mut impl EncodeBuffer, _: ()) -> Result<(), EncodeError> {
        match self {
            Self::None => 0u8.encode(buf, ()),
            Self::Data => 1u8.encode(buf, ()),
            Self::Request => 2u8.encode(buf, ()),
            Self::Reset => 3u8.encode(buf, ()),
            Self::Start => 4u8.encode(buf, ()),
        }
    }
    fn decode(buf: &mut impl codec::DecodeBuffer, _: ()) -> Result<Self, DecodeError> {
        let cmd = u8::decode(buf, ())?;
        match cmd {
            0 => Ok(Self::None),
            1 => Ok(Self::Data),
            2 => Ok(Self::Request),
            3 => Ok(Self::Reset),
            4 => Ok(Self::Start),
            _ => Err(DecodeError::Invalid),
        }
    }
}

#[derive(Debug)]
pub struct Packet<const DATA_SIZE: usize> {
    sequence_id: SequenceId,
    ack_id: SequenceId,
    data: Vec<u8, DATA_SIZE, u8>,
    cmd: PacketCmd,
}

impl<const DATA_SIZE: usize> Packet<DATA_SIZE> {
    fn new(sequence_id: SequenceId, ack_id: SequenceId, raw: &[u8]) -> Result<Packet<DATA_SIZE>, ()> {
        Ok(Packet {
            sequence_id: sequence_id,
            data: Vec::<u8, DATA_SIZE, u8>::from_slice(raw).map_err(|_| ())?,
            cmd: PacketCmd::Data,
            ack_id
        })
    }
    pub fn ack(ack_id: SequenceId) -> Packet<DATA_SIZE> {
        Packet {
            sequence_id: SequenceId::none(),
            data: Vec::from_slice(&[]).expect("Empty slice always succeeds"),
            cmd: PacketCmd::None, ack_id
        }
    }
    pub fn request(sequence_id: SequenceId) -> Packet<DATA_SIZE> {
        Packet { sequence_id, data: Vec::new(), cmd: PacketCmd::Request, ack_id: SequenceId::none() }
    }
    pub fn reset() -> Packet<DATA_SIZE> {
        Packet { sequence_id: SequenceId::none(), data: Vec::new(), cmd: PacketCmd::Reset, ack_id: SequenceId::none() }
    }
    pub fn start() -> Packet<DATA_SIZE> {
        Packet {
            sequence_id: SequenceId::none(), data: Vec::new(),
            cmd: PacketCmd::Start, ack_id: SequenceId::none()
        }
    }
}

impl<const DATA_SIZE: usize> Codec<()> for Packet<DATA_SIZE> {
    fn encode(&self, buf: &mut impl EncodeBuffer, _: ()) -> Result<(), EncodeError> {
        MAGIC_ID.encode(buf, ())?;
        self.cmd.encode(buf, ())?;
        self.sequence_id.encode(buf, ())?;
        self.ack_id.encode(buf, ())?;
        self.data.encode(buf, SizeContext::U8Len)?;
        // TODO: Checksum
        Ok(())
    }
    fn decode(buf: &mut impl codec::DecodeBuffer, _: ()) -> Result<Self, DecodeError> {
        if u8::decode(buf, ())? != MAGIC_ID {return Err(DecodeError::Invalid);}

        let cmd = PacketCmd::decode(buf, ())?;
        let sequence_id = SequenceId::decode(buf, ())?;
        let ack_id = SequenceId::decode(buf, ())?;
        let payload = Vec::<u8, DATA_SIZE, u8>::decode(buf, SizeContext::U8Len)?;
        Ok(Packet {
            sequence_id,
            ack_id,
            data: payload,
            cmd,
        })
    }
}




Meta

rustc --version --verbose:

rustc 1.99.0-nightly (1a833e165 2026-07-29)
binary: rustc
commit-hash: 1a833e16546c2eb012758ddd499964fd8afee29e
commit-date: 2026-07-29
host: x86_64-unknown-linux-gnu
release: 1.99.0-nightly
LLVM version: 22.1.8
Error output
thread 'rustc' (37087) panicked at /rustc-dev/1a833e16546c2eb012758ddd499964fd8afee29e/compiler/rustc_type_ir/src/binder.rs:788:9:
type parameter `/#2` (/#2/2) out of range when instantiating, args=[Writer/#0, DATA_SIZE/#1]
stack backtrace:
   0:     0x7b18730b64d6 - <<std[a31cbe3acb964aad]::sys::backtrace::BacktraceLock>::print::DisplayBacktrace as core[ed5d9bd1b3102e1a]::fmt::Display>::fmt
   1:     0x7b187380ad52 - core[ed5d9bd1b3102e1a]::fmt::write
   2:     0x7b18730cb61c - <std[a31cbe3acb964aad]::sys::stdio::unix::Stderr as core[ed5d9bd1b3102e1a]::io::write::Write>::write_fmt
   3:     0x7b18730880aa - std[a31cbe3acb964aad]::panicking::default_hook::{closure#0}
   4:     0x7b18730a8bb3 - std[a31cbe3acb964aad]::panicking::default_hook
   5:     0x7b187201835b - std[a31cbe3acb964aad]::panicking::update_hook::<alloc[bd6e42b2068c47ab]::boxed::Box<rustc_driver_impl[6783d01fd94ba16f]::install_ice_hook::{closure#1}>>::{closure#0}
   6:     0x7b18730a9052 - std[a31cbe3acb964aad]::panicking::panic_with_hook
   7:     0x7b1873088162 - std[a31cbe3acb964aad]::panicking::panic_handler::{closure#0}
   8:     0x7b187307fd79 - std[a31cbe3acb964aad]::sys::backtrace::__rust_end_short_backtrace::<std[a31cbe3acb964aad]::panicking::panic_handler::{closure#0}, !>
   9:     0x7b1873089b5d - __rustc[1dc8cc5ee6929c6b]::rust_begin_unwind
  10:     0x7b186fe1682c - core[ed5d9bd1b3102e1a]::panicking::panic_fmt
  11:     0x7b1872622e35 - <rustc_type_ir[d2e4ec8bb1fc4f7f]::binder::ArgFolder<rustc_middle[8a4fff7d680873ae]::ty::context::TyCtxt>>::type_param_out_of_range
  12:     0x7b187385265a - <rustc_type_ir[d2e4ec8bb1fc4f7f]::binder::ArgFolder<rustc_middle[8a4fff7d680873ae]::ty::context::TyCtxt> as rustc_type_ir[d2e4ec8bb1fc4f7f]::fold::TypeFolder<rustc_middle[8a4fff7d680873ae]::ty::context::TyCtxt>>::fold_ty
  13:     0x7b18738540f4 - <&rustc_middle[8a4fff7d680873ae]::ty::list::RawList<(), rustc_middle[8a4fff7d680873ae]::ty::generic_args::GenericArg> as rustc_type_ir[d2e4ec8bb1fc4f7f]::fold::TypeFoldable<rustc_middle[8a4fff7d680873ae]::ty::context::TyCtxt>>::fold_with::<rustc_type_ir[d2e4ec8bb1fc4f7f]::binder::ArgFolder<rustc_middle[8a4fff7d680873ae]::ty::context::TyCtxt>>
  14:     0x7b187384f2de - <rustc_type_ir[d2e4ec8bb1fc4f7f]::binder::ArgFolder<rustc_middle[8a4fff7d680873ae]::ty::context::TyCtxt> as rustc_type_ir[d2e4ec8bb1fc4f7f]::fold::TypeFolder<rustc_middle[8a4fff7d680873ae]::ty::context::TyCtxt>>::fold_const
  15:     0x7b187384ff5a - <rustc_type_ir[d2e4ec8bb1fc4f7f]::binder::ArgFolder<rustc_middle[8a4fff7d680873ae]::ty::context::TyCtxt> as rustc_type_ir[d2e4ec8bb1fc4f7f]::fold::TypeFolder<rustc_middle[8a4fff7d680873ae]::ty::context::TyCtxt>>::fold_ty
  16:     0x7b1873c6544f - <&rustc_middle[8a4fff7d680873ae]::ty::list::RawList<(), rustc_middle[8a4fff7d680873ae]::ty::Ty> as rustc_type_ir[d2e4ec8bb1fc4f7f]::fold::TypeFoldable<rustc_middle[8a4fff7d680873ae]::ty::context::TyCtxt>>::fold_with::<rustc_type_ir[d2e4ec8bb1fc4f7f]::binder::ArgFolder<rustc_middle[8a4fff7d680873ae]::ty::context::TyCtxt>>
  17:     0x7b1872ddfd28 - <rustc_type_ir[d2e4ec8bb1fc4f7f]::binder::EarlyBinder<rustc_middle[8a4fff7d680873ae]::ty::context::TyCtxt, rustc_type_ir[d2e4ec8bb1fc4f7f]::binder::Binder<rustc_middle[8a4fff7d680873ae]::ty::context::TyCtxt, rustc_type_ir[d2e4ec8bb1fc4f7f]::ty_kind::CoroutineWitnessTypes<rustc_middle[8a4fff7d680873ae]::ty::context::TyCtxt>>>>::instantiate::<&rustc_middle[8a4fff7d680873ae]::ty::list::RawList<(), rustc_middle[8a4fff7d680873ae]::ty::generic_args::GenericArg>>
  18:     0x7b187382dcf5 - <rustc_trait_selection[aba9d9ed9a7e610d]::traits::select::SelectionContext>::confirm_auto_impl_candidate::{closure#0}
  19:     0x7b1874b18f0b - <rustc_trait_selection[aba9d9ed9a7e610d]::traits::select::SelectionContext>::confirm_candidate
  20:     0x7b1873a93c06 - <rustc_trait_selection[aba9d9ed9a7e610d]::traits::select::SelectionContext>::evaluate_trait_predicate_recursively
  21:     0x7b1873a8c7d1 - <rustc_trait_selection[aba9d9ed9a7e610d]::traits::select::SelectionContext>::evaluate_predicate_recursively
  22:     0x7b1873a93d87 - <rustc_trait_selection[aba9d9ed9a7e610d]::traits::select::SelectionContext>::evaluate_trait_predicate_recursively
  23:     0x7b1873a8c7d1 - <rustc_trait_selection[aba9d9ed9a7e610d]::traits::select::SelectionContext>::evaluate_predicate_recursively
  24:     0x7b1873a93d87 - <rustc_trait_selection[aba9d9ed9a7e610d]::traits::select::SelectionContext>::evaluate_trait_predicate_recursively
  25:     0x7b1873a8c7d1 - <rustc_trait_selection[aba9d9ed9a7e610d]::traits::select::SelectionContext>::evaluate_predicate_recursively
  26:     0x7b1873a93d87 - <rustc_trait_selection[aba9d9ed9a7e610d]::traits::select::SelectionContext>::evaluate_trait_predicate_recursively
  27:     0x7b1873a90d50 - <rustc_trait_selection[aba9d9ed9a7e610d]::traits::select::SelectionContext>::evaluate_root_obligation
  28:     0x7b1873a8eeb5 - rustc_traits[d548f957c06040bd]::evaluate_obligation::evaluate_obligation
  29:     0x7b1873a8e74f - rustc_query_impl[6a9183a651f07735]::query_impl::evaluate_obligation::invoke_provider_fn::__rust_begin_short_backtrace
  30:     0x7b18745c1c06 - rustc_query_impl[6a9183a651f07735]::execution::try_execute_query::<rustc_middle[8a4fff7d680873ae]::query::caches::DefaultCache<rustc_type_ir[d2e4ec8bb1fc4f7f]::canonical::CanonicalQueryInput<rustc_middle[8a4fff7d680873ae]::ty::context::TyCtxt, rustc_middle[8a4fff7d680873ae]::ty::ParamEnvAnd<rustc_middle[8a4fff7d680873ae]::ty::predicate::Predicate>>, rustc_middle[8a4fff7d680873ae]::query::erase::ErasedData<[u8; 2usize]>>, true>
  31:     0x7b18745c0e90 - rustc_query_impl[6a9183a651f07735]::query_impl::evaluate_obligation::execute_query_incr::__rust_end_short_backtrace
  32:     0x7b1874ca5214 - rustc_trait_selection[aba9d9ed9a7e610d]::traits::type_known_to_meet_bound_modulo_regions
  33:     0x7b1873e90830 - rustc_ty_utils[6aa3e9345cd6b371]::common_traits::is_trait_raw
  34:     0x7b1873e903de - rustc_query_impl[6a9183a651f07735]::query_impl::is_freeze_raw::invoke_provider_fn::__rust_begin_short_backtrace
  35:     0x7b1873d18eb6 - rustc_query_impl[6a9183a651f07735]::execution::try_execute_query::<rustc_middle[8a4fff7d680873ae]::query::caches::DefaultCache<rustc_middle[8a4fff7d680873ae]::ty::PseudoCanonicalInput<rustc_middle[8a4fff7d680873ae]::ty::Ty>, rustc_middle[8a4fff7d680873ae]::query::erase::ErasedData<[u8; 1usize]>>, true>
  36:     0x7b1873d1ce27 - rustc_query_impl[6a9183a651f07735]::query_impl::is_freeze_raw::execute_query_incr::__rust_end_short_backtrace
  37:     0x7b1874173d6f - <rustc_middle[8a4fff7d680873ae]::ty::Ty>::is_freeze
  38:     0x7b1874174fc0 - <rustc_mir_transform[20f807b108e81fbd]::ssa::SsaLocals>::new
  39:     0x7b1870dcb554 - <rustc_mir_transform[20f807b108e81fbd]::copy_prop::CopyProp as rustc_mir_transform[20f807b108e81fbd]::pass_manager::MirPass>::run_pass
  40:     0x7b187380912a - rustc_mir_transform[20f807b108e81fbd]::pass_manager::run_passes_inner
  41:     0x7b1873afc112 - rustc_mir_transform[20f807b108e81fbd]::optimized_mir
  42:     0x7b1873afb317 - rustc_query_impl[6a9183a651f07735]::query_impl::optimized_mir::invoke_provider_fn::__rust_begin_short_backtrace
  43:     0x7b1873b6b66f - rustc_query_impl[6a9183a651f07735]::execution::try_execute_query::<rustc_middle[8a4fff7d680873ae]::query::caches::DefIdCache<rustc_middle[8a4fff7d680873ae]::query::erase::ErasedData<[u8; 8usize]>>, true>
  44:     0x7b1873b5bc9d - rustc_query_impl[6a9183a651f07735]::query_impl::optimized_mir::execute_query_incr::__rust_end_short_backtrace
  45:     0x7b1874b2fd7a - <rustc_middle[8a4fff7d680873ae]::ty::context::TyCtxt>::coroutine_layout
  46:     0x7b187464f39e - rustc_ty_utils[6aa3e9345cd6b371]::layout::layout_of_uncached
  47:     0x7b1874647878 - rustc_ty_utils[6aa3e9345cd6b371]::layout::layout_of
  48:     0x7b1874647791 - rustc_query_impl[6a9183a651f07735]::query_impl::layout_of::invoke_provider_fn::__rust_begin_short_backtrace
  49:     0x7b1873d49bbf - rustc_query_impl[6a9183a651f07735]::execution::try_execute_query::<rustc_middle[8a4fff7d680873ae]::query::caches::DefaultCache<rustc_middle[8a4fff7d680873ae]::ty::PseudoCanonicalInput<rustc_middle[8a4fff7d680873ae]::ty::Ty>, rustc_middle[8a4fff7d680873ae]::query::erase::ErasedData<[u8; 16usize]>>, true>
  50:     0x7b1873d49131 - rustc_query_impl[6a9183a651f07735]::query_impl::layout_of::execute_query_incr::__rust_end_short_backtrace
  51:     0x7b1873bfb901 - <rustc_middle[8a4fff7d680873ae]::ty::context::TyCtxt>::par_hir_body_owners::<rustc_interface[fe012aea17673c3c]::passes::run_required_analyses::{closure#2}::{closure#0}>::{closure#0}
  52:     0x7b1873bfa510 - rustc_interface[fe012aea17673c3c]::passes::analysis
  53:     0x7b1874a212d2 - rustc_query_impl[6a9183a651f07735]::execution::try_execute_query::<rustc_middle[8a4fff7d680873ae]::query::caches::SingleCache<rustc_middle[8a4fff7d680873ae]::query::erase::ErasedData<[u8; 0usize]>>, true>
  54:     0x7b1874a20a63 - rustc_query_impl[6a9183a651f07735]::query_impl::analysis::execute_query_incr::__rust_end_short_backtrace
  55:     0x7b18749cfa99 - rustc_interface[fe012aea17673c3c]::interface::run_compiler::<(), rustc_driver_impl[6783d01fd94ba16f]::run_compiler::{closure#0}>::{closure#2}
  56:     0x7b1874a01f64 - std[a31cbe3acb964aad]::sys::backtrace::__rust_begin_short_backtrace::<rustc_interface[fe012aea17673c3c]::util::run_in_thread_with_globals<rustc_interface[fe012aea17673c3c]::util::run_in_thread_pool_with_globals<rustc_interface[fe012aea17673c3c]::interface::run_compiler<(), rustc_driver_impl[6783d01fd94ba16f]::run_compiler::{closure#0}>::{closure#2}, ()>::{closure#0}, ()>::{closure#0}::{closure#0}, ()>
  57:     0x7b1874a01d2d - <std[a31cbe3acb964aad]::thread::lifecycle::spawn_unchecked<rustc_interface[fe012aea17673c3c]::util::run_in_thread_with_globals<rustc_interface[fe012aea17673c3c]::util::run_in_thread_pool_with_globals<rustc_interface[fe012aea17673c3c]::interface::run_compiler<(), rustc_driver_impl[6783d01fd94ba16f]::run_compiler::{closure#0}>::{closure#2}, ()>::{closure#0}, ()>::{closure#0}::{closure#0}, ()>::{closure#1} as core[ed5d9bd1b3102e1a]::ops::function::FnOnce<()>>::call_once::{shim:vtable#0}
  58:     0x7b1874a0e0fa - <std[a31cbe3acb964aad]::sys::thread::unix::Thread>::new::thread_start
  59:     0x7b186de9cb84 - start_thread
                               at ./nptl/pthread_create.c:447:8
  60:     0x7b186df29d6c - clone3
                               at ./misc/../sysdeps/unix/sysv/linux/x86_64/clone3.S:78:0
  61:                0x0 - <unknown>

error: the compiler unexpectedly panicked. This is a bug

note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md

note: please make sure that you have updated to the latest nightly

note: please attach the file at `/home/owner/repos/compiler_error/lcp/rustc-ice-2026-07-31T01_24_04-37084.txt` to your bug report

note: rustc 1.99.0-nightly (1a833e165 2026-07-29) running on x86_64-unknown-linux-gnu

note: compiler flags: --crate-type lib -C embed-bitcode=no -C debuginfo=2 -C incremental=[REDACTED]

note: some of the compiler flags provided by cargo are hidden

query stack during panic:
#0 [evaluate_obligation] evaluating trait selection obligation `{async fn body of protocol::<impl at src/protocol.rs:472:1: 473:35>::process_reset()}: core::marker::Freeze`
#1 [is_freeze_raw] computing whether `{async fn body of protocol::<impl at src/protocol.rs:472:1: 473:35>::process_reset()}` is freeze
... and 3 other queries... use `env RUST_BACKTRACE=1` to see the full query stack
warning: `lcp` (lib) generated 2 warnings
error: could not compile `lcp` (lib); 2 warnings emitted

Caused by:
  process didn't exit successfully: `/home/owner/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustc --crate-name lcp --edition=2024 src/lib.rs --error-format=json --json=diagnostic-rendered-ansi,artifacts,future-incompat --diagnostic-width=124 --crate-type lib --emit=dep-info,metadata,link -C embed-bitcode=no -C debuginfo=2 --check-cfg 'cfg(docsrs,test)' --check-cfg 'cfg(feature, values())' -C metadata=f215ee653b74a792 -C extra-filename=-ab80402c8131bde7 --out-dir /home/owner/repos/compiler_error/lcp/target/debug/deps -C incremental=/home/owner/repos/compiler_error/lcp/target/debug/incremental -L dependency=/home/owner/repos/compiler_error/lcp/target/debug/deps --extern async_channel=/home/owner/repos/compiler_error/lcp/target/debug/deps/libasync_channel-c6554ffbd9187735.rmeta --extern codec=/home/owner/repos/compiler_error/lcp/target/debug/deps/libcodec-11c76b29dba3f814.rmeta --extern embassy_futures=/home/owner/repos/compiler_error/lcp/target/debug/deps/libembassy_futures-3dcc6f905aaa095b.rmeta --extern embassy_time=/home/owner/repos/compiler_error/lcp/target/debug/deps/libembassy_time-e7290c3990b23b5e.rmeta --extern embedded_io_async=/home/owner/repos/compiler_error/lcp/target/debug/deps/libembedded_io_async-ec87c78bb722456f.rmeta --extern heapless=/home/owner/repos/compiler_error/lcp/target/debug/deps/libheapless-59f5e7b6bdbb98f9.rmeta` (exit status: 101)
Backtrace

thread 'rustc' (37269) panicked at /rustc-dev/1a833e16546c2eb012758ddd499964fd8afee29e/compiler/rustc_type_ir/src/binder.rs:788:9:
type parameter `/#2` (/#2/2) out of range when instantiating, args=[Writer/#0, DATA_SIZE/#1]
stack backtrace:
   0: __rustc::rust_begin_unwind
   1: core::panicking::panic_fmt
   2: <rustc_type_ir::binder::ArgFolder<rustc_middle::ty::context::TyCtxt>>::type_param_out_of_range
   3: <rustc_type_ir::binder::ArgFolder<rustc_middle::ty::context::TyCtxt> as rustc_type_ir::fold::TypeFolder<rustc_middle::ty::context::TyCtxt>>::fold_ty
   4: <&rustc_middle::ty::list::RawList<(), rustc_middle::ty::generic_args::GenericArg> as rustc_type_ir::fold::TypeFoldable<rustc_middle::ty::context::TyCtxt>>::fold_with::<rustc_type_ir::binder::ArgFolder<rustc_middle::ty::context::TyCtxt>>
   5: <rustc_type_ir::binder::ArgFolder<rustc_middle::ty::context::TyCtxt> as rustc_type_ir::fold::TypeFolder<rustc_middle::ty::context::TyCtxt>>::fold_const
   6: <rustc_type_ir::binder::ArgFolder<rustc_middle::ty::context::TyCtxt> as rustc_type_ir::fold::TypeFolder<rustc_middle::ty::context::TyCtxt>>::fold_ty
   7: <&rustc_middle::ty::list::RawList<(), rustc_middle::ty::Ty> as rustc_type_ir::fold::TypeFoldable<rustc_middle::ty::context::TyCtxt>>::fold_with::<rustc_type_ir::binder::ArgFolder<rustc_middle::ty::context::TyCtxt>>
   8: <rustc_type_ir::binder::EarlyBinder<rustc_middle::ty::context::TyCtxt, rustc_type_ir::binder::Binder<rustc_middle::ty::context::TyCtxt, rustc_type_ir::ty_kind::CoroutineWitnessTypes<rustc_middle::ty::context::TyCtxt>>>>::instantiate::<&rustc_middle::ty::list::RawList<(), rustc_middle::ty::generic_args::GenericArg>>
   9: <rustc_trait_selection::traits::select::SelectionContext>::confirm_auto_impl_candidate::{closure#0}
  10: <rustc_trait_selection::traits::select::SelectionContext>::confirm_candidate
  11: <rustc_trait_selection::traits::select::SelectionContext>::evaluate_trait_predicate_recursively
  12: <rustc_trait_selection::traits::select::SelectionContext>::evaluate_predicate_recursively
  13: <rustc_trait_selection::traits::select::SelectionContext>::evaluate_trait_predicate_recursively
  14: <rustc_trait_selection::traits::select::SelectionContext>::evaluate_predicate_recursively
  15: <rustc_trait_selection::traits::select::SelectionContext>::evaluate_trait_predicate_recursively
  16: <rustc_trait_selection::traits::select::SelectionContext>::evaluate_predicate_recursively
  17: <rustc_trait_selection::traits::select::SelectionContext>::evaluate_trait_predicate_recursively
  18: <rustc_trait_selection::traits::select::SelectionContext>::evaluate_root_obligation
  19: rustc_traits::evaluate_obligation::evaluate_obligation
      [... omitted 1 frame ...]
  20: rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions
  21: rustc_ty_utils::common_traits::is_trait_raw
      [... omitted 1 frame ...]
  22: <rustc_middle::ty::Ty>::is_freeze
  23: <rustc_mir_transform::ssa::SsaLocals>::new
  24: <rustc_mir_transform::copy_prop::CopyProp as rustc_mir_transform::pass_manager::MirPass>::run_pass
  25: rustc_mir_transform::pass_manager::run_passes_inner
  26: rustc_mir_transform::optimized_mir
      [... omitted 1 frame ...]
  27: <rustc_middle::ty::context::TyCtxt>::coroutine_layout
  28: rustc_ty_utils::layout::layout_of_uncached
  29: rustc_ty_utils::layout::layout_of
      [... omitted 1 frame ...]
  30: <rustc_middle::ty::context::TyCtxt>::par_hir_body_owners::<rustc_interface::passes::run_required_analyses::{closure#2}::{closure#0}>::{closure#0}
  31: rustc_interface::passes::analysis
  32: rustc_query_impl::execution::try_execute_query::<rustc_middle::query::caches::SingleCache<rustc_middle::query::erase::ErasedData<[u8; 0]>>, true>
  33: rustc_interface::interface::run_compiler::<(), rustc_driver_impl::run_compiler::{closure#0}>::{closure#2}
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

error: the compiler unexpectedly panicked. This is a bug

note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md

note: please make sure that you have updated to the latest nightly

note: please attach the file at `/home/owner/repos/compiler_error/lcp/rustc-ice-2026-07-31T01_25_30-37267.txt` to your bug report

note: rustc 1.99.0-nightly (1a833e165 2026-07-29) running on x86_64-unknown-linux-gnu

note: compiler flags: --crate-type lib -C embed-bitcode=no -C debuginfo=2 -C incremental=[REDACTED]

note: some of the compiler flags provided by cargo are hidden

query stack during panic:
#0 [evaluate_obligation] evaluating trait selection obligation `{async fn body of protocol::<impl at src/protocol.rs:472:1: 473:35>::process_reset()}: core::marker::Freeze`
#1 [is_freeze_raw] computing whether `{async fn body of protocol::<impl at src/protocol.rs:472:1: 473:35>::process_reset()}` is freeze
#2 [optimized_mir] optimizing MIR for `protocol::<impl at src/protocol.rs:472:1: 473:35>::process_rx_data::{closure#0}`
#3 [layout_of] computing layout of `{async fn body of protocol::<impl at src/protocol.rs:472:1: 473:35>::process_rx_data()}`
#4 [analysis] running analysis passes on crate `lcp`
end of query stack
warning: `lcp` (lib) generated 2 warnings
error: could not compile `lcp` (lib); 2 warnings emitted

Caused by:
  process didn't exit successfully: `/home/owner/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustc --crate-name lcp --edition=2024 src/lib.rs --error-format=json --json=diagnostic-rendered-ansi,artifacts,future-incompat --diagnostic-width=124 --crate-type lib --emit=dep-info,metadata,link -C embed-bitcode=no -C debuginfo=2 --check-cfg 'cfg(docsrs,test)' --check-cfg 'cfg(feature, values())' -C metadata=f215ee653b74a792 -C extra-filename=-ab80402c8131bde7 --out-dir /home/owner/repos/compiler_error/lcp/target/debug/deps -C incremental=/home/owner/repos/compiler_error/lcp/target/debug/incremental -L dependency=/home/owner/repos/compiler_error/lcp/target/debug/deps --extern async_channel=/home/owner/repos/compiler_error/lcp/target/debug/deps/libasync_channel-c6554ffbd9187735.rmeta --extern codec=/home/owner/repos/compiler_error/lcp/target/debug/deps/libcodec-11c76b29dba3f814.rmeta --extern embassy_futures=/home/owner/repos/compiler_error/lcp/target/debug/deps/libembassy_futures-3dcc6f905aaa095b.rmeta --extern embassy_time=/home/owner/repos/compiler_error/lcp/target/debug/deps/libembassy_time-e7290c3990b23b5e.rmeta --extern embedded_io_async=/home/owner/repos/compiler_error/lcp/target/debug/deps/libembedded_io_async-ec87c78bb722456f.rmeta --extern heapless=/home/owner/repos/compiler_error/lcp/target/debug/deps/libheapless-59f5e7b6bdbb98f9.rmeta` (exit status: 101)

[rustc-ice-2026-07-31T01_26_39-37443.txt](https://github.com/user-attachments/files/30568248/rustc-ice-2026-07-31T01_26_39-37443.txt)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the supplied Rust reproducer and its use of #![feature(generic_const_exprs)], then reduce the Cargo dependencies and code until the ICE remains in a minimal example. Run that example with the relevant compiler build and record the ICE output; done means a minimized reproducer and a compiler test or fix that no longer triggers the failure.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.