use crate::context::{Context, MutualInductive};
use crate::error::{KernelError, KernelResult};
use crate::term::Term;
pub struct NestedDecl {
pub name: String,
pub sort: Term,
pub constructors: Vec<(String, Term)>,
}
#[derive(Debug, Clone)]
pub struct IsoNames {
pub container: String,
pub sibling: String,
pub to_generic: String,
pub from_generic: String,
}
pub struct Compiled {
pub block: Vec<MutualInductive>,
pub isos: Vec<(String, Term, Term)>,
pub iso_names: Vec<IsoNames>,
pub siblings: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct NestedInfo {
pub siblings: Vec<String>,
pub isos: Vec<IsoNames>,
}
fn g(n: &str) -> Term {
Term::Global(n.to_string())
}
fn v(n: &str) -> Term {
Term::Var(n.to_string())
}
fn app(f: Term, x: Term) -> Term {
Term::App(Box::new(f), Box::new(x))
}
fn apps(f: Term, xs: Vec<Term>) -> Term {
xs.into_iter().fold(f, app)
}
fn pi(p: &str, t: Term, b: Term) -> Term {
Term::Pi { param: p.to_string(), param_type: Box::new(t), body_type: Box::new(b) }
}
fn arrow(a: Term, b: Term) -> Term {
pi("_", a, b)
}
fn lam(p: &str, t: Term, b: Term) -> Term {
Term::Lambda { param: p.to_string(), param_type: Box::new(t), body: Box::new(b) }
}
enum ArgKind {
Elem,
Recursive,
Other(Term),
}
fn peel_pis(t: &Term) -> (Vec<(String, Term)>, Term) {
let mut params = Vec::new();
let mut cur = t.clone();
while let Term::Pi { param, param_type, body_type } = cur {
params.push((param, *param_type));
cur = *body_type;
}
(params, cur)
}
fn is_global(t: &Term, name: &str) -> bool {
matches!(t, Term::Global(n) if n == name)
}
fn analyze_container_ctor(ctor_ty: &Term, container: &str) -> KernelResult<(String, Vec<ArgKind>)> {
let (params, _residual) = peel_pis(ctor_ty);
if params.is_empty() {
return Err(KernelError::CertificationError(format!(
"nested-compile: container '{container}' constructor has no element parameter"
)));
}
let a = params[0].0.clone();
let mut kinds = Vec::new();
for (_, ty) in ¶ms[1..] {
let kind = match ty {
Term::Var(n) if *n == a => ArgKind::Elem,
Term::App(f, x) if is_global(f, container) && matches!(x.as_ref(), Term::Var(n) if *n == a) => {
ArgKind::Recursive
}
_ => ArgKind::Other(ty.clone()),
};
kinds.push(kind);
}
Ok((a, kinds))
}
fn occurs_anywhere(ty: &Term, name: &str) -> bool {
match ty {
Term::Global(n) => n == name,
Term::App(f, x) => occurs_anywhere(f, name) || occurs_anywhere(x, name),
Term::Pi { param_type, body_type, .. } => {
occurs_anywhere(param_type, name) || occurs_anywhere(body_type, name)
}
Term::Lambda { param_type, body, .. } => {
occurs_anywhere(param_type, name) || occurs_anywhere(body, name)
}
_ => false,
}
}
fn is_nested_container_type(ctx: &Context, decl: &NestedDecl, ty: &Term) -> bool {
let mut head = ty;
while let Term::App(f, _) = head {
head = f;
}
matches!(head, Term::Global(c) if c != &decl.name && ctx.is_inductive(c))
&& occurs_anywhere(ty, &decl.name)
}
struct Spec {
repr: String,
generic: Term,
to: Option<String>,
from: Option<String>,
}
struct SibNode {
container: String,
repr: String,
elem_repr: String,
elem_generic: Term,
inner_to: Option<String>,
inner_from: Option<String>,
ctors: Vec<(String, Vec<ArgKind>)>,
to_name: String,
from_name: String,
}
fn specialize(
ctx: &Context,
decl: &NestedDecl,
ty: &Term,
nodes: &mut Vec<SibNode>,
seen: &mut std::collections::HashSet<String>,
) -> KernelResult<Spec> {
if is_global(ty, &decl.name) {
return Ok(Spec { repr: decl.name.clone(), generic: g(&decl.name), to: None, from: None });
}
if let Term::App(f, inner_ty) = ty {
if let Term::Global(c) = f.as_ref() {
if c != &decl.name && ctx.is_inductive(c) {
let inner = specialize(ctx, decl, inner_ty, nodes, seen)?;
let repr = format!("{}${}", inner.repr, c);
let generic = app(g(c), inner.generic.clone());
let to_name = format!("{repr}_to_{c}");
let from_name = format!("{repr}_from_{c}");
if seen.insert(repr.clone()) {
let mut ctors = Vec::new();
for (orig_ctor, orig_ty) in ctx.get_constructors(c) {
let (_a, kinds) = analyze_container_ctor(orig_ty, c)?;
if kinds.iter().any(|k| matches!(k, ArgKind::Other(_))) {
return Err(KernelError::CertificationError(format!(
"nested-compile: container '{c}' constructor '{orig_ctor}' has an \
argument that is neither the element nor a recursive occurrence \
— specializing it is not supported (only pure containers like \
`List`/`TList`)"
)));
}
ctors.push((orig_ctor.to_string(), kinds));
}
nodes.push(SibNode {
container: c.clone(),
repr: repr.clone(),
elem_repr: inner.repr.clone(),
elem_generic: inner.generic.clone(),
inner_to: inner.to.clone(),
inner_from: inner.from.clone(),
ctors,
to_name: to_name.clone(),
from_name: from_name.clone(),
});
}
return Ok(Spec { repr, generic, to: Some(to_name), from: Some(from_name) });
}
}
}
Err(KernelError::CertificationError(format!(
"nested-compile: '{}' occurs in argument type {ty} in a position that is not a nesting \
inside a registered unary container (only `Container …` nestings are specialized)",
decl.name
)))
}
pub fn compile_nested(ctx: &Context, decl: &NestedDecl) -> KernelResult<Compiled> {
if decl.sort != Term::Sort(crate::term::Universe::Type(0)) {
return Err(KernelError::CertificationError(format!(
"nested-compile: '{}' must be a `Type 0` inductive (specialized siblings are \
`Type 0`); higher-universe nesting is not supported",
decl.name
)));
}
let mut nodes: Vec<SibNode> = Vec::new();
let mut seen = std::collections::HashSet::new();
let mut own_ctors = Vec::new();
for (cname, cty) in &decl.constructors {
let (params, residual) = peel_pis(cty);
let mut rebuilt = residual;
for (pname, pty) in params.into_iter().rev() {
let pty2 = if is_nested_container_type(ctx, decl, &pty) {
g(&specialize(ctx, decl, &pty, &mut nodes, &mut seen)?.repr)
} else {
pty
};
rebuilt = pi(&pname, pty2, rebuilt);
}
own_ctors.push((cname.clone(), rebuilt));
}
if nodes.is_empty() {
return Err(KernelError::CertificationError(format!(
"nested-compile: '{}' has no nested container occurrence — register it directly",
decl.name
)));
}
let mut block = vec![MutualInductive {
name: decl.name.clone(),
sort: decl.sort.clone(),
num_params: 0,
constructors: own_ctors,
}];
let mut siblings = Vec::new();
for node in &nodes {
siblings.push(node.repr.clone());
let mut sib_ctors = Vec::new();
for (orig, kinds) in &node.ctors {
let mut sty = g(&node.repr);
for kind in kinds.iter().rev() {
let arg_ty = match kind {
ArgKind::Elem => g(&node.elem_repr),
ArgKind::Recursive => g(&node.repr),
ArgKind::Other(t) => t.clone(),
};
sty = arrow(arg_ty, sty);
}
sib_ctors.push((format!("{}_{}", node.repr, orig), sty));
}
block.push(MutualInductive {
name: node.repr.clone(),
sort: Term::Sort(crate::term::Universe::Type(0)),
num_params: 0,
constructors: sib_ctors,
});
}
let mut isos = Vec::new();
let mut iso_names = Vec::new();
for node in &nodes {
let to_ty = arrow(g(&node.repr), app(g(&node.container), node.elem_generic.clone()));
let from_ty = arrow(app(g(&node.container), node.elem_generic.clone()), g(&node.repr));
isos.push((node.to_name.clone(), to_ty, build_to_generic(node)));
isos.push((node.from_name.clone(), from_ty, build_from_generic(node)));
iso_names.push(IsoNames {
container: node.container.clone(),
sibling: node.repr.clone(),
to_generic: node.to_name.clone(),
from_generic: node.from_name.clone(),
});
}
Ok(Compiled { block, isos, iso_names, siblings })
}
fn build_to_generic(node: &SibNode) -> Term {
let mut cases = Vec::new();
for (orig_ctor, kinds) in &node.ctors {
let mut body = apps(g(orig_ctor), vec![node.elem_generic.clone()]);
for (i, kind) in kinds.iter().enumerate() {
body = app(body, convert_to(kind, v(&format!("a{i}")), "rec", &node.inner_to));
}
for (i, kind) in kinds.iter().enumerate().rev() {
let ty = match kind {
ArgKind::Elem => g(&node.elem_repr),
ArgKind::Recursive => g(&node.repr),
ArgKind::Other(t) => t.clone(),
};
body = lam(&format!("a{i}"), ty, body);
}
cases.push(body);
}
Term::Fix {
name: "rec".to_string(),
body: Box::new(lam(
"x",
g(&node.repr),
Term::Match {
discriminant: Box::new(v("x")),
motive: Box::new(lam("_", g(&node.repr), app(g(&node.container), node.elem_generic.clone()))),
cases,
},
)),
}
}
fn build_from_generic(node: &SibNode) -> Term {
let mut cases = Vec::new();
for (orig_ctor, kinds) in &node.ctors {
let mut body = g(&format!("{}_{}", node.repr, orig_ctor));
for (i, kind) in kinds.iter().enumerate() {
body = app(body, convert_from(kind, v(&format!("a{i}")), "rec", &node.inner_from));
}
for (i, kind) in kinds.iter().enumerate().rev() {
let ty = match kind {
ArgKind::Elem => node.elem_generic.clone(),
ArgKind::Recursive => app(g(&node.container), node.elem_generic.clone()),
ArgKind::Other(t) => t.clone(),
};
body = lam(&format!("a{i}"), ty, body);
}
cases.push(body);
}
Term::Fix {
name: "rec".to_string(),
body: Box::new(lam(
"x",
app(g(&node.container), node.elem_generic.clone()),
Term::Match {
discriminant: Box::new(v("x")),
motive: Box::new(lam("_", app(g(&node.container), node.elem_generic.clone()), g(&node.repr))),
cases,
},
)),
}
}
fn convert_to(kind: &ArgKind, a: Term, rec: &str, inner_to: &Option<String>) -> Term {
match kind {
ArgKind::Recursive => app(v(rec), a),
ArgKind::Elem => match inner_to {
Some(iso) => app(g(iso), a),
None => a,
},
ArgKind::Other(_) => a,
}
}
fn convert_from(kind: &ArgKind, a: Term, rec: &str, inner_from: &Option<String>) -> Term {
match kind {
ArgKind::Recursive => app(v(rec), a),
ArgKind::Elem => match inner_from {
Some(iso) => app(g(iso), a),
None => a,
},
ArgKind::Other(_) => a,
}
}