Skip to content
1 change: 1 addition & 0 deletions src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1782,6 +1782,7 @@ impl Interpreter {

let mut comps = self.eval_rule_ref(&rule_ref)?;
if let Some(ke) = &key_expr {
is_const_rule = is_const_rule && Self::is_simple_literal(ke)?;
comps.push(self.eval_expr(ke)?);
}
let output = if let Some(oe) = &output_expr {
Expand Down
6 changes: 6 additions & 0 deletions src/languages/rego/compiler/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ pub enum CompilerError {
#[error("Invalid function expression with package")]
InvalidFunctionExpressionWithPackage,

#[error("partial object rules with constant keys are not yet supported by the RVM compiler")]
PartialObjectConstantKeyUnsupported,

#[error("partial object rules with nested bracket keys are not yet supported by the RVM compiler")]
PartialObjectNestedKeyUnsupported,

#[error("Compilation error: {message}")]
General { message: String },
}
Expand Down
48 changes: 47 additions & 1 deletion src/languages/rego/compiler/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ impl<'a> Compiler<'a> {
crate::ast::Expr::RefBrack { .. } if assign.is_some() => {
RuleType::PartialObject
}
crate::ast::Expr::RefBrack { .. } => RuleType::PartialSet,
crate::ast::Expr::RefBrack { .. } => RuleType::PartialObject,
_ => RuleType::Complete,
Comment on lines 58 to 63
},
_ => RuleType::Complete,
Expand Down Expand Up @@ -88,6 +88,48 @@ impl<'a> Compiler<'a> {
})
}

fn validate_partial_object_shape(&self, refr: &ExprRef) -> Result<()> {
let Expr::RefBrack {
refr: prefix,
index,
..
} = refr.as_ref()
else {
return Ok(());
};

if Self::has_dynamic_bracket(prefix) {
return Err(CompilerError::PartialObjectNestedKeyUnsupported.at(refr.span()));
}

if Self::is_simple_literal(index) {
return Err(CompilerError::PartialObjectConstantKeyUnsupported.at(index.span()));
}

Ok(())
}

Comment on lines +92 to +111
fn has_dynamic_bracket(expr: &ExprRef) -> bool {
match expr.as_ref() {
Expr::RefBrack { refr, index, .. } => {
!Self::is_simple_literal(index) || Self::has_dynamic_bracket(refr)
}
Expr::RefDot { refr, .. } => Self::has_dynamic_bracket(refr),
_ => false,
}
}

fn is_simple_literal(expr: &ExprRef) -> bool {
matches!(
expr.as_ref(),
Expr::String { .. }
| Expr::RawString { .. }
| Expr::Number { .. }
| Expr::Bool { .. }
| Expr::Null { .. }
)
}

pub(super) fn get_or_assign_rule_index(&mut self, rule_path: &str) -> Result<u16> {
if let Some(&index) = self.rule_index_map.get(rule_path) {
return Ok(index);
Expand Down Expand Up @@ -345,6 +387,10 @@ impl<'a> Compiler<'a> {

let (key_expr, value_expr) = match head {
RuleHead::Compr { refr, assign, .. } => {
if rule_type == RuleType::PartialObject {
self.validate_partial_object_shape(refr)?;
}

self.rule_definition_function_params[rule_index as usize].push(None);
self.rule_definition_destructuring_patterns[rule_index as usize]
.push(None);
Expand Down
91 changes: 91 additions & 0 deletions tests/interpreter/cases/rule/partial_object_v1.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# 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
}
Comment on lines +4 to +16
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
Loading