feat: Implement Rego else block compilation

- teach the Rego compiler to compile else chains correctly
- test suite

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
Anand Krishnamoorthi
2025-12-05 12:26:26 -06:00
parent a232b13e50
commit 9fa8036ce4
3 changed files with 154 additions and 15 deletions

View File

@@ -471,6 +471,20 @@ impl<'a> Compiler<'a> {
::core::convert::identity(body_idx);
let previous_value_expr = self
.context_stack
.last()
.and_then(|ctx| ctx.value_expr.clone());
let mut body_value_expr =
body.assign.as_ref().map(|assign| assign.value.clone());
if body_value_expr.is_none() && body_idx == 0 {
body_value_expr = previous_value_expr.clone();
}
if let Some(context) = self.context_stack.last_mut() {
context.value_expr = body_value_expr.clone();
}
self.emit_instruction(
Instruction::RuleInit {
result_reg: result_register,
@@ -481,23 +495,23 @@ impl<'a> Compiler<'a> {
if !body.query.stmts.is_empty() {
self.compile_query(&body.query)?;
} else {
let value_expr_opt =
self.context_stack.last().unwrap().value_expr.clone();
if let Some(value_expr) = value_expr_opt {
let value_reg = self.compile_rego_expr(&value_expr)?;
self.emit_instruction(
Instruction::Move {
dest: result_register,
src: value_reg,
},
value_expr.span(),
);
}
} else if let Some(value_expr) = body_value_expr.clone() {
let value_reg = self.compile_rego_expr(&value_expr)?;
self.emit_instruction(
Instruction::Move {
dest: result_register,
src: value_reg,
},
value_expr.span(),
);
}
self.emit_instruction(Instruction::RuleReturn {}, &body.span);
if let Some(context) = self.context_stack.last_mut() {
context.value_expr = previous_value_expr;
}
self.pop_scope();
}
}

View File

@@ -26,7 +26,6 @@ const OPA_TODO_FOLDERS: &[&str] = &[
"baseandvirtualdocs",
"dataderef",
"defaultkeyword",
"elsekeyword",
"every",
"fix1863",
"functions",
@@ -34,7 +33,7 @@ const OPA_TODO_FOLDERS: &[&str] = &[
"partialobjectdoc",
"planner-ir",
"refheads",
"virtualdocs",
"type",
"walkbuiltin",
// RVM Compiler does not support 'with' keyword yet.
"withkeyword",

View File

@@ -0,0 +1,126 @@
# Rego Else Rules Test Suite
# Exercises compiler support for else bodies, including assignment overrides and boolean fallback logic.
cases:
- note: else_rule_short_circuit
modules:
- |
package test
decision := 1 if {
1 == 1
}
else := 2 if {
1 == 1
}
query: data.test.decision
want_result: 1
- note: else_rule_fallback
modules:
- |
package test
decision := 1 if {
1 == 2
}
else := 2 if {
1 == 2
}
else := 3 if {
1 == 1
}
query: data.test.decision
want_result: 3
- note: else_rule_assignment_only
modules:
- |
package test
decision := 1 if {
1 == 2
}
else := 99
query: data.test.decision
want_result: 99
- note: else_rule_no_assignment_boolean
input:
method: "POST"
modules:
- |
package test
allow if {
input.method == "GET"
}
else if {
input.method == "POST"
}
query: data.test.allow
want_result: true
- note: else_rule_multiple_definitions
modules:
- |
package test
decision := "first" if {
false
}
else := "first-else" if {
false
}
decision := "second" if {
false
}
else := "second-else" if {
true
}
query: data.test.decision
want_result: "second-else"
- note: else_rule_function_fallback
modules:
- |
package test
f(x) := "small" if {
x < 5
}
else := "medium" if {
x < 10
}
f(x) := "large" if {
x >= 10
}
result := f(8)
query: data.test.result
want_result: "medium"
- note: else_rule_multiple_defined_single
modules:
- |
package ex
multiple_defined := false if {
false
}
else if {
true
}
else := false
query: data.ex.multiple_defined
want_result: true
- note: else_rule_boolean_middle_then_assignment
modules:
- |
package corner
corner_case := 7 if {
false
}
else := 6 if {
false
} else if {
true
}
else := 99
query: data.corner.corner_case
want_result: true