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
- 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.
- 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).
- Rendering: Strings print without quotes; empty sets render as
<undefined>; non-set/non-primitive arguments halt with "illegal argument type".
- Configurable output: A
print.Hook interface controls where output goes. If no hook is set, internal.print is a silent no-op.
Work items
- 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.
- 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.
<undefined> rendering — Empty sets (originally undefined expressions) render as <undefined>.
- Type dispatch — Strings render without quotes; compounds use
to_key(); non-set arguments in the array produce an error.
- Print hook API — Add a configurable callback on
Interpreter (C++ and C API) to replace hardcoded std::cout. No hook = silent no-op.
- Tests — Add YAML test cases covering: empty print, string/collection args, undefined args, cross-product with sets, and error on illegal argument types.
References
Summary
The current
internal.printimplementation 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 bringinginternal.printto full OPA equivalence.Current behavior
print()arguments are passed through as raw valuesUndefined, the builtin returnsfalse(rule body fails)std::coutOPA behavior (
topdown/print.go)ast/compile.go:rewritePrintCalls): Eachprint(a, b, ...)is rewritten tointernal.print([{__local0__ | __local0__ = a}, {__local1__ | __local1__ = b}, ...])— wrapping every argument in a set comprehension so undefined expressions become empty sets rather than failing.walk()), OPA prints every combination across all argument positions (one line per tuple).<undefined>; non-set/non-primitive arguments halt with"illegal argument type".print.Hookinterface controls where output goes. If no hook is set,internal.printis a silent no-op.Work items
file_to_regoor earlyrego_to_bundlepipeline that transformsprint()calls: wrap each argument in a set comprehension, pack into a single array argument, rename tointernal.print. This is the largest piece of work.print()function insrc/builtins/internal.ccto iterate the array-of-sets, compute the cartesian product, and format/print one line per combination.<undefined>rendering — Empty sets (originally undefined expressions) render as<undefined>.to_key(); non-set arguments in the array produce an error.Interpreter(C++ and C API) to replace hardcodedstd::cout. No hook = silent no-op.References
ast/compile.go:2614-2722topdown/print.gotopdown/print_test.gointernal.printreceives a single array-of-sets argumentsrc/builtins/internal.cc:59-79