use crate::validate::{err, ValidationError};
pub const DEFAULT_MAX_CALL_DEPTH: usize = 32;
pub const DEFAULT_MAX_NESTING_DEPTH: usize = 64;
pub const DEFAULT_MAX_NODE_COUNT: usize = 100_000;
pub const DEFAULT_MAX_EXPR_DEPTH: usize = 1_024;
#[derive(Debug, Default)]
pub struct LimitState {
pub node_count: usize,
pub nesting_reported: bool,
pub node_count_reported: bool,
}
#[inline]
pub fn check_limits(limits: &mut LimitState, depth: usize, errors: &mut Vec<ValidationError>) {
limits.node_count = limits.node_count.saturating_add(1);
if limits.node_count > DEFAULT_MAX_NODE_COUNT && !limits.node_count_reported {
limits.node_count_reported = true;
errors.push(err(format!(
"V019: program has more than {DEFAULT_MAX_NODE_COUNT} statement nodes. Fix: split the program into smaller kernels or run an optimization pass before lowering."
)));
}
if depth > DEFAULT_MAX_NESTING_DEPTH && !limits.nesting_reported {
limits.nesting_reported = true;
errors.push(err(format!(
"V018: program nesting depth {depth} exceeds max {DEFAULT_MAX_NESTING_DEPTH}. Fix: flatten nested If/Loop/Block structures or split the program before lowering."
)));
}
}
#[inline]
#[must_use]
pub fn check_expr_depth(depth: usize, errors: &mut Vec<ValidationError>) -> bool {
if depth > DEFAULT_MAX_EXPR_DEPTH {
errors.push(err(format!(
"V033: expression nesting depth {depth} exceeds max {DEFAULT_MAX_EXPR_DEPTH}. Fix: split the expression into intermediate let-bindings before lowering."
)));
return false;
}
true
}
#[inline]
#[must_use]
pub fn max_call_depth(op_id: &str, depth: usize) -> Result<usize, usize> {
let _ = op_id;
if depth > DEFAULT_MAX_CALL_DEPTH {
return Err(depth);
}
Ok(depth)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn check_limits_within_bounds_no_error() {
let mut limits = LimitState::default();
let mut errors = Vec::new();
check_limits(&mut limits, 5, &mut errors);
assert!(errors.is_empty());
assert_eq!(limits.node_count, 1);
}
#[test]
fn check_limits_nesting_depth_overflow() {
let mut limits = LimitState::default();
let mut errors = Vec::new();
check_limits(&mut limits, DEFAULT_MAX_NESTING_DEPTH + 1, &mut errors);
assert_eq!(errors.len(), 1);
assert!(errors[0].message().contains("V018"));
}
#[test]
fn check_limits_nesting_only_reports_once() {
let mut limits = LimitState::default();
let mut errors = Vec::new();
check_limits(&mut limits, DEFAULT_MAX_NESTING_DEPTH + 1, &mut errors);
check_limits(&mut limits, DEFAULT_MAX_NESTING_DEPTH + 2, &mut errors);
assert_eq!(errors.len(), 1, "nesting should only be reported once");
}
#[test]
fn check_expr_depth_within_bounds() {
let mut errors = Vec::new();
assert!(check_expr_depth(100, &mut errors));
assert!(errors.is_empty());
}
#[test]
fn check_expr_depth_overflow() {
let mut errors = Vec::new();
assert!(!check_expr_depth(DEFAULT_MAX_EXPR_DEPTH + 1, &mut errors));
assert_eq!(errors.len(), 1);
assert!(errors[0].message().contains("V033"));
}
#[test]
fn max_call_depth_within_bounds() {
assert!(max_call_depth("my_op", 10).is_ok());
}
#[test]
fn max_call_depth_exceeded() {
let result = max_call_depth("my_op", DEFAULT_MAX_CALL_DEPTH + 1);
assert!(result.is_err());
}
}