Skip to content

fix(rvm): propagate strict rule errors and output conflicts - #934

Draft
Anand Krishnamoorthi (anakrish) wants to merge 2 commits into
microsoft:mainfrom
anakrish:fix/rvm-rule-errors-932
Draft

Anand Krishnamoorthi (anakrish) wants to merge 2 commits into
microsoft:mainfrom
anakrish:fix/rvm-rule-errors-932

Conversation

@anakrish

@anakrish Anand Krishnamoorthi (anakrish) commented Nov 10, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Fixes #932.

  • Use one shared propagation policy so strict runtime errors survive rule evaluation, while ordinary failed conditions remain recoverable.
  • Return typed errors for conflicting complete-rule/function outputs regardless of strict mode, and preserve cleanup and terminal error state across nested and resumed execution.
  • Preserve Rego total ordering for mixed-type relational comparisons instead of treating valid comparisons as strict errors.
  • Bound value-bearing error messages, execution-state diagnostics, and HostAwait diagnostics, including invalid resumes after terminal errors and the FFI execution-state getter.
  • Check every complete-rule/function yield before updating the result, including same-body loop iterations. Both per-yield and cross-definition comparisons observe configured resource limits.
  • Evaluate independent definitions even when their static outputs agree. Preserve explicit else short-circuiting and the interpreter's legitimate first-witness shortcut within simple constant-head rule bodies; functions, variable heads, and inner comprehensions retain their distinct interpreter behavior.

Non-strict recovery otherwise remains unchanged.

Validation

Fresh debug builds only, with at most two build jobs:

  • cargo test --locked --test mod -j2 rule_errors -- --nocapture: 2 tests passed; 119 YAML scenarios exercised all three VM paths (357 executions). Two non-strict nested-function-conflict scenarios explicitly document the interpreter's existing incorrect success behavior.
  • Focused compiled shared-DAG regressions cover strict errors, terminal-state formatting, invalid resumes, HostAwait diagnostics, and interruptible equal-output comparisons across rules, functions, and definitions. New guard bytecode also survives binary round-tripping.
  • Default debug CI library tests: 407 passed, 2 ignored; main integration target: 211 passed. Documentation tests: 92 passed, 2 ignored.
  • All-features RVM library tests: 107 passed; RVM integration tests: 102 passed.
  • FFI RVM tests: 37 passed with --test-threads=1, including the actual bounded execution-state getter. Serial execution avoids interference between process-global memory-limit tests observed in an earlier parallel run. The existing nested-budget test now accumulates many bounded diagnostics rather than relying on one oversized diagnostic.
  • cargo xtask ci-debug --frozen passed with RUSTFLAGS='--force-warn deprecated_in_future', including all-features builds, no-default-feature tests, Azure RBAC tests, 2,875 OPA cases, and all six feature-combination checks.
  • Workspace and FFI formatting checks and git diff --check passed.

Unmodified Rust 1.97 debug CI remains blocked by two pre-existing deprecated_in_future errors at src/builtins/azure_policy/template_functions_encoding.rs:177,182. cargo xtask clippy reports seven existing errors: those two deprecations and five unrelated Clippy findings. No source suppressions or unrelated lint fixes were added.

Compatibility

Adds VmError::RuleOutputConflict and GuardMode::RuleOutput; downstream exhaustive matches may need updating. The new guard variant is appended, preserving existing guard discriminants. Recompile previously serialized policies to obtain the new per-yield checks; legacy early_exit_on_first_success metadata remains serialized but is no longer honored at runtime.

Diagnostic formatting summarizes containers instead of recursively expanding their contents. Non-value error-state variant names remain unchanged.

Follow-up implementation addresses the source-review findings and adds the missing regressions; this description no longer claims that the earlier review was clean. The PR remains a draft.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4a5b6b76-47ed-4d59-8b05-4cca089961b9

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved critical VM issues allow exponential error formatting, hide rule-body errors, and miss conflicting outputs.

3 open issues
What changed in this PR

Updates Regorus’s RVM error handling to address #932 and improve consistency with the interpreter, while preserving ordinary non-strict recovery.

Changes:

  • Shares rule-error propagation logic and adds typed errors for conflicting complete-rule and function outputs.
  • Records terminal failures across initial and resumed execution while supporting VM reuse.
  • Preserves mixed-type relational ordering and expands differential and lifecycle coverage across all three execution paths.
File Description
tests/​value_walk_safety.rs Uses logical operations to test bounded error diagnostics.
tests/​value_serialization_limits.rs Updates malformed-option expectations for strict mode.
tests/​rvm/​vm/​suites/​type_errors.yaml Expects mixed-type ordering in both strictness modes.
tests/​rvm/​vm/​suites/​function_calls.yaml Expects errors for conflicting function outputs.
tests/​rvm/​vm/​suites/​call_rule.yaml Covers conflicts and strict body/destructuring errors.
tests/​rvm/​rego/​mod.rs Adds three-mode execution and aligned interpreter strictness.
tests/​rvm/​rego/​cases/​rule_errors.yaml Adds differential error, conflict, recovery, and ordering cases.
tests/​rvm/​compiler.rs Tests typed conflicts, terminal failures, and VM reuse.
tests/​explanation_corpus/​cases/​rules.yaml Removes obsolete conflict-divergence expectations.
tests/​explanation_corpus/​cases/​calls_values_and_secrets.yaml Removes error divergences and aligns division-error matching.
src/​value/​tests.rs Updates deep-value comparison and diagnostic checks.
src/​rvm/​vm/​state.rs Records terminal suspendable error states.
src/​rvm/​vm/​rules.rs Centralizes propagation, cleanup, and output-conflict handling.
src/​rvm/​vm/​execution.rs Applies shared propagation policy during suspendable execution.
src/​rvm/​vm/​execution_model.rs Removes obsolete conflict-tracking state.
src/​rvm/​vm/​errors.rs Adds VmError::RuleOutputConflict.
src/​rvm/​vm/​dispatch.rs Removes strict-mode mixed-type comparison rejection.
src/​rvm/​vm/​comparison.rs Expands ordering and undefined-propagation tests.
docs/​rvm/​vm-runtime.md Documents propagation, terminal failures, and subsequent VM execution.

Review effort: Max

Comment thread src/rvm/vm/rules.rs Resolved
Comment thread src/rvm/vm/rules.rs Resolved
Comment thread src/rvm/vm/rules.rs Resolved
Preserve constant-head witness short-circuiting without skipping fallible independent definitions, and keep complete/function yield comparisons resource-aware.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4a5b6b76-47ed-4d59-8b05-4cca089961b9

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved critical defects allow cached programs and suspendable lookups to miss rule conflicts, while comparison traversal and error diagnostics remain insufficiently bounded by execution limits.

5 open issues
3 resolved since last review

Review effort: Max

/// Assert not undefined — fail (return undefined) if register is undefined.
NotUndefined,
/// Assert a defined complete-rule output agrees with earlier yields in this body.
RuleOutput,
Comment thread src/rvm/vm/comparison.rs
Comment on lines +109 to +110
if !self.needs_checked_comparison() {
return Ok(compare_unchecked(left, right, equality));
Comment thread src/rvm/vm/errors.rs
| VmError::InvalidModulo { .. }
| VmError::InvalidIteration { .. }
| VmError::HostAwaitResponseMissing { .. } => core::fmt::Display::fmt(self.0, f),
_ => core::fmt::Debug::fmt(self.0, f),
Comment thread src/rvm/vm/rules.rs
// no need to verify consistency with the rest.
if rule_info.early_exit_on_first_success {
break 'outer;
match self.jump_to(*body_entry_point) {
Comment thread src/rvm/vm/execution.rs
// Returning Ok(false) causes the caller to clear execution_stack and
// propagate the error, terminating the evaluation entirely.
if self.is_fatal_vm_error(&err) {
if self.should_propagate_rule_error(&err) {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RVM: errors and conflicts in rule bodies evaluate to undefined, even with strict builtin errors

2 participants