bug: RVM compiler misclassifies bracket-syntax default rules (config["key"]) as PartialObject
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 346
- Forks
- 75
- Avg merge
- 1d 3h
- Merged PRs (30d)
- 15
Description
Summary
Rules using bracket-syntax with defaults (e.g., default config["timeout"] := 30) are misclassified by the RVM compiler as PartialObject instead of Complete. This causes incorrect evaluation when querying the specific rule path.
Reproduction
package test
default config["timeout"] := 30
config["timeout"] := 60 if {
false
}
Expected
| Query | Expected Result |
|---|---|
data.test.config.timeout |
30 |
data.test.config |
{"timeout": 30} |
data.test |
{"config": {"timeout": 30}} |
Actual
| Query | Interpreter | RVM |
|---|---|---|
data.test.config.timeout |
{} (wrong) |
{} (wrong) |
data.test.config |
{"timeout": 30} ✓ |
N/A (package query) |
data.test |
{"config": {"timeout": 30}} ✓ |
N/A (package query) |
Both the interpreter and RVM return incorrect results for the specific rule query data.test.config.timeout. Only the interpreter's package-level evaluation produces the correct result.
Root Cause
In compute_rule_type() (src/languages/rego/compiler/rules.rs), the rule type classification matches RefBrack with assignment as PartialObject:
RuleHead::Compr { refr, assign, .. } => match refr.as_ref() {
crate::ast::Expr::RefBrack { .. } if assign.is_some() => {
RuleType::PartialObject // ← incorrect for literal keys
}
crate::ast::Expr::RefBrack { .. } => RuleType::PartialSet,
_ => RuleType::Complete,
},
The pattern config["timeout"] := 30 is parsed as RefBrack { index: Expr::String("timeout"), .. } with an assignment. The current code treats all RefBrack + assignment as PartialObject, but only variable keys produce partial objects. A string literal key is semantically equivalent to config.timeout — a complete rule assigned to a specific object key.
The distinction:
config[key] := valuewherekeyisExpr::Var→ PartialObject (dynamic key, generates entries for multiple keys)config["timeout"] := valuewhere"timeout"isExpr::String→ Complete (static key, equivalent to dot access)
Proposed Fix
In compute_rule_type(), inspect the RefBrack.index expression to distinguish literal keys from variable keys:
RuleHead::Compr { refr, assign, .. } => match refr.as_ref() {
crate::ast::Expr::RefBrack { index, .. } if assign.is_some() => {
match index.as_ref() {
// Literal key like config["timeout"] — treated as complete rule
crate::ast::Expr::String { .. }
| crate::ast::Expr::RawString { .. }
| crate::ast::Expr::Number { .. } => RuleType::Complete,
// Variable key like config[key] — partial object
_ => RuleType::PartialObject,
}
}
crate::ast::Expr::RefBrack { .. } => RuleType::PartialSet,
_ => RuleType::Complete,
},
This same fix should also be verified in the interpreter's eval_rule_in_path code path, which currently also returns {} for the specific query.
The skipped test case default_rule_with_object_key in tests/rvm/rego/cases/default_rules.yaml validates this scenario and should be unskipped after the fix.
Impact
Low — this pattern is uncommon in practice.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in src/languages/rego/compiler/rules.rs at compute_rule_type(), then inspect the interpreter's eval_rule_in_path path. Run the skipped default_rule_with_object_key case in tests/rvm/rego/cases/default_rules.yaml; done means the test is enabled and bracket-syntax defaults return the expected specific rule value in both execution paths.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 74/100