Fix partial object iteration dropping bindings and guard unsupported RVM patterns#718
Fix partial object iteration dropping bindings and guard unsupported RVM patterns#718
Conversation
Agent-Logs-Url: https://github.com/microsoft/regorus/sessions/abb61b2e-582a-48ea-91ac-0cfebaaea96f Co-authored-by: anakrish <35780660+anakrish@users.noreply.github.com>
Agent-Logs-Url: https://github.com/microsoft/regorus/sessions/abb61b2e-582a-48ea-91ac-0cfebaaea96f Co-authored-by: anakrish <35780660+anakrish@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Fixes a correctness bug where partial object rules with dynamic keys would stop after the first matching binding, causing missing keys in the produced object. The fix aligns interpreter constantness detection and RVM rule typing so both execution paths emit all bindings for dynamic-key partial objects.
Changes:
- Interpreter: tighten “constant rule” detection so early-return is only enabled when the object key expression is a simple literal.
- RVM compiler: classify bracket-head comprehension rules (
p[k] if ...) asPartialObjectinstead ofPartialSet. - Add regression YAML cases covering dynamic-key partial objects, explicit-value partial objects, multi-body iteration, array iteration, and
containspartial sets (plus interpreter-only v1 coverage for constant-key and multilevel shapes).
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| tests/rvm/rego/cases/partial_object_rules.yaml | Adds RVM+interpreter regression cases for partial object iteration (and contains partial set) behavior. |
| tests/interpreter/cases/rule/partial_object_v1.yaml | Adds interpreter-focused Rego v1 cases for constant-key and multilevel partial object shapes. |
| src/languages/rego/compiler/rules.rs | Adjusts rule-type classification so p[k] if { ... } compiles as a partial object. |
| src/interpreter.rs | Prevents incorrect early-return for dynamic-key partial object rules by tightening constness detection. |
| BAZ: 3 | ||
| FOO: 1 | ||
|
|
||
| - note: partial_object_constant_key_still_works |
RVM: Add compiler errors for unsupported partial object patterns (addresses #719)The interpreter fix in this PR is correct and approved. However, the RVM classification change ( What to doAdd compiler error guards so unsupported patterns fall back to the interpreter. The RVM compiler already does this for other unsupported features (e.g., Step 1: Add error variants to
|
|
@copilot Take a look at #718 (comment) |
Agent-Logs-Url: https://github.com/microsoft/regorus/sessions/6aa57d3d-7860-4646-ac8a-8e6183b117da Co-authored-by: anakrish <35780660+anakrish@users.noreply.github.com>
Addressed in f2ff982. I added explicit RVM compiler errors for constant-key and nested-bracket partial object heads, validated them in the rule compilation path, and updated the RVM regressions to expect those compiler errors while allowing interpreter success.
Done in f2ff982. |
Review Findings — Round 2The interpreter fix is correct and approved. The RVM compiler error guards need adjustments: Finding 1 (High): Validation guard is too narrow —
|
|
@copilot Please address the findings in the previous comment. |
Agent-Logs-Url: https://github.com/microsoft/regorus/sessions/357566e7-fc57-4672-94cc-d31687ed59d1 Co-authored-by: anakrish <35780660+anakrish@users.noreply.github.com>
Addressed in bf43dd4. The partial-object guard now applies to all
Done in bf43dd4. |
| cases: | ||
| - note: constant_key_partial_object_v1 | ||
| data: {} | ||
| input: | ||
| enabled: true | ||
| modules: | ||
| - | | ||
| package test | ||
| import rego.v1 | ||
|
|
||
| p["fixed"] if { | ||
| input.enabled | ||
| } |
Addressed in 3b4f618. I added the comprehensive partial-object cases to
Done in 3b4f618. |
| RuleHead::Compr { refr, assign, .. } => match refr.as_ref() { | ||
| crate::ast::Expr::RefBrack { .. } if assign.is_some() => { | ||
| RuleType::PartialObject | ||
| } | ||
| crate::ast::Expr::RefBrack { .. } => RuleType::PartialSet, | ||
| crate::ast::Expr::RefBrack { .. } => RuleType::PartialObject, | ||
| _ => RuleType::Complete, |
Fix: Tests should not encode buggy behavior as expected outputSeveral test cases currently hard-code known-incorrect RVM output as Rule: If the expected behavior cannot be tested correctly today, use Tests to fix:1. Undefined key/value tests — These currently assert
For each, change to # TODO (#719): RVM incorrectly materializes undefined keys/values instead of
# skipping them. Correct behavior: skip iterations where key or value is undefined.
skip: trueKeep the 2. Duplicate key conflict tests — These currently assert last-writer-wins (
For each, set the correct expectation ( # TODO (#719): regorus silently overwrites conflicting keys instead of erroring.
# Correct behavior: error on same key with different values.
skip: true
want_error: "conflict"3. Nested literal prefix — Alternatively, if fixing the guard is too complex, just add a test case for Do NOT use |
|
@copilot Please address the findings in the previous comment. |
Agent-Logs-Url: https://github.com/microsoft/regorus/sessions/19553cd8-af3d-4285-86c1-3ef1a72c0b93 Co-authored-by: anakrish <35780660+anakrish@users.noreply.github.com>
Addressed in 4f6651a. The undefined-key/value and conflicting-duplicate-key cases now use
Done in 4f6651a. |
|
@copilot Please take a look at #718 (comment) |
Took another look — this was already addressed in 4f6651a, and there aren’t any additional changes needed beyond that update. |
Missing: Regression tests for the exact issue #712 reproducerThe PR fixes the bug but doesn't have a test for the exact scenario reported in the issue. Please add these to lock down v0/v1 semantics for the original reproducer: # --- Issue #712 exact reproducer: violations[k] in v0 vs v1 ---
# v0: violations[k] { ... } is a partial SET (no "if" keyword)
- note: issue_712_reproducer_v0_partial_set
data: {}
input:
servers:
FOO: 1
BAR: 2
BAZ: 3
modules:
- |
package test
violations[k] {
some k, _ in input.servers
}
query: data.test.violations
want_result:
set!: ["BAR", "BAZ", "FOO"]
# v1: violations[k] if { ... } is a partial OBJECT (key -> true)
# This is the exact bug from issue #712 — was producing only one entry
- note: issue_712_reproducer_v1_partial_object
data: {}
input:
servers:
FOO: 1
BAR: 2
BAZ: 3
modules:
- |
package test
violations[k] if {
some k, _ in input.servers
}
query: data.test.violations
want_result:
BAR: true
BAZ: true
FOO: true
# v1: violations contains k if { ... } is a partial SET
- note: issue_712_reproducer_v1_contains_partial_set
data: {}
input:
servers:
FOO: 1
BAR: 2
BAZ: 3
modules:
- |
package test
violations contains k if {
some k, _ in input.servers
}
query: data.test.violations
want_result:
set!: ["BAR", "BAZ", "FOO"]These three tests together lock down the v0/v1 semantic distinction that caused the original issue. Add them to both |
Agent-Logs-Url: https://github.com/microsoft/regorus/sessions/ca6066bd-e40f-4334-9ace-4b14a72ad7b4 Co-authored-by: anakrish <35780660+anakrish@users.noreply.github.com>
Added the issue #712 reproducer coverage in a639b9d. The RVM suite now includes the v1 partial-object and
Done in a639b9d. |
| let Expr::RefBrack { | ||
| refr: prefix, | ||
| index, | ||
| .. | ||
| } = refr.as_ref() | ||
| else { | ||
| return Ok(()); | ||
| }; | ||
|
|
||
| if Self::has_unsupported_bracket_prefix(prefix) { | ||
| return Err(CompilerError::PartialObjectNestedKeyUnsupported.at(refr.span())); | ||
| } | ||
|
|
||
| if Self::is_simple_literal(index) { | ||
| return Err(CompilerError::PartialObjectConstantKeyUnsupported.at(index.span())); | ||
| } | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
Partial object rules with iterating bodies, such as
p[k] if { some k, _ in input.items }, were only emitting the first matching binding. This affected both the interpreter and the RVM path for the supported dynamic-key form;contains-based partial sets and comprehensions were not the source of the bug.Interpreter: stop early-return on dynamic object keys
eval_output_expr_in_loopsop[k] ifis only treated as constant whenkis a simple literal.RVM compiler: classify supported partial object heads correctly
p[k] if { ... }→ partial objectp[k] := v if { ... }→ partial objectp contains k if { ... }→ partial setp[input.aliases[k]] := v if { ... }continue to compile and collect all bindings.p["a"][k] := v if { ... }continue to compile as supported shapes.RVM compiler: reject unsupported partial object patterns
p["fixed"] if { ... }andp["fixed"] := v if { ... }p[a][b] if { ... },p[a][b] := v if { ... }, and literal-prefix variants such asp[1][k] := v if { ... }p[a].q[b] if { ... }Regression coverage
containspartial setsviolations[k] { ... }as a partial set in v0violations[k] if { ... }as a partial object in v1violations contains k if { ... }as a partial set in v1notfiltering, dot+bracket object-value heads, and OPA-style join/composite-value caseswithin rule bodieseveryvacuous-truth partial-object case was verified to still diverge in the RVM and remains explicitly skipped with a note describing the current behavior.Example:
With:
{ "items": { "FOO": 1, "BAR": 2, "BAZ": 3 } }this now evaluates to:
{ "BAR": true, "BAZ": true, "FOO": true }For now, unsupported RVM partial object shapes fail fast with compiler errors and fall back to the interpreter behavior rather than producing incorrect results. Additional runtime edge-case and conformance-style tests also lock down supported behavior while preserving skipped TODO coverage for known partial-object divergences that remain to be fixed separately.