Skip to content

internal.print — match OPA's set-comprehension wrapping and cross-product semantics #209

@matajoh

Description

@matajoh

Summary

The current internal.print implementation prints arguments directly and fails the rule body if any argument is undefined. OPA wraps each argument in a set comprehension at compile time so that undefined values produce <undefined> in output rather than short-circuiting evaluation. This issue tracks bringing internal.print to full OPA equivalence.

Current behavior

  • print() arguments are passed through as raw values
  • If any argument is Undefined, the builtin returns false (rule body fails)
  • Output goes unconditionally to std::cout

OPA behavior (topdown/print.go)

  1. Compiler rewrite (ast/compile.go:rewritePrintCalls): Each print(a, b, ...) is rewritten to internal.print([{__local0__ | __local0__ = a}, {__local1__ | __local1__ = b}, ...]) — wrapping every argument in a set comprehension so undefined expressions become empty sets rather than failing.
  2. Cross-product expansion: If a set has multiple elements (e.g. from walk()), OPA prints every combination across all argument positions (one line per tuple).
  3. Rendering: Strings print without quotes; empty sets render as <undefined>; non-set/non-primitive arguments halt with "illegal argument type".
  4. Configurable output: A print.Hook interface controls where output goes. If no hook is set, internal.print is a silent no-op.

Work items

  1. Compiler rewrite pass — Add a rewrite in the file_to_rego or early rego_to_bundle pipeline that transforms print() calls: wrap each argument in a set comprehension, pack into a single array argument, rename to internal.print. This is the largest piece of work.
  2. Builtin cross-product logic — Rewrite the print() function in src/builtins/internal.cc to iterate the array-of-sets, compute the cartesian product, and format/print one line per combination.
  3. <undefined> rendering — Empty sets (originally undefined expressions) render as <undefined>.
  4. Type dispatch — Strings render without quotes; compounds use to_key(); non-set arguments in the array produce an error.
  5. Print hook API — Add a configurable callback on Interpreter (C++ and C API) to replace hardcoded std::cout. No hook = silent no-op.
  6. Tests — Add YAML test cases covering: empty print, string/collection args, undefined args, cross-product with sets, and error on illegal argument types.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestopa-compatIncreasing compatibility with the upstream OPA implementation.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions