use std::sync::Arc;
use sim_kernel::{CaseId, ClassId, Expr, FunctionId, PreparedArgs, Symbol, shape_is_subshape_of};
use crate::{
ClassShape, ExprKind, ExprKindShape, FunctionCase, FunctionObject, ListShape, ObjectExpr, Shape,
};
use super::{TestInstance, cx, general_case_impl, register_test_class, specific_case_impl};
#[test]
fn class_shape_with_self_referential_instance_shape_rejects_without_overflow() {
let mut cx = cx();
let symbol = Symbol::new("SelfRef");
register_test_class(&mut cx, ClassId(101), symbol.clone(), Vec::new(), true);
let shape = ClassShape::new(symbol.clone());
let expr = ObjectExpr {
class: symbol,
fields: vec![(Symbol::new("x"), Expr::Bool(true))],
}
.to_expr();
let matched = shape.check_expr(&mut cx, &expr).unwrap();
assert!(!matched.accepted);
}
#[test]
fn class_shape_two_cycle_hierarchy_terminates() {
let mut cx = cx();
let a = Symbol::new("CycleA");
let b = Symbol::new("CycleB");
let unrelated = Symbol::new("Unrelated");
register_test_class(&mut cx, ClassId(111), a.clone(), vec![b.clone()], false);
register_test_class(&mut cx, ClassId(112), b.clone(), vec![a.clone()], false);
register_test_class(&mut cx, ClassId(113), unrelated.clone(), Vec::new(), false);
let shape_a = ClassShape::new(a);
let shape_b = ClassShape::new(b.clone());
let shape_unrelated = ClassShape::new(unrelated);
assert!(shape_is_subshape_of(&mut cx, &shape_a, &shape_b).unwrap());
assert!(!shape_is_subshape_of(&mut cx, &shape_a, &shape_unrelated).unwrap());
let number = ExprKindShape::new(ExprKind::Number);
assert!(!shape_is_subshape_of(&mut cx, &shape_a, &number).unwrap());
}
#[test]
fn select_case_prefers_proven_more_specific_overload() {
let mut cx = cx();
let parent = Symbol::new("DispatchParent");
let child = Symbol::new("DispatchChild");
register_test_class(&mut cx, ClassId(121), parent.clone(), Vec::new(), false);
register_test_class(
&mut cx,
ClassId(122),
child.clone(),
vec![parent.clone()],
false,
);
let general = FunctionCase {
id: CaseId(1),
name: Symbol::qualified("case", "general"),
args: Arc::new(ListShape::new(vec![Arc::new(ClassShape::new(parent))])),
result: None,
demand: Vec::new(),
priority: 0,
implementation: general_case_impl,
};
let specific = FunctionCase {
id: CaseId(2),
name: Symbol::qualified("case", "specific"),
args: Arc::new(ListShape::new(vec![Arc::new(ClassShape::new(
child.clone(),
))])),
result: None,
demand: Vec::new(),
priority: 0,
implementation: specific_case_impl,
};
let function = FunctionObject::new(
FunctionId(9),
Symbol::qualified("test", "dispatch"),
vec![general, specific],
);
let instance = cx
.factory()
.opaque(Arc::new(TestInstance {
class_symbol: child,
}))
.unwrap();
let prepared = PreparedArgs::new(vec![instance]);
let selected = function
.select_case(&mut cx, &prepared)
.expect("more-specific overload should be selected, not ambiguous");
assert_eq!(selected.case.id, CaseId(2));
}