use smallvec::{smallvec, SmallVec};
use crate::graph::region::Region;
use super::{
ValId, ValueEnum, ValueData, Value, ValueDesc,
error::{ValueError, IncomparableRegions, RegionAlreadyFused, RegionError},
expr::SMALL_SEXPR_SIZE
};
pub const SMALL_RESULT_SIZE: usize = SMALL_SEXPR_SIZE;
#[derive(Debug, Clone, PartialEq, Hash, Eq)]
pub struct Lambda {
region: Region,
results: SmallVec<[ValId; SMALL_RESULT_SIZE]>
}
impl Lambda {
pub fn new(region: Region, results: SmallVec<[ValId; SMALL_RESULT_SIZE]>)
-> Result<Lambda, RegionError> {
{
let mut params = region.params_mut();
if params.fused() { return Err(RegionAlreadyFused.into()) }
for result in results.iter() {
let result_region = &result.data().region;
if !(result_region >= ®ion) {
let err = IncomparableRegions(
smallvec![region.downgrade(), result_region.clone()]
);
return Err(err.into())
}
}
params.fuse();
}
Ok(Lambda { region, results })
}
pub fn region(&self) -> &Region { &self.region }
pub fn results(&self) -> &[ValId] { &self.results }
pub fn dependencies(&self) -> std::slice::Iter<ValId> { self.results().iter() }
}
impl From<Lambda> for ValueEnum {
fn from(lambda: Lambda) -> ValueEnum { ValueEnum::Lambda(lambda) }
}
impl From<Lambda> for ValueData {
fn from(lambda: Lambda) -> ValueData {
let region = lambda.region().parent.clone();
ValueData::with_region(lambda.into(), region)
}
}
impl From<Lambda> for ValId {
fn from(lambda: Lambda) -> ValId {
ValId::try_new(ValueData::from(lambda)).expect("Impossible")
}
}
impl ValueDesc for Lambda {
type Err = ValueError;
}
impl Value for Lambda {}
#[cfg(test)]
mod tests {
use super::*;
use crate::value::primitive::logical::Bool;
use crate::assert_jeq;
#[test]
fn invalid_nested_lambda_result_fails() {
let region = Region::new();
let nested_region = Region::new_in(region.downgrade());
let (iy, y) = nested_region.add_with_ty(Bool.into());
assert_eq!(iy, 0);
let err = Lambda::new(region.clone(), smallvec![y.clone()])
.expect_err("This is an invalid function");
match err {
RegionError::IncomparableRegions(err) => {
assert!(
err.0.as_slice() == &[region.downgrade(), nested_region.downgrade()]
|| err.0.as_slice() == &[nested_region.downgrade(), region.downgrade()]
)
},
err => panic!("Wrong error {:?} (expected incomparable regions)", err)
}
}
#[test]
fn lambda_on_fused_region_fails() {
let region = Region::new();
{ region.params_mut().fuse(); }
let t = ValId::from(true);
let err = Lambda::new(region.clone(), smallvec![t.clone()])
.expect_err("This function's region is invalid");
match err {
RegionError::RegionAlreadyFused(_) => {},
err => panic!("Wrong error {:?} (expected region already fused)", err)
}
}
#[test]
fn constant_bool_lambda() {
let region = Region::new();
let t = ValId::from(true);
let lambda = Lambda::new(region.clone(), smallvec![t.clone()])
.expect("This is a valid function");
assert_eq!(lambda.region(), ®ion);
assert_eq!(lambda.results().len(), 1);
assert_jeq!(lambda.results()[0], &t);
let lambda = ValId::from(lambda);
let _ = lambda;
}
#[test]
fn identity_bool_lambda() {
let region = Region::new();
let (_, x) = region.add_with_ty(Bool.into());
let lambda = Lambda::new(region.clone(), smallvec![x.clone()])
.expect("This is a valid function");
assert_eq!(lambda.region(), ®ion);
assert_eq!(lambda.results().len(), 1);
assert_eq!(lambda.results()[0], x);
let lambda = ValId::from(lambda);
let _ = lambda;
}
}