qlibs / sml

C++20 State Machine library
166 stars 7 forks source link

Compilation issues with GCC-12 (and 13 when Clang-17 works) #4

Closed sschnug closed 7 months ago

sschnug commented 9 months ago

While playing with this library for the first time, i stumbled upon compiler-issues with the following example code:

#include "sml2"

#include <cstdio>

using namespace sml::dsl;

// states
auto state_idling     = "idling"_s;
auto state_solving    = "solving"_s;
auto state_cancelling = "cancelling"_s;

// events
struct client_solve              {};
struct client_cancel             {};
struct server_finish_computation {};

// actions
auto action_run_solve    = [] { std::puts("run_solve"); };
auto action_cancel_solve = [] { std::puts("cancel_solve"); };
auto action_reject_solve = [] { std::puts("reject_solve"); };
auto action_noop         = [] { std::puts("noop"); };
auto action_unreachable  = [] { std::puts("unreachable code reached: this should not happen!"); };

int main() {
    // state machine
    sml::sm fsm = [] {

        // * src_state + event [ guard ] / action = dst_state 
        return transition_table{
            *state_idling     + event<client_solve>              / action_run_solve    = state_solving,
             state_idling     + event<client_cancel>             / action_noop         = state_idling,
             state_idling     + event<server_finish_computation> / action_unreachable  = state_idling,

             state_solving    + event<client_cancel>             / action_cancel_solve = state_cancelling,
             state_solving    + event<client_solve>              / action_reject_solve = state_solving,
             state_solving    + event<server_finish_computation> / action_noop         = state_idling,

             // WITH    -> gcc-12 ERROR; clang-17 OK
             // WITHOUT -> gcc-12 OK; clang-17 OK 
             state_cancelling + event<client_solve>              / action_reject_solve = state_cancelling, 
        };

    };

    fsm.process_event(server_finish_computation{});
    fsm.process_event(client_cancel{});
    fsm.process_event(client_solve{});             
    fsm.process_event(client_solve{});
    fsm.process_event(client_cancel{});
    fsm.process_event(client_solve{});
}

Clang:

unreachable code reached: this should not happen!
noop
run_solve
reject_solve
cancel_solve
reject_solve

GCC

external/sml2/sml2: In instantiation of 'struct sml::v_2_0_0::meta::mappings<client_solve(sml::v_2_0_0::<lambda()>), 1>::set<sml::v_2_0_0::back::transitions<sml::v_2_0_0::front::transition<sml::v_2_0_0::meta::fixed_hash_string<10>{"cancelling", 65982}, sml::v_2_0_0::meta::list<client_solve>, const sml::v_2_0_0::front::detail::<lambda()>, <lambda()>, sml::v_2_0_0::meta::fixed_hash_string<10>{"cancelling", 65982}>, sml::v_2_0_0::front::transition<sml::v_2_0_0::meta::fixed_hash_string<7>{"*idling", 4670}, sml::v_2_0_0::meta::list<client_solve>, const sml::v_2_0_0::front::detail::<lambda()>, <lambda()>, sml::v_2_0_0::meta::fixed_hash_string<7>{"solving", 10910}> > >':
external/sml2/sml2:155:5:   required from 'constexpr void sml::v_2_0_0::back::insert() [with TEvent = client_solve(sml::v_2_0_0::<lambda()>); T = sml::v_2_0_0::front::transition<sml::v_2_0_0::meta::fixed_hash_string<10>{"cancelling", 65982}, sml::v_2_0_0::meta::list<client_solve>, const sml::v_2_0_0::front::detail::<lambda()>, <lambda()>, sml::v_2_0_0::meta::fixed_hash_string<10>{"cancelling", 65982}>; auto Counter = 0]'
external/sml2/sml2:164:64:   required from 'class sml::v_2_0_0::back::sm<sml::v_2_0_0::dsl::transition_table<sml::v_2_0_0::front::transition<sml::v_2_0_0::meta::fixed_hash_string<7>{"*idling", 4670}, sml::v_2_0_0::meta::list<client_solve>, const sml::v_2_0_0::front::detail::<lambda()>, <lambda()>, sml::v_2_0_0::meta::fixed_hash_string<7>{"solving", 10910}>, sml::v_2_0_0::front::transition<sml::v_2_0_0::meta::fixed_hash_string<6>{"idling", 4670}, sml::v_2_0_0::meta::list<client_cancel>, const sml::v_2_0_0::front::detail::<lambda()>, <lambda()>, sml::v_2_0_0::meta::fixed_hash_string<6>{"idling", 4670}>, sml::v_2_0_0::front::transition<sml::v_2_0_0::meta::fixed_hash_string<6>{"idling", 4670}, sml::v_2_0_0::meta::list<server_finish_computation>, const sml::v_2_0_0::front::detail::<lambda()>, <lambda()>, sml::v_2_0_0::meta::fixed_hash_string<6>{"idling", 4670}>, sml::v_2_0_0::front::transition<sml::v_2_0_0::meta::fixed_hash_string<7>{"solving", 10910}, sml::v_2_0_0::meta::list<client_cancel>, const sml::v_2_0_0::front::detail::<lambda()>, <lambda()>, sml::v_2_0_0::meta::fixed_hash_string<10>{"cancelling", 65982}>, sml::v_2_0_0::front::transition<sml::v_2_0_0::meta::fixed_hash_string<7>{"solving", 10910}, sml::v_2_0_0::meta::list<client_solve>, const sml::v_2_0_0::front::detail::<lambda()>, <lambda()>, sml::v_2_0_0::meta::fixed_hash_string<7>{"solving", 10910}>, sml::v_2_0_0::front::transition<sml::v_2_0_0::meta::fixed_hash_string<7>{"solving", 10910}, sml::v_2_0_0::meta::list<server_finish_computation>, const sml::v_2_0_0::front::detail::<lambda()>, <lambda()>, sml::v_2_0_0::meta::fixed_hash_string<6>{"idling", 4670}>, sml::v_2_0_0::front::transition<sml::v_2_0_0::meta::fixed_hash_string<10>{"cancelling", 65982}, sml::v_2_0_0::meta::list<client_solve>, const sml::v_2_0_0::front::detail::<lambda()>, <lambda()>, sml::v_2_0_0::meta::fixed_hash_string<10>{"cancelling", 65982}> >, sml::v_2_0_0::<lambda()> >'
external/sml2/sml2:352:8:   required from 'struct sml::v_2_0_0::sm<main()::<lambda()>, sml::v_2_0_0::<lambda()> >'
playground/design/fsm/fsm_2_own.cc:43:5:   required from here
external/sml2/sml2:171:51:   in 'constexpr' expansion of 'sml::v_2_0_0::back::transition<sml::v_2_0_0::<lambda()>, sml::v_2_0_0::front::transition<sml::v_2_0_0::meta::fixed_hash_string<10>{"cancelling", 65982}, sml::v_2_0_0::meta::list<client_solve>, const sml::v_2_0_0::front::detail::<lambda()>, <lambda()>, sml::v_2_0_0::meta::fixed_hash_string<10>{"cancelling", 65982}>, sml::v_2_0_0::meta::list, client_solve>((sml::v_2_0_0::front::transition<sml::v_2_0_0::meta::fixed_hash_string<10>{"cancelling", 65982}, sml::v_2_0_0::meta::list<client_solve>, const sml::v_2_0_0::front::detail::<lambda()>, <lambda()>, sml::v_2_0_0::meta::fixed_hash_string<10>{"cancelling", 65982}>::event(), sml::v_2_0_0::front::transition<sml::v_2_0_0::meta::fixed_hash_string<10>{"cancelling", 65982}, sml::v_2_0_0::meta::list<client_solve>, const sml::v_2_0_0::front::detail::<lambda()>, <lambda()>, sml::v_2_0_0::meta::fixed_hash_string<10>{"cancelling", 65982}>::event()))'
external/sml2/sml2:137:17: error: redefinition of 'auto sml::v_2_0_0::meta::get(mappings<client_solve(sml::v_2_0_0::<lambda()>), 1>)'
  137 |     friend auto get(mappings) { return T{}; }
      |                 ^~~
external/sml2/sml2:137:17: note: 'auto sml::v_2_0_0::meta::get(mappings<client_solve(sml::v_2_0_0::<lambda()>), 1>)' previously declared here

Expected Behavior

Actual Behavior

Steps to Reproduce the Problem

Just compile and run.

My setup based on bazel:

build --host_cxxopt=-std=c++20 --cxxopt=-std=c++20 --repo_env=CC=clang-17 --repo_env=CXX=clang++-17

Or Godbolt clang-trunk vs. gcc-13 | aka ok/fail

Specifications

krzysztof-jusiak commented 9 months ago

Confirming that I'm seeing the same, I have suspicious where the issue is coming from but I don't have a fix yet, need to dig some more :thinking:

pao commented 8 months ago

If you didn't have a reduced test case for this yet, I managed to run into the same issue and generated one, which you can find at https://godbolt.org/z/e3MvdTqxr. The breakage happens on the third transition which uses the same event. Also observed that GCC trunk is generally unhappy with sml2 in its current state.

krzysztof-jusiak commented 7 months ago

Sorry it took so long, but the issue is now fixed, all godbolt links mentioned in this issue are now working. Additional tests have been added to ensure the correctness. As a cherry on top compilation times have been improved by at least factor of 2 on top of already much improved compilation times.