use oxilean_kernel::{BinderInfo, Declaration, Environment, Expr, Level, Name};
use super::types::{
AsyncSessionEndpoint, BaseType, ChoreographyEngine, ChoreographyStep, DeadlockChecker, GType,
GradualSessionMonitor, LType, Message, MonitorResult, ProbBranch, ProbSessionScheduler, Role,
SType, SessionChecker, SessionEndpoint, SessionOp, SessionSubtypeChecker,
};
pub fn app(f: Expr, a: Expr) -> Expr {
Expr::App(Box::new(f), Box::new(a))
}
pub fn app2(f: Expr, a: Expr, b: Expr) -> Expr {
app(app(f, a), b)
}
pub fn app3(f: Expr, a: Expr, b: Expr, c: Expr) -> Expr {
app(app2(f, a, b), c)
}
pub fn cst(s: &str) -> Expr {
Expr::Const(Name::str(s), vec![])
}
pub fn prop() -> Expr {
Expr::Sort(Level::zero())
}
pub fn type0() -> Expr {
Expr::Sort(Level::succ(Level::zero()))
}
pub fn pi(bi: BinderInfo, name: &str, dom: Expr, body: Expr) -> Expr {
Expr::Pi(bi, Name::str(name), Box::new(dom), Box::new(body))
}
pub fn arrow(a: Expr, b: Expr) -> Expr {
pi(BinderInfo::Default, "_", a, b)
}
pub fn impl_pi(name: &str, dom: Expr, body: Expr) -> Expr {
pi(BinderInfo::Implicit, name, dom, body)
}
pub fn bvar(n: u32) -> Expr {
Expr::BVar(n)
}
pub fn nat_ty() -> Expr {
cst("Nat")
}
pub fn bool_ty() -> Expr {
cst("Bool")
}
pub fn list_ty(elem: Expr) -> Expr {
app(cst("List"), elem)
}
pub fn string_ty() -> Expr {
cst("String")
}
pub fn stype_ty() -> Expr {
type0()
}
pub fn send_ty() -> Expr {
arrow(type0(), arrow(cst("SType"), cst("SType")))
}
pub fn recv_ty() -> Expr {
arrow(type0(), arrow(cst("SType"), cst("SType")))
}
pub fn end_ty() -> Expr {
cst("SType")
}
pub fn schoice_ty() -> Expr {
arrow(cst("SType"), arrow(cst("SType"), cst("SType")))
}
pub fn sbranch_ty() -> Expr {
arrow(cst("SType"), arrow(cst("SType"), cst("SType")))
}
pub fn srec_ty() -> Expr {
arrow(arrow(cst("SType"), cst("SType")), cst("SType"))
}
pub fn svar_ty() -> Expr {
arrow(nat_ty(), cst("SType"))
}
pub fn dual_ty() -> Expr {
arrow(cst("SType"), cst("SType"))
}
pub fn dual_involutive_ty() -> Expr {
pi(
BinderInfo::Default,
"S",
cst("SType"),
app2(
cst("Eq"),
app(cst("dual"), app(cst("dual"), bvar(0))),
bvar(0),
),
)
}
pub fn dual_send_ty() -> Expr {
pi(
BinderInfo::Default,
"T",
type0(),
pi(
BinderInfo::Default,
"S",
cst("SType"),
app2(
cst("Eq"),
app(cst("dual"), app2(cst("Send"), bvar(1), bvar(0))),
app2(cst("Recv"), bvar(1), app(cst("dual"), bvar(0))),
),
),
)
}
pub fn dual_end_ty() -> Expr {
app2(cst("Eq"), app(cst("dual"), cst("End")), cst("End"))
}
pub fn channel_ty() -> Expr {
arrow(cst("SType"), type0())
}
pub fn send_msg_ty() -> Expr {
impl_pi(
"T",
type0(),
impl_pi(
"S",
cst("SType"),
arrow(
app(cst("Channel"), app2(cst("Send"), bvar(1), bvar(0))),
arrow(bvar(2), app(cst("Channel"), bvar(1))),
),
),
)
}
pub fn recv_msg_ty() -> Expr {
impl_pi(
"T",
type0(),
impl_pi(
"S",
cst("SType"),
arrow(
app(cst("Channel"), app2(cst("Recv"), bvar(1), bvar(0))),
app2(cst("Prod"), bvar(2), app(cst("Channel"), bvar(1))),
),
),
)
}
pub fn close_ty() -> Expr {
arrow(app(cst("Channel"), cst("End")), cst("Unit"))
}
pub fn linear_type_ty() -> Expr {
arrow(type0(), prop())
}
pub fn linear_ctx_ty() -> Expr {
type0()
}
pub fn ctx_split_ty() -> Expr {
arrow(
cst("LinearCtx"),
arrow(cst("LinearCtx"), arrow(cst("LinearCtx"), prop())),
)
}
pub fn linear_well_typed_ty() -> Expr {
arrow(
cst("LinearCtx"),
arrow(type0(), arrow(cst("SType"), prop())),
)
}
pub fn linear_safety_ty() -> Expr {
arrow(type0(), prop())
}
pub fn affine_type_ty() -> Expr {
arrow(type0(), prop())
}
pub fn unrestricted_type_ty() -> Expr {
arrow(type0(), prop())
}
pub fn role_ty() -> Expr {
type0()
}
pub fn gtype_ty() -> Expr {
type0()
}
pub fn gcomm_ty() -> Expr {
arrow(
cst("Role"),
arrow(
cst("Role"),
arrow(type0(), arrow(cst("GType"), cst("GType"))),
),
)
}
pub fn gchoice_ty() -> Expr {
arrow(
cst("Role"),
arrow(
cst("Role"),
arrow(cst("GType"), arrow(cst("GType"), cst("GType"))),
),
)
}
pub fn gend_ty() -> Expr {
cst("GType")
}
pub fn grec_ty() -> Expr {
arrow(arrow(cst("GType"), cst("GType")), cst("GType"))
}
pub fn well_formed_ty() -> Expr {
arrow(cst("GType"), prop())
}
pub fn participants_ty() -> Expr {
arrow(cst("GType"), list_ty(cst("Role")))
}
pub fn ltype_ty() -> Expr {
type0()
}
pub fn lsend_ty() -> Expr {
arrow(
cst("Role"),
arrow(type0(), arrow(cst("LType"), cst("LType"))),
)
}
pub fn lrecv_ty() -> Expr {
arrow(
cst("Role"),
arrow(type0(), arrow(cst("LType"), cst("LType"))),
)
}
pub fn lchoice_ty() -> Expr {
arrow(
cst("Role"),
arrow(cst("LType"), arrow(cst("LType"), cst("LType"))),
)
}
pub fn lbranch_ty() -> Expr {
arrow(
cst("Role"),
arrow(cst("LType"), arrow(cst("LType"), cst("LType"))),
)
}
pub fn lend_ty() -> Expr {
cst("LType")
}
pub fn project_ty() -> Expr {
arrow(cst("GType"), arrow(cst("Role"), cst("LType")))
}
pub fn projection_sound_ty() -> Expr {
pi(
BinderInfo::Default,
"G",
cst("GType"),
pi(
BinderInfo::Default,
"p",
cst("Role"),
arrow(
app(cst("WellFormed"), bvar(1)),
app(
cst("LocallyConsistent"),
app2(cst("project"), bvar(2), bvar(1)),
),
),
),
)
}
pub fn locally_consistent_ty() -> Expr {
arrow(cst("LType"), prop())
}
pub fn mergeability_ty() -> Expr {
arrow(cst("LType"), arrow(cst("LType"), prop()))
}
pub fn process_ty() -> Expr {
type0()
}
pub fn reduces_ty() -> Expr {
arrow(type0(), arrow(type0(), prop()))
}
pub fn stuck_ty() -> Expr {
arrow(type0(), prop())
}
pub fn deadlock_free_ty() -> Expr {
arrow(type0(), prop())
}
pub fn progress_ty() -> Expr {
arrow(type0(), arrow(cst("SType"), prop()))
}
pub fn subject_reduction_ty() -> Expr {
prop()
}
pub fn type_safety_ty() -> Expr {
prop()
}
pub fn deadlock_freedom_thm_ty() -> Expr {
pi(
BinderInfo::Default,
"P",
type0(),
pi(
BinderInfo::Default,
"S",
cst("SType"),
arrow(
app2(cst("WellTypedProcess"), bvar(1), bvar(0)),
app(cst("DeadlockFree"), bvar(2)),
),
),
)
}
pub fn well_typed_process_ty() -> Expr {
arrow(type0(), arrow(cst("SType"), prop()))
}
pub fn progress_thm_ty() -> Expr {
pi(
BinderInfo::Default,
"P",
type0(),
pi(
BinderInfo::Default,
"S",
cst("SType"),
arrow(
app2(cst("WellTypedProcess"), bvar(1), bvar(0)),
app2(
cst("Or"),
app(cst("IsValue"), bvar(2)),
app(cst("Exists"), app(cst("Reduces"), bvar(3))),
),
),
),
)
}
pub fn is_value_ty() -> Expr {
arrow(type0(), prop())
}
pub fn infer_stype_ty() -> Expr {
arrow(type0(), arrow(cst("SType"), prop()))
}
pub fn principal_stype_ty() -> Expr {
arrow(type0(), arrow(cst("SType"), prop()))
}
pub fn stype_unify_ty() -> Expr {
arrow(cst("SType"), arrow(cst("SType"), arrow(type0(), prop())))
}
pub fn stype_subst_ty() -> Expr {
type0()
}
pub fn inference_complete_ty() -> Expr {
prop()
}
pub fn inference_sound_ty() -> Expr {
prop()
}
pub fn dep_stype_ty() -> Expr {
type0()
}
pub fn dep_send_ty() -> Expr {
impl_pi(
"T",
type0(),
arrow(arrow(bvar(0), cst("DepSType")), cst("DepSType")),
)
}
pub fn dep_recv_ty() -> Expr {
impl_pi(
"T",
type0(),
arrow(arrow(bvar(0), cst("DepSType")), cst("DepSType")),
)
}
pub fn dep_end_ty() -> Expr {
cst("DepSType")
}
pub fn dep_dual_ty() -> Expr {
arrow(cst("DepSType"), cst("DepSType"))
}
pub fn dep_channel_ty() -> Expr {
arrow(cst("DepSType"), type0())
}
pub fn dep_send_msg_ty() -> Expr {
arrow(cst("DepSType"), arrow(type0(), arrow(type0(), type0())))
}
pub fn unfold_ty() -> Expr {
arrow(cst("SType"), cst("SType"))
}
pub fn fold_ty() -> Expr {
arrow(cst("SType"), cst("SType"))
}
pub fn iso_recursive_ty() -> Expr {
arrow(cst("SType"), prop())
}
pub fn equi_recursive_ty() -> Expr {
arrow(cst("SType"), prop())
}
pub fn coinductive_ty() -> Expr {
arrow(cst("SType"), prop())
}
pub fn stype_equiv_ty() -> Expr {
arrow(cst("SType"), arrow(cst("SType"), prop()))
}
pub fn unfold_preserves_dual_ty() -> Expr {
pi(
BinderInfo::Default,
"S",
cst("SType"),
app2(
cst("Eq"),
app(cst("dual"), app(cst("Unfold"), bvar(0))),
app(cst("Unfold"), app(cst("dual"), bvar(0))),
),
)
}
pub fn async_stype_ty() -> Expr {
type0()
}
pub fn async_send_ty() -> Expr {
arrow(type0(), arrow(cst("AsyncSType"), cst("AsyncSType")))
}
pub fn async_recv_ty() -> Expr {
arrow(type0(), arrow(cst("AsyncSType"), cst("AsyncSType")))
}
pub fn eventual_delivery_ty() -> Expr {
arrow(cst("AsyncSType"), prop())
}
pub fn buffer_bound_ty() -> Expr {
arrow(cst("AsyncSType"), arrow(nat_ty(), prop()))
}
pub fn async_dual_ty() -> Expr {
arrow(cst("AsyncSType"), cst("AsyncSType"))
}
pub fn async_end_ty() -> Expr {
cst("AsyncSType")
}
pub fn ll_prop_ty() -> Expr {
type0()
}
pub fn tensor_ty() -> Expr {
arrow(cst("LLProp"), arrow(cst("LLProp"), cst("LLProp")))
}
pub fn lolli_ty() -> Expr {
arrow(cst("LLProp"), arrow(cst("LLProp"), cst("LLProp")))
}
pub fn par_ty() -> Expr {
arrow(cst("LLProp"), arrow(cst("LLProp"), cst("LLProp")))
}
pub fn bang_ty() -> Expr {
arrow(cst("LLProp"), cst("LLProp"))
}
pub fn why_not_ty() -> Expr {
arrow(cst("LLProp"), cst("LLProp"))
}
pub fn cut_elim_ty() -> Expr {
prop()
}
pub fn curry_howard_ll_ty() -> Expr {
arrow(cst("LLProp"), arrow(cst("SType"), prop()))
}
pub fn ssubtype_ty() -> Expr {
arrow(cst("SType"), arrow(cst("SType"), prop()))
}
pub fn ssubtype_refl_ty() -> Expr {
pi(
BinderInfo::Default,
"S",
cst("SType"),
app2(cst("SSubtype"), bvar(0), bvar(0)),
)
}
pub fn ssubtype_trans_ty() -> Expr {
pi(
BinderInfo::Default,
"S1",
cst("SType"),
pi(
BinderInfo::Default,
"S2",
cst("SType"),
pi(
BinderInfo::Default,
"S3",
cst("SType"),
arrow(
app2(cst("SSubtype"), bvar(2), bvar(1)),
arrow(
app2(cst("SSubtype"), bvar(2), bvar(1)),
app2(cst("SSubtype"), bvar(4), bvar(2)),
),
),
),
),
)
}
pub fn send_covariant_ty() -> Expr {
pi(
BinderInfo::Default,
"T",
type0(),
pi(
BinderInfo::Default,
"S1",
cst("SType"),
pi(
BinderInfo::Default,
"S2",
cst("SType"),
arrow(
app2(cst("SSubtype"), bvar(1), bvar(0)),
app2(
cst("SSubtype"),
app2(cst("Send"), bvar(2), bvar(2)),
app2(cst("Send"), bvar(2), bvar(1)),
),
),
),
),
)
}
pub fn recv_contravariant_ty() -> Expr {
pi(
BinderInfo::Default,
"T",
type0(),
pi(
BinderInfo::Default,
"S1",
cst("SType"),
pi(
BinderInfo::Default,
"S2",
cst("SType"),
arrow(
app2(cst("SSubtype"), bvar(1), bvar(0)),
app2(
cst("SSubtype"),
app2(cst("Recv"), bvar(2), bvar(1)),
app2(cst("Recv"), bvar(2), bvar(2)),
),
),
),
),
)
}
pub fn ho_stype_ty() -> Expr {
type0()
}
pub fn send_chan_ty() -> Expr {
arrow(cst("SType"), arrow(cst("HOSType"), cst("HOSType")))
}
pub fn recv_chan_ty() -> Expr {
arrow(cst("SType"), arrow(cst("HOSType"), cst("HOSType")))
}
pub fn session_passing_ty() -> Expr {
arrow(cst("HOSType"), prop())
}
pub fn ho_dual_ty() -> Expr {
arrow(cst("HOSType"), cst("HOSType"))
}
pub fn ex_stype_ty() -> Expr {
type0()
}
pub fn throw_ty() -> Expr {
arrow(type0(), cst("ExSType"))
}
pub fn catch_ty() -> Expr {
arrow(cst("ExSType"), arrow(cst("ExSType"), cst("ExSType")))
}
pub fn exception_safe_ty() -> Expr {
arrow(cst("ExSType"), prop())
}
pub fn fault_tolerant_ty() -> Expr {
arrow(cst("ExSType"), prop())
}
pub fn g_stype_ty() -> Expr {
type0()
}
pub fn gradual_send_ty() -> Expr {
arrow(type0(), arrow(cst("GSType"), cst("GSType")))
}
pub fn gradual_recv_ty() -> Expr {
arrow(type0(), arrow(cst("GSType"), cst("GSType")))
}
pub fn dyn_stype_ty() -> Expr {
cst("GSType")
}
pub fn cast_insertion_ty() -> Expr {
arrow(
cst("GSType"),
arrow(cst("SType"), arrow(cst("GSType"), prop())),
)
}
pub fn dynamic_monitor_ty() -> Expr {
arrow(cst("GSType"), prop())
}
pub fn gradual_consistency_ty() -> Expr {
arrow(cst("GSType"), arrow(cst("GSType"), prop()))
}
pub fn prob_stype_ty() -> Expr {
type0()
}
pub fn prob_choice_ty() -> Expr {
arrow(
cst("SType"),
arrow(cst("SType"), arrow(cst("Real"), cst("ProbSType"))),
)
}
pub fn expected_cost_ty() -> Expr {
arrow(cst("ProbSType"), arrow(nat_ty(), prop()))
}
pub fn markov_chain_protocol_ty() -> Expr {
arrow(cst("ProbSType"), prop())
}
pub fn prob_termination_ty() -> Expr {
arrow(cst("ProbSType"), prop())
}
pub fn choreography_ty() -> Expr {
type0()
}
pub fn realization_ty() -> Expr {
arrow(cst("Choreography"), arrow(cst("GType"), prop()))
}
pub fn global_view_ty() -> Expr {
arrow(cst("GType"), cst("Choreography"))
}
pub fn endpoint_projection_ty() -> Expr {
arrow(cst("Choreography"), arrow(cst("Role"), cst("LType")))
}
pub fn choreography_consistency_ty() -> Expr {
arrow(cst("Choreography"), prop())
}
pub fn ambience_compatibility_ty() -> Expr {
arrow(cst("Choreography"), arrow(cst("GType"), prop()))
}
pub fn shared_mem_type_ty() -> Expr {
type0()
}
pub fn mutex_ty() -> Expr {
arrow(type0(), cst("SharedMemType"))
}
pub fn acquire_ty() -> Expr {
arrow(cst("SharedMemType"), arrow(cst("SType"), cst("SType")))
}
pub fn release_ty() -> Expr {
arrow(cst("SharedMemType"), arrow(cst("SType"), cst("SType")))
}
pub fn data_race_free_ty() -> Expr {
arrow(cst("SharedMemType"), prop())
}
pub fn build_session_types_env(env: &mut Environment) -> Result<(), String> {
let axioms: &[(&str, Expr)] = &[
("SType", stype_ty()),
("Send", send_ty()),
("Recv", recv_ty()),
("End", end_ty()),
("SChoice", schoice_ty()),
("SBranch", sbranch_ty()),
("SRec", srec_ty()),
("SVar", svar_ty()),
("dual", dual_ty()),
("dual_involutive", dual_involutive_ty()),
("dual_send", dual_send_ty()),
("dual_end", dual_end_ty()),
("Channel", channel_ty()),
("send_msg", send_msg_ty()),
("recv_msg", recv_msg_ty()),
("close", close_ty()),
("LinearType", linear_type_ty()),
("LinearCtx", linear_ctx_ty()),
("CtxSplit", ctx_split_ty()),
("LinearWellTyped", linear_well_typed_ty()),
("LinearSafety", linear_safety_ty()),
("AffineType", affine_type_ty()),
("UnrestrictedType", unrestricted_type_ty()),
("Role", role_ty()),
("GType", gtype_ty()),
("GComm", gcomm_ty()),
("GChoice", gchoice_ty()),
("GEnd", gend_ty()),
("GRec", grec_ty()),
("WellFormed", well_formed_ty()),
("Participants", participants_ty()),
("LType", ltype_ty()),
("LSend", lsend_ty()),
("LRecv", lrecv_ty()),
("LChoice", lchoice_ty()),
("LBranch", lbranch_ty()),
("LEnd", lend_ty()),
("project", project_ty()),
("LocallyConsistent", locally_consistent_ty()),
("Mergeability", mergeability_ty()),
("ProjectionSound", projection_sound_ty()),
("Process", process_ty()),
("Reduces", reduces_ty()),
("Stuck", stuck_ty()),
("DeadlockFree", deadlock_free_ty()),
("Progress", progress_ty()),
("SubjectReduction", subject_reduction_ty()),
("TypeSafety", type_safety_ty()),
("WellTypedProcess", well_typed_process_ty()),
("DeadlockFreedomThm", deadlock_freedom_thm_ty()),
("ProgressThm", progress_thm_ty()),
("IsValue", is_value_ty()),
("InferSType", infer_stype_ty()),
("PrincipalSType", principal_stype_ty()),
("STypeUnify", stype_unify_ty()),
("STypeSubst", stype_subst_ty()),
("InferenceComplete", inference_complete_ty()),
("InferenceSound", inference_sound_ty()),
("DepSType", dep_stype_ty()),
("DepSend", dep_send_ty()),
("DepRecv", dep_recv_ty()),
("DepEnd", dep_end_ty()),
("DepDual", dep_dual_ty()),
("DepChannel", dep_channel_ty()),
("DepSendMsg", dep_send_msg_ty()),
("Unfold", unfold_ty()),
("Fold", fold_ty()),
("IsoRecursive", iso_recursive_ty()),
("EquiRecursive", equi_recursive_ty()),
("Coinductive", coinductive_ty()),
("STypeEquiv", stype_equiv_ty()),
("UnfoldPreservesDual", unfold_preserves_dual_ty()),
("AsyncSType", async_stype_ty()),
("AsyncSend", async_send_ty()),
("AsyncRecv", async_recv_ty()),
("EventualDelivery", eventual_delivery_ty()),
("BufferBound", buffer_bound_ty()),
("AsyncDual", async_dual_ty()),
("AsyncEnd", async_end_ty()),
("LLProp", ll_prop_ty()),
("Tensor", tensor_ty()),
("Lolli", lolli_ty()),
("Par", par_ty()),
("Bang", bang_ty()),
("WhyNot", why_not_ty()),
("CutElim", cut_elim_ty()),
("CurryHowardLL", curry_howard_ll_ty()),
("SSubtype", ssubtype_ty()),
("SSubtypeRefl", ssubtype_refl_ty()),
("SSubtypeTrans", ssubtype_trans_ty()),
("SendCovariant", send_covariant_ty()),
("RecvContravariant", recv_contravariant_ty()),
("HOSType", ho_stype_ty()),
("SendChan", send_chan_ty()),
("RecvChan", recv_chan_ty()),
("SessionPassing", session_passing_ty()),
("HODual", ho_dual_ty()),
("ExSType", ex_stype_ty()),
("Throw", throw_ty()),
("Catch", catch_ty()),
("ExceptionSafe", exception_safe_ty()),
("FaultTolerant", fault_tolerant_ty()),
("GSType", g_stype_ty()),
("GradualSend", gradual_send_ty()),
("GradualRecv", gradual_recv_ty()),
("DynSType", dyn_stype_ty()),
("CastInsertion", cast_insertion_ty()),
("DynamicMonitor", dynamic_monitor_ty()),
("GradualConsistency", gradual_consistency_ty()),
("ProbSType", prob_stype_ty()),
("ProbChoice", prob_choice_ty()),
("ExpectedCost", expected_cost_ty()),
("MarkovChainProtocol", markov_chain_protocol_ty()),
("ProbTermination", prob_termination_ty()),
("Choreography", choreography_ty()),
("Realization", realization_ty()),
("GlobalView", global_view_ty()),
("EndpointProjection", endpoint_projection_ty()),
("ChoreographyConsistency", choreography_consistency_ty()),
("AmbienceCompatibility", ambience_compatibility_ty()),
("SharedMemType", shared_mem_type_ty()),
("Mutex", mutex_ty()),
("Acquire", acquire_ty()),
("Release", release_ty()),
("DataRaceFree", data_race_free_ty()),
];
for (name, ty) in axioms {
env.add(Declaration::Axiom {
name: Name::str(*name),
univ_params: vec![],
ty: ty.clone(),
})
.ok();
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use oxilean_kernel::{Environment, Name};
#[test]
fn test_build_env_registers_axioms() {
let mut env = Environment::new();
build_session_types_env(&mut env).expect("build should succeed");
assert!(env.get(&Name::str("SType")).is_some(), "SType missing");
assert!(env.get(&Name::str("Send")).is_some(), "Send missing");
assert!(env.get(&Name::str("Recv")).is_some(), "Recv missing");
assert!(env.get(&Name::str("dual")).is_some(), "dual missing");
assert!(env.get(&Name::str("GType")).is_some(), "GType missing");
assert!(env.get(&Name::str("project")).is_some(), "project missing");
assert!(
env.get(&Name::str("DeadlockFree")).is_some(),
"DeadlockFree missing"
);
assert!(
env.get(&Name::str("DepSType")).is_some(),
"DepSType missing"
);
}
#[test]
fn test_dual_involutive() {
let st = SType::Send(
Box::new(BaseType::Nat),
Box::new(SType::Recv(Box::new(BaseType::Bool), Box::new(SType::End))),
);
let d = st.dual();
let dd = d.dual();
assert_eq!(st, dd, "dual should be involutive");
}
#[test]
fn test_dual_swaps_send_recv() {
let send = SType::Send(Box::new(BaseType::Nat), Box::new(SType::End));
assert!(matches!(send.dual(), SType::Recv(_, _)));
let recv = SType::Recv(Box::new(BaseType::Bool), Box::new(SType::End));
assert!(matches!(recv.dual(), SType::Send(_, _)));
}
#[test]
fn test_dual_choice() {
let choice = SType::Choice(Box::new(SType::End), Box::new(SType::End));
let d = choice.dual();
assert!(matches!(d, SType::Branch(_, _)));
let branch = SType::Branch(Box::new(SType::End), Box::new(SType::End));
assert!(matches!(branch.dual(), SType::Choice(_, _)));
}
#[test]
fn test_session_endpoint_send_recv() {
let st = SType::Send(
Box::new(BaseType::Nat),
Box::new(SType::Recv(Box::new(BaseType::Nat), Box::new(SType::End))),
);
let mut ep = SessionEndpoint::new(st);
ep.send(Message::Nat(42)).expect("send should succeed");
assert!(ep.remaining.is_recv());
}
#[test]
fn test_session_type_checker() {
let st = SType::Send(
Box::new(BaseType::Nat),
Box::new(SType::Recv(Box::new(BaseType::Bool), Box::new(SType::End))),
);
let mut checker = SessionChecker::new();
checker.register_channel("ch", st);
let ops = vec![
SessionOp::Send(BaseType::Nat),
SessionOp::Recv(BaseType::Bool),
SessionOp::Close,
];
let result = checker.check_usage("ch", &ops);
assert!(
result.is_ok(),
"Well-typed usage should type check: {:?}",
result
);
assert_eq!(result.expect("result should be valid"), SType::End);
}
#[test]
fn test_global_type_projection() {
let alice = Role::new("Alice");
let bob = Role::new("Bob");
let global = GType::Comm {
sender: alice.clone(),
receiver: bob.clone(),
msg_ty: BaseType::Nat,
cont: Box::new(GType::End),
};
let alice_proj = global.project(&alice);
let bob_proj = global.project(&bob);
assert!(
matches!(alice_proj, LType::Send(_, _, _)),
"Alice should have LSend"
);
assert!(
matches!(bob_proj, LType::Recv(_, _, _)),
"Bob should have LRecv"
);
}
#[test]
fn test_deadlock_checker() {
let mut checker = DeadlockChecker::new();
checker.add_wait("ch1", "A", "B");
checker.add_wait("ch2", "B", "C");
assert!(checker.is_deadlock_free(), "Should be deadlock-free");
checker.add_wait("ch3", "C", "A");
assert!(
!checker.is_deadlock_free(),
"Cycle: should NOT be deadlock-free"
);
}
#[test]
fn test_recursive_session_unfold() {
let nat_stream = SType::Rec(
"X".to_string(),
Box::new(SType::Send(
Box::new(BaseType::Nat),
Box::new(SType::Var("X".to_string())),
)),
);
let unfolded = nat_stream.unfold();
assert!(unfolded.is_send());
}
#[test]
fn test_new_axioms_registered() {
let mut env = Environment::new();
build_session_types_env(&mut env).expect("build should succeed");
assert!(env.get(&Name::str("AsyncSType")).is_some());
assert!(env.get(&Name::str("AsyncSend")).is_some());
assert!(env.get(&Name::str("EventualDelivery")).is_some());
assert!(env.get(&Name::str("BufferBound")).is_some());
assert!(env.get(&Name::str("AsyncDual")).is_some());
assert!(env.get(&Name::str("LLProp")).is_some());
assert!(env.get(&Name::str("Tensor")).is_some());
assert!(env.get(&Name::str("Lolli")).is_some());
assert!(env.get(&Name::str("Bang")).is_some());
assert!(env.get(&Name::str("CutElim")).is_some());
assert!(env.get(&Name::str("CurryHowardLL")).is_some());
assert!(env.get(&Name::str("SSubtype")).is_some());
assert!(env.get(&Name::str("SSubtypeRefl")).is_some());
assert!(env.get(&Name::str("SendCovariant")).is_some());
assert!(env.get(&Name::str("RecvContravariant")).is_some());
assert!(env.get(&Name::str("HOSType")).is_some());
assert!(env.get(&Name::str("SendChan")).is_some());
assert!(env.get(&Name::str("SessionPassing")).is_some());
assert!(env.get(&Name::str("ExSType")).is_some());
assert!(env.get(&Name::str("Throw")).is_some());
assert!(env.get(&Name::str("Catch")).is_some());
assert!(env.get(&Name::str("FaultTolerant")).is_some());
assert!(env.get(&Name::str("GSType")).is_some());
assert!(env.get(&Name::str("DynSType")).is_some());
assert!(env.get(&Name::str("CastInsertion")).is_some());
assert!(env.get(&Name::str("GradualConsistency")).is_some());
assert!(env.get(&Name::str("ProbSType")).is_some());
assert!(env.get(&Name::str("ProbChoice")).is_some());
assert!(env.get(&Name::str("MarkovChainProtocol")).is_some());
assert!(env.get(&Name::str("ProbTermination")).is_some());
assert!(env.get(&Name::str("Choreography")).is_some());
assert!(env.get(&Name::str("Realization")).is_some());
assert!(env.get(&Name::str("EndpointProjection")).is_some());
assert!(env.get(&Name::str("ChoreographyConsistency")).is_some());
assert!(env.get(&Name::str("SharedMemType")).is_some());
assert!(env.get(&Name::str("Mutex")).is_some());
assert!(env.get(&Name::str("Acquire")).is_some());
assert!(env.get(&Name::str("DataRaceFree")).is_some());
}
#[test]
fn test_async_endpoint_send_buffered() {
let st = SType::Send(Box::new(BaseType::Nat), Box::new(SType::End));
let mut ep = AsyncSessionEndpoint::new(st);
ep.async_send(Message::Nat(7))
.expect("async_send should succeed");
assert_eq!(ep.outbox_len(), 1, "message should be in outbox");
assert!(
ep.remaining == SType::End,
"remaining should advance to End"
);
}
#[test]
fn test_async_endpoint_flush_and_recv() {
let sender_st = SType::Send(Box::new(BaseType::Nat), Box::new(SType::End));
let recv_st = SType::Recv(Box::new(BaseType::Nat), Box::new(SType::End));
let mut sender = AsyncSessionEndpoint::new(sender_st);
let mut receiver = AsyncSessionEndpoint::new(recv_st);
sender.async_send(Message::Nat(42)).expect("send ok");
let flushed = sender.flush_to(&mut receiver);
assert_eq!(flushed, 1);
assert_eq!(receiver.inbox_len(), 1);
let msg = receiver.async_recv().expect("recv ok");
assert!(matches!(msg, Message::Nat(42)));
}
#[test]
fn test_subtype_reflexivity() {
let mut checker = SessionSubtypeChecker::new();
let st = SType::Send(Box::new(BaseType::Nat), Box::new(SType::End));
assert!(
checker.is_subtype(&st, &st),
"every type is a subtype of itself"
);
}
#[test]
fn test_subtype_end() {
let mut checker = SessionSubtypeChecker::new();
assert!(checker.is_subtype(&SType::End, &SType::End));
}
#[test]
fn test_subtype_incompatible() {
let mut checker = SessionSubtypeChecker::new();
let send = SType::Send(Box::new(BaseType::Nat), Box::new(SType::End));
let recv = SType::Recv(Box::new(BaseType::Nat), Box::new(SType::End));
assert!(
!checker.is_subtype(&send, &recv),
"Send is not subtype of Recv"
);
}
#[test]
fn test_prob_scheduler_probabilities_sum_to_one() {
let branches = vec![
ProbBranch {
label: "A".into(),
weight: 1.0,
cont: SType::End,
},
ProbBranch {
label: "B".into(),
weight: 3.0,
cont: SType::End,
},
];
let sched = ProbSessionScheduler::new(branches);
let probs = sched.probabilities();
let sum: f64 = probs.iter().sum();
assert!((sum - 1.0).abs() < 1e-9, "probabilities should sum to 1");
}
#[test]
fn test_prob_scheduler_greedy_choice() {
let branches = vec![
ProbBranch {
label: "low".into(),
weight: 1.0,
cont: SType::End,
},
ProbBranch {
label: "high".into(),
weight: 9.0,
cont: SType::End,
},
];
let sched = ProbSessionScheduler::new(branches);
assert_eq!(
sched.greedy_choice(),
Some(1),
"highest weight branch is index 1"
);
}
#[test]
fn test_choreography_engine_simple() {
let alice = Role::new("Alice");
let bob = Role::new("Bob");
let global = GType::Comm {
sender: alice.clone(),
receiver: bob.clone(),
msg_ty: BaseType::Nat,
cont: Box::new(GType::End),
};
let mut engine = ChoreographyEngine::new();
engine.execute(&global).expect("execution should succeed");
assert_eq!(engine.comm_count(), 1, "one communication step");
assert!(matches!(engine.trace[0], ChoreographyStep::Comm { .. }));
assert!(matches!(engine.trace[1], ChoreographyStep::End));
}
#[test]
fn test_monitor_exact_match() {
let st = SType::Send(Box::new(BaseType::Nat), Box::new(SType::End));
let mut monitor = GradualSessionMonitor::new(st);
let result = monitor.check_send(&BaseType::Nat);
assert_eq!(result, MonitorResult::Ok);
assert!(monitor.is_safe());
assert!(monitor.casts.is_empty());
}
#[test]
fn test_monitor_type_mismatch_inserts_cast() {
let st = SType::Send(Box::new(BaseType::Nat), Box::new(SType::End));
let mut monitor = GradualSessionMonitor::new(st);
let result = monitor.check_send(&BaseType::Bool);
assert!(matches!(result, MonitorResult::CastInserted(_)));
assert!(!monitor.casts.is_empty());
}
#[test]
fn test_monitor_wrong_direction_failure() {
let st = SType::Recv(Box::new(BaseType::Nat), Box::new(SType::End));
let mut monitor = GradualSessionMonitor::new(st);
let result = monitor.check_send(&BaseType::Nat);
assert!(matches!(result, MonitorResult::Failure(_)));
assert!(!monitor.is_safe());
}
}