Skip to content

Preserve lazy evaluation in integer primitive debug checks #31

@morinim

Description

@morinim

Problem

integer::internal::int_value_index() currently checks all arguments by iterating over f->arity() and calling pars[i] for each argument.

That helper is used inside Expects(...), so in debug builds integer primitives eagerly evaluate every argument before the primitive body runs. This changes the behavior of lazy conditionals such as:

  • integer::ife
  • integer::ifl
  • integer::ifz

For these primitives, only the condition and the selected branch should be evaluated. In debug builds, the current check can evaluate an unused branch, which may be invalid, expensive, or side-effecting.

Expected Behavior

Debug validation should preserve the same lazy evaluation semantics as release builds.

For example, integer::ifz should validate/evaluate:

  • argument 0 first
  • argument 1 only when the condition is zero
  • argument 2 only otherwise

Suggested Fix

Replace the arity-wide helper with one that checks only explicitly requested argument indexes, then call it after branch selection where needed.

Example shape:

[[nodiscard]] inline bool int_value_index(const function::params &pars, std::initializer_list<std::size_t> is)
{
  return std::ranges::all_of(is, [&pars](std::size_t i)
  {
    return pars[i].index() == d_int;
  });
}

Then conditionals can validate only the selected branch.

Notes

This is mostly a debug-build correctness issue because Expects(...) is compiled out under NDEBUG, but debug and release should not differ in which lazy branches are evaluated.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions