korken89 / smlang-rs

A State Machine Language DSL procedual macro for Rust
Apache License 2.0
192 stars 28 forks source link

Multiple lifetime issues #57

Closed oblique closed 1 year ago

oblique commented 1 year ago

The following state machine generates incorrect code:

pub struct X;
pub struct Y;

smlang::statemachine! {
    transitions: {
        *State1 + Event1(&'a X) [guard1] / action1 = State2(&'a X),
        State2(&'a X) + Event2(&'b Y) [guard2] / action2 = State3((&'a X, &'b Y)),
    }
}

What is generated:

pub trait StateMachineContext {
    fn guard1<'a>(&mut self, event_data: &'a X) -> Result<(), ()>;
    fn guard2<'b>(&mut self, state_data: &&'a X, event_data: &'b Y) -> Result<(), ()>;
    fn action1<'a, 'a>(&mut self, event_data: &'a X) -> &'a X;
    fn action2<'b>(&mut self, state_data: &'a X, event_data: &'b Y) -> (&'a X, &'b Y);
}

pub enum States<'a> {
    State1,
    State2(&'a X),
    State3((&'a X, &'b Y)),
}

impl<'a> PartialEq for States<'a> {
    // ...
}

pub struct StateMachine<'a, T: StateMachineContext> {
    state: Option<States<'a>>,
    context: T,
}

impl<'a, T: StateMachineContext> StateMachine<'a, T> {
    // ...
    pub const fn new_with_state(context: T, initial_state: States<'a>) -> Self {
        // ...
    }

    pub fn state(&self) -> Result<&States, Error> {
        self.state.as_ref().ok_or_else(|| Error::Poisoned)
    }

    pub fn process_event(&mut self, mut event: Events) -> Result<&States, Error> {
        // ...
    }
    // ...
}

What is expected to be generated:

pub trait StateMachineContext {
    fn guard1<'a>(&mut self, event_data: &'a X) -> Result<(), ()>;
    fn guard2<'a, 'b>(&mut self, state_data: &'a X, event_data: &'b Y) -> Result<(), ()>;
    fn action1<'a>(&mut self, event_data: &'a X) -> &'a X;
    fn action2<'a, 'b>(&mut self, state_data: &'a X, event_data: &'b Y) -> (&'a X, &'b Y);
}

pub enum States<'a, 'b> {
    State1,
    State2(&'a X),
    State3((&'a X, &'b Y)),
}

impl<'a, 'b> PartialEq for States<'a, 'b> {
    // ...
}

pub struct StateMachine<'a, 'b, T: StateMachineContext> {
    state: Option<States<'a, 'b>>,
    context: T,
}

impl<'a, 'b, T: StateMachineContext> StateMachine<'a, 'b, T> {
    // ...
    pub const fn new_with_state(context: T, initial_state: States<'a, 'b>) -> Self {
        // ...
    }

    pub fn state(&self) -> Result<&States<'a, 'b>, Error> {
        self.state.as_ref().ok_or_else(|| Error::Poisoned)
    }

    pub fn process_event(&mut self, mut event: Events<'a, 'b>) -> Result<&States<'a, 'b>, Error> {
        // ...
    }
    // ...
}