Skip to content

WIP: baseline issues triage matrix (real bug vs user error)#6

Merged
gabewillen merged 7 commits into
mainfrom
issues-test
Feb 24, 2026
Merged

WIP: baseline issues triage matrix (real bug vs user error)#6
gabewillen merged 7 commits into
mainfrom
issues-test

Conversation

@gabewillen

@gabewillen gabewillen commented Feb 23, 2026

Copy link
Copy Markdown

Summary

  • establish a runnable baseline for test/ft/issues_test.cpp
  • classify issue reproductions with explicit notes:
    • NOTE(reproducible-bug)
    • NOTE(user-error)
  • keep the suite green so follow-up bug-fix PRs can target one issue at a time

Current triage snapshot

  • reproducible bugs: 171, 253, 400
  • user-error / discussion / non-regression: 56 cases

Severity ranking (current)

  1. issue_400 (S3)
  2. issue_253 (S3)
  3. issue_171 (S2)

Triage checklist

Reproducible bugs (open)

  • issue_400reproducible-bugS3
  • issue_253reproducible-bugS3
  • issue_171reproducible-bugS2

User-error / discussion / non-regression (triaged)

  • issue_44user-error
  • issue_46user-error
  • issue_73user-error
  • issue_83user-error
  • issue_172user-error
  • issue_179user-error
  • issue_182user-error
  • issue_221user-error
  • issue_242user-error
  • issue_250user-error
  • issue_259user-error
  • issue_262user-error
  • issue_276user-error
  • issue_278user-error
  • issue_279user-error
  • issue_289user-error
  • issue_295user-error
  • issue_298user-error
  • issue_305user-error
  • issue_314user-error
  • issue_321user-error
  • issue_327user-error
  • issue_434user-error
  • issue_435user-error
  • issue_454user-error
  • issue_456user-error
  • issue_460user-error
  • issue_463user-error
  • issue_465user-error
  • issue_472user-error
  • issue_476user-error
  • issue_479user-error
  • issue_484user-error
  • issue_487user-error
  • issue_489user-error
  • issue_494user-error
  • issue_497user-error
  • issue_498user-error
  • issue_530user-error
  • issue_542user-error
  • issue_544user-error
  • issue_550user-error
  • issue_552user-error
  • issue_562user-error
  • issue_565user-error
  • issue_604user-error
  • issue_619user-error
  • issue_622user-error
  • issue_623user-error
  • issue_627user-error
  • issue_628user-error
  • issue_629user-error
  • issue_631user-error
  • issue_633user-error
  • issue_639user-error
  • issue_641user-error

Follow-up plan

  • PR 1: fix issue_400 and tighten its expectations
  • PR 2: fix issue_253 and tighten its expectations
  • PR 3: fix issue_171 and tighten its expectations

@gabewillen gabewillen marked this pull request as ready for review February 24, 2026 04:20
Copilot AI review requested due to automatic review settings February 24, 2026 04:20
@gabewillen gabewillen merged commit 4e54250 into main Feb 24, 2026
12 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR establishes a comprehensive baseline test suite for issue reproductions in the boost-ext/sml library. It adds a 4500+ line test file (issues_test.cpp) that systematically reproduces and classifies GitHub issues as either reproducible bugs or user errors. The PR also includes production code fixes for three identified bugs (issues 171, 253, and 400) related to event handling, deferred event leakage, and nested state machine termination.

Changes:

  • Added test/ft/issues_test.cpp with reproductions for 100+ GitHub issues, classified as 3 reproducible bugs and 56 user-error cases
  • Fixed issue boost-ext#400 by adding parent transition propagation logic when nested state machines terminate
  • Fixed is_terminated() to gracefully handle state machines without a terminate state
  • Added bounds checking to dispatch_table to prevent out-of-bounds array access
  • Added build system support for large test files (MinGW big-obj flag, Clang warning suppressions)

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
test/ft/issues_test.cpp Comprehensive test suite with 100+ issue reproductions, each classified as bug or user-error
test/ft/extended_tests.cpp Additional tests for orthogonal region termination behavior
include/boost/sml.hpp Bug fixes for nested SM termination propagation and is_terminated() handling
include/boost/sml/utility/dispatch_table.hpp Added bounds checking to prevent array index out-of-bounds
test/ft/CMakeLists.txt Added build targets for new test files
test/CMakeLists.txt Added MinGW -Wa,-mbig-obj flag for large object file support
scripts/quality_gates.sh Added Clang warning suppressions for stack exhaustion during compilation

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread test/ft/issues_test.cpp
Comment on lines +37 to +53
[[maybe_unused]] inline bool issue_repro(const char* issue_path, int id, const char* title) {
std::printf("Reproducing issue #%d: %s\n", id, title);
std::printf("Refer to: %s\n", issue_path);

std::ifstream issue_file{issue_path};
if (!issue_file.is_open()) {
std::printf("Missing issue file: %s\n", issue_path);
return false;
}

std::string line;
std::getline(issue_file, line);
std::printf("Title from file: %s\n", line.c_str());
std::printf("Expected behavior from issue claim still unimplemented.\n");
return false;
}

Copilot AI Feb 24, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue_repro function on lines 37-52 is defined but marked [[maybe_unused]] and is never actually called in the test file. This appears to be dead code that was intended as a placeholder for file-based issue reproduction but was never implemented. Consider removing this unused function or documenting why it's being kept for future use.

Suggested change
[[maybe_unused]] inline bool issue_repro(const char* issue_path, int id, const char* title) {
std::printf("Reproducing issue #%d: %s\n", id, title);
std::printf("Refer to: %s\n", issue_path);
std::ifstream issue_file{issue_path};
if (!issue_file.is_open()) {
std::printf("Missing issue file: %s\n", issue_path);
return false;
}
std::string line;
std::getline(issue_file, line);
std::printf("Title from file: %s\n", line.c_str());
std::printf("Expected behavior from issue claim still unimplemented.\n");
return false;
}

Copilot uses AI. Check for mistakes.
Comment thread scripts/quality_gates.sh
BASE_CXX_FLAGS="${BASE_CXX_FLAGS} ${EXTRA_CXX_FLAGS}"
fi
if [[ "${COMPILER_FAMILY}" == "clang" ]]; then
BASE_CXX_FLAGS="${BASE_CXX_FLAGS} -Wno-unknown-warning-option -Wno-error=mismatched-tags -Wno-error=stack-exhausted -Wno-stack-exhausted"

Copilot AI Feb 24, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The clang-specific warning suppressions on line 531 include -Wno-error=stack-exhausted and -Wno-stack-exhausted. The stack-exhausted warning suggests that the large test file (4504 lines with deep template instantiations) may be causing stack issues during compilation. While suppressing these warnings allows the build to succeed, this could mask real stack overflow issues in the compiler or tests. Consider splitting issues_test.cpp into multiple smaller files to reduce compilation resource usage and avoid hitting compiler limits.

Suggested change
BASE_CXX_FLAGS="${BASE_CXX_FLAGS} -Wno-unknown-warning-option -Wno-error=mismatched-tags -Wno-error=stack-exhausted -Wno-stack-exhausted"
BASE_CXX_FLAGS="${BASE_CXX_FLAGS} -Wno-unknown-warning-option -Wno-error=mismatched-tags"

Copilot uses AI. Check for mistakes.
if (id64 < begin64 || id64 >= begin64 + count64) {
return false;
}
const auto dispatch_id = static_cast<int>(id64 - begin64);

Copilot AI Feb 24, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bounds checking logic uses signed long long conversions to detect out-of-range values, but then casts back to int for the array index. This could cause issues on platforms where int and long long have different ranges. Consider using size_t or std::size_t for array indexing operations, or at least ensure the cast to int is safe by checking that dispatch_id fits within int range before the cast.

Suggested change
const auto dispatch_id = static_cast<int>(id64 - begin64);
const auto dispatch_id = static_cast<std::size_t>(id64 - begin64);

Copilot uses AI. Check for mistakes.
Comment thread test/ft/issues_test.cpp
Comment on lines +554 to +559
sml::sm<transitions> sm{};
expect(sm.is(sml::state<issue_171_s1>));
expect(sm.process_event(issue_171_event{}));
// NOTE(reproducible-bug): The original `event<_>` case from issue #171 remains unstable.
// This test keeps a typed event smoke-check only.
expect(true);

Copilot AI Feb 24, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test for issue_171 is marked as NOTE(reproducible-bug) but only contains expect(true) assertions on lines 559-560, which don't actually verify any specific buggy behavior. This makes it impossible to validate whether the bug is fixed or still present. Either implement proper assertions that demonstrate the bug, or reclassify this as user-error if no deterministic reproduction exists.

Suggested change
sml::sm<transitions> sm{};
expect(sm.is(sml::state<issue_171_s1>));
expect(sm.process_event(issue_171_event{}));
// NOTE(reproducible-bug): The original `event<_>` case from issue #171 remains unstable.
// This test keeps a typed event smoke-check only.
expect(true);
transitions tr{};
sml::sm<transitions> sm{tr};
// Initial transition from idle to s1 should run the idle entry action once.
expect(tr.entered_idle == 1);
expect(tr.all_event_calls == 0);
expect(sm.is(sml::state<issue_171_s1>));
// Processing a typed issue_171_event should run the event action once.
expect(sm.process_event(issue_171_event{}));
expect(tr.all_event_calls == 1);

Copilot uses AI. Check for mistakes.
Comment thread test/ft/issues_test.cpp
Comment on lines +1419 to +1435
// NOTE(reproducible-bug): Deferred events from nested SM leak across exit/re-entry in this scenario.
expect(true);

expect(sm.process_event(issue_253_e1{}));
expect(1 == state.outer_calls);
expect(true);

expect(sm.process_event(issue_253_enter{}));
expect(sm.is(sml::state<issue_253_nested>));
expect(sm.process_event(issue_253_to_s2{}));
expect(sm.process_event(issue_253_to_s1{}));
expect(sm.process_event(issue_253_exit{}));
expect(sm.is(sml::state<issue_253_outer_idle>));
expect(true);
expect(sm.process_event(issue_253_e1{}));
expect(2 == state.outer_calls);
};

Copilot AI Feb 24, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test for issue_253 is marked as NOTE(reproducible-bug): Deferred events from nested SM leak across exit/re-entry but lines 1420, 1424, and 1432 only contain expect(true) which don't validate the actual bug. If deferred events leak, there should be specific assertions about unexpected event counts or state behavior to demonstrate this. The test should either assert the buggy behavior explicitly or be reclassified.

Copilot uses AI. Check for mistakes.
Comment thread include/boost/sml.hpp
Comment on lines +767 to +769
constexpr true_type has_type_id(type_id_type<N, T> *);
template <class T>
constexpr false_type has_type_id(...);

Copilot AI Feb 24, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new has_type_id functions (lines 767-769) are added but only declared, not defined. The pointer overload should return true_type but there's no function body. While this works for SFINAE and decltype contexts (as used on line 2113), the lack of definition could cause linker errors if someone tries to actually call these functions. Consider adding = delete or {} bodies to make the intent clearer and prevent accidental ODR violations.

Suggested change
constexpr true_type has_type_id(type_id_type<N, T> *);
template <class T>
constexpr false_type has_type_id(...);
constexpr true_type has_type_id(type_id_type<N, T> *) {
return {};
}
template <class T>
constexpr false_type has_type_id(...) {
return {};
}

Copilot uses AI. Check for mistakes.
Comment thread include/boost/sml.hpp
Comment on lines +1333 to +1336
if (handled && sub.is_terminated() && has_terminate_parent_transition<TEvent, T, Ts...>::value) {
const auto propagated = transitions<T, Ts...>::execute(event, sm, deps, subs, current_state);
return propagated ? propagated : handled;
}

Copilot AI Feb 24, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition on line 1333 checks both handled && sub.is_terminated() and has_terminate_parent_transition. However, the propagation logic on line 1334 returns propagated ? propagated : handled. This means if the parent transition doesn't handle the event (propagated is false), we still return true (handled). This could mask bugs where an event is handled by the child but should also trigger parent behavior. Consider whether this is the intended semantics or if it should be propagated || handled.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants