Skip to content

contracts: add violation handler infrastructure and annotated optional#7129

Draft
Sanchit2662 wants to merge 1 commit intoTheHPXProject:masterfrom
Sanchit2662:poc/contracts-infrastructure
Draft

contracts: add violation handler infrastructure and annotated optional#7129
Sanchit2662 wants to merge 1 commit intoTheHPXProject:masterfrom
Sanchit2662:poc/contracts-infrastructure

Conversation

@Sanchit2662
Copy link
Copy Markdown
Contributor

@Sanchit2662 Sanchit2662 commented Mar 30, 2026

What this is

A standalone, buildable contracts infrastructure - violation handler with enforce/observe/ignore mode dispatch, the macro layer (HPX_CONTRACT_ASSERT, HPX_PRE, HPX_POST), and a minimal optional<T> annotated with P3471 preconditions. Lives in poc/contracts/ and builds independently of HPX's CMake so anyone can run it without a full HPX build.
This is a proof-of-concept : not the real integration, just enough to show the approach is sound.


What I built

Three files that form the core of what the full project would need:

  • violation_handler.hpp - a violation_info struct that mirrors what std::contract_violation will look like in C++26, plus a customizable handler (default: log to stderr + abort). This is the same pattern HPX already uses for assertion_handler.

  • contract_macros.hpp - HPX_CONTRACT_ASSERT, HPX_PRE, HPX_POST macros that dispatch to the right behavior depending on a compile-time mode flag.

  • annotated_optional.hpp - a minimal optional<T> with HPX_CONTRACT_ASSERT(has_value()) on operator*, operator->, and value() - exactly the P3471 preconditions, and exactly what was done in the real optional.hpp on the feat/contracts-optional-operator-star branch.

Plus a contracts_poc.cpp with 20 tests that run across all three modes and a CMakeLists.txt that builds all three at once.


How it works

The key idea is compile-time mode selection:

  • -DHPX_CONTRACTS_MODE=0 → ENFORCE: check predicate, call handler, abort on violation
  • -DHPX_CONTRACTS_MODE=1 → OBSERVE: check predicate, call handler, continue
  • -DHPX_CONTRACTS_MODE=2 → IGNORE: predicate never evaluated (zero cost)

These map directly to P2900's three semantics. In IGNORE mode HPX_CONTRACT_ASSERT(expr) compiles to ((void)0) - zero overhead.

The violation handler is user-installable (same API as hpx::assertion::set_assertion_handler), which makes it testable - instead of aborting, tests install a recording handler that captures violations and check them.


Build it yourself

cd poc/contracts
cmake -S . -B build && cmake --build build -j$(nproc)
./build/contracts_poc_enforce   # 20 passed
./build/contracts_poc_observe   # 20 passed
./build/contracts_poc_ignore    # 1 passed (verifies zero violations in ignore mode)

What this isn't

This is not the actual implementation. Specifically:

  • The violation handler here is header-only. In the real module it would be a compiled .cpp under libs/core/contracts/src/ with HPX_CORE_EXPORT linkage.
  • The macros here don't touch HPX_ASSERT at all. The real integration (via HPX_CONTRACTS_WITH_ASSERTS_AS_CONTRACT_ASSERTS) is already in the existing contracts module.
  • The optional<T> here is a standalone toy. The actual annotation is already on the feat/contracts-optional-operator-star branch in the real optional.hpp.
  • No CMake integration into HPX's build system - this builds standalone on purpose so reviewers can run it without a full HPX build.

Questions for reviewers

  1. The violation handler is noexcept throughout (matching the existing handle_assert). Is that right for observe mode too, or should we allow the handler to throw in observe mode to integrate with HPX's exception-based error propagation?

  2. For HPX_PRE / HPX_POST in fallback mode - I currently put them as body statements (top of function / before return). When native contracts land, they move to the declaration. Does HPX want a migration story for that, or is it acceptable that fallback-mode and native-mode HPX_PRE are syntactically in different positions?

  3. Is the priority order right - containers first, then futures, then parallel algorithms? Or should futures come first since future::get() on an invalid future is probably the most common real-world bug?

Signed-off-by: Sanchit2662 <sanchit2662@gmail.com>
@StellarBot
Copy link
Copy Markdown

Can one of the admins verify this patch?

Copy link
Copy Markdown
Contributor

@hkaiser hkaiser left a comment

Choose a reason for hiding this comment

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

Did you plan to integrate this proof of concept implementation with the existing infrastructure in HPX?

@hkaiser
Copy link
Copy Markdown
Contributor

hkaiser commented Apr 11, 2026

@Sanchit2662 What's your plans with moving this forward?

@Sanchit2662
Copy link
Copy Markdown
Contributor Author

Hey @hkaiser , thanks for taking a look!

Yeah so the plan is definitely not to keep this as a separate standalone thing. The idea is to integrate it properly with the existing assertion infrastructure in HPX. What I'm thinking is that HPX_PRE / HPX_POST / HPX_CONTRACT_ASSERT would just delegate to hpx::assertion::detail::handle_assert under the hood, or something very close to it. That way there's no duplication of the handler registration pattern that's already in libs/core/assertion/.

The three modes map pretty naturally to what HPX already does. IGNORE is basically how release builds work today where HPX_ASSERT expands to nothing. ENFORCE is the HPX_DEBUG equivalent. OBSERVE is the only genuinely new thing, it's the "log the violation but don't abort" mode, which the current assertion infra doesn't support.

For what to annotate first, I was thinking of following the same priority P3471 uses for the hardened STL. So hpx::optional first since it's a direct equivalent (operator*, operator->, value() all need has_value() to be true). Then HPX containers for out-of-bounds stuff, then hpx::future / hpx::shared_future since get() has a clear valid() precondition, and then parallel algorithms for iterator range checks.

On the macro side, the design I have in mind keeps a clean migration path. When a C++26 compiler with native contract support shows up, HPX_PRE / HPX_POST can just expand to the native pre / post syntax without needing to re-annotate everything.

One thing I'm genuinely unsure about though: should the contract mode be its own CMake option like HPX_WITH_CONTRACTS_MODE that exists alongside HPX_DEBUG, or is it cleaner to just fold it into the existing debug flag for now? Would love your thoughts on that.

@hkaiser
Copy link
Copy Markdown
Contributor

hkaiser commented Apr 12, 2026

Yeah so the plan is definitely not to keep this as a separate standalone thing. The idea is to integrate it properly with the existing assertion infrastructure in HPX. What I'm thinking is that HPX_PRE / HPX_POST / HPX_CONTRACT_ASSERT would just delegate to hpx::assertion::detail::handle_assert under the hood, or something very close to it. That way there's no duplication of the handler registration pattern that's already in libs/core/assertion/.

The three modes map pretty naturally to what HPX already does. IGNORE is basically how release builds work today where HPX_ASSERT expands to nothing. ENFORCE is the HPX_DEBUG equivalent. OBSERVE is the only genuinely new thing, it's the "log the violation but don't abort" mode, which the current assertion infra doesn't support.

IIUC, you suggest adding an additional mode to the existing infrastructure that logs violations. That would be ok, I think.

For what to annotate first, I was thinking of following the same priority P3471 uses for the hardened STL. So hpx::optional first since it's a direct equivalent (operator*, operator->, value() all need has_value() to be true). Then HPX containers for out-of-bounds stuff, then hpx::future / hpx::shared_future since get() has a clear valid() precondition, and then parallel algorithms for iterator range checks.

As said, modifying hpx::optional does not make real sense as this is a fall back workaround version used for older compilers only. Except for that, hpx::optional is simply a alias for std::optional.

One thing I'm genuinely unsure about though: should the contract mode be its own CMake option like HPX_WITH_CONTRACTS_MODE that exists alongside HPX_DEBUG, or is it cleaner to just fold it into the existing debug flag for now? Would love your thoughts on that.

For pre-C++26 compilers this has to be a HPX CMake option. For conforming compilers this can be derived from the compiler options themselves.

@codacy-production
Copy link
Copy Markdown

Not up to standards ⛔

🔴 Issues 1 critical · 4 high · 12 medium

Alerts:
⚠ 17 issues (≤ 0 issues of at least minor severity)

Results:
17 new issues

Category Results
ErrorProne 4 high
1 critical
12 medium

View in Codacy

🟢 Metrics 46 complexity · 0 duplication

Metric Results
Complexity 46
Duplication 0

View in Codacy

TIP This summary will be updated as you push new changes. Give us feedback

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants