fix(interpreter,rvm): correct partial object rule iteration and classification (#718)

Partial object rules with dynamic keys (e.g. `violations[k] if { ... }`)
only produced a single entry instead of collecting all bindings. Two
independent bugs caused this:

1. Interpreter: the early-return optimization in eval_output_expr_in_loop
   checked whether the rule_ref was constant but never verified whether
   the key expression was also constant. A variable key like `k` was
   treated as constant output, causing the loop to exit after the first
   iteration. Fixed by gating early-return on key_expr constness.

2. RVM: compute_rule_type incorrectly classified `p[k] if { ... }` as
   PartialSet instead of PartialObject. OPA v1 semantics define this
   form as a partial object (key -> true). Fixed the classification and
   added compiler error guards for patterns the RVM codegen cannot yet
   handle (constant keys, nested bracket keys), ensuring graceful
   fallback to the interpreter.

The OPA test harness now skips RVM validation per-case when partial
object compiler errors are raised, rather than blanket-skipping entire
folders. This preserves RVM coverage for unrelated tests in the same
folders.

Closes #712

Co-authored-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
Copilot
2026-05-18 15:14:08 -05:00
committed by GitHub
parent 3111bf58f2
commit dae3052781
6 changed files with 1167 additions and 1 deletions

View File

@@ -0,0 +1,150 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
cases:
- note: constant_key_partial_object_v1
data: {}
input:
enabled: true
modules:
- |
package test
import rego.v1
p["fixed"] if {
input.enabled
}
query: data.test
want_result:
p:
fixed: true
- note: multilevel_partial_object_v1
data: {}
input:
nested:
app:
read: 1
write: 2
ops:
deploy: 3
modules:
- |
package test
import rego.v1
p[a][b] if {
some a, obj in input.nested
some b, _ in obj
}
query: data.test
want_result:
p:
app:
read: true
write: true
ops:
deploy: true
- note: constant_key_partial_object_explicit_value_v1
data: {}
input:
enabled: true
modules:
- |
package test
import rego.v1
p["fixed"] := 7 if {
input.enabled
}
query: data.test
want_result:
p:
fixed: 7
- note: multilevel_partial_object_explicit_value_v1
data: {}
input:
nested:
app:
read: 1
write: 2
ops:
deploy: 3
modules:
- |
package test
import rego.v1
p[a][b] := v if {
some a, obj in input.nested
some b, v in obj
}
query: data.test
want_result:
p:
app:
read: 1
write: 2
ops:
deploy: 3
- note: issue_712_reproducer_v0_partial_set
data: {}
input:
servers:
FOO: 1
BAR: 2
BAZ: 3
modules:
- |
package test
import future.keywords.in
violations[k] {
some k, _ in input.servers
}
query: data.test.violations
want_result:
set!: ["BAR", "BAZ", "FOO"]
- note: issue_712_reproducer_v1_partial_object
data: {}
input:
servers:
FOO: 1
BAR: 2
BAZ: 3
modules:
- |
package test
import rego.v1
violations[k] if {
some k, _ in input.servers
}
query: data.test.violations
want_result:
BAR: true
BAZ: true
FOO: true
- note: issue_712_reproducer_v1_contains_partial_set
data: {}
input:
servers:
FOO: 1
BAR: 2
BAZ: 3
modules:
- |
package test
import rego.v1
violations contains k if {
some k, _ in input.servers
}
query: data.test.violations
want_result:
set!: ["BAR", "BAZ", "FOO"]