iter 22b.1.1: ClassDef/InstanceDef AST + downstream match arms
Adds Def::Class(ClassDef) and Def::Instance(InstanceDef) variants per Decision 11 §"Form-A schema". All optional fields (superclass, doc, default body) carry skip_serializing_if so canonical-JSON bytes of pre-22b fixtures stay bit-identical. Downstream match-on-Def sites are extended with explicit Class/Instance arms. Behaviour for 22b.1 is placeholder (skip in typecheck/codegen, one-line summary in pretty/manifest, placeholder marker in prose/print) with TODO comments naming the deferred sub-iters (22b.2 typecheck, 22b.3 codegen, 22b.4 prose-projection arms).
This commit is contained in:
@@ -1007,6 +1007,14 @@ fn collect_refs(def: &ailang_core::Def) -> std::collections::BTreeSet<String> {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Iter 22b.1: `ail deps` does not yet trace references inside
|
||||
// class/instance defs. Walking method signatures and bodies
|
||||
// (default bodies, instance method bodies) lands in 22b.2 along
|
||||
// with the typecheck arms — until then a class/instance def
|
||||
// contributes no entries to the dependency graph. Tools that
|
||||
// call `collect_refs` (`ail deps`, the cross-module-fan-out
|
||||
// diagnostics) therefore treat them as leaves.
|
||||
ailang_core::Def::Class(_) | ailang_core::Def::Instance(_) => {}
|
||||
}
|
||||
out
|
||||
}
|
||||
@@ -1416,6 +1424,25 @@ fn def_summary(d: &ailang_core::Def) -> (&'static str, String, Vec<String>) {
|
||||
.join(" | ");
|
||||
("type", s, vec![])
|
||||
}
|
||||
// Iter 22b.1: a one-line `class C a` / `instance C T` summary
|
||||
// for `ail manifest`. The `effects` slot is empty for both —
|
||||
// class methods carry their own effect annotations on each
|
||||
// method signature, but the class itself does not (it is a
|
||||
// collection of method signatures, not a single function).
|
||||
ailang_core::Def::Class(c) => (
|
||||
"class",
|
||||
format!("{} {}", c.name, c.param),
|
||||
vec![],
|
||||
),
|
||||
ailang_core::Def::Instance(i) => (
|
||||
"instance",
|
||||
format!(
|
||||
"{} {}",
|
||||
i.class,
|
||||
ailang_core::pretty::type_to_string(&i.type_)
|
||||
),
|
||||
vec![],
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -751,6 +751,18 @@ pub fn check(m: &Module) -> Result<CheckedModule> {
|
||||
name: def.name().to_string(),
|
||||
args: vec![],
|
||||
},
|
||||
// Iter 22b.1: class/instance defs are not yet checked.
|
||||
// The symbols map is keyed by definition name; class/
|
||||
// instance contribute their class name, but with no
|
||||
// concrete pre-monomorphisation type we represent them
|
||||
// as a placeholder Con. Tools that consume this map
|
||||
// (`ail diff`, `ail manifest`) read kind separately
|
||||
// via `def_kind`, so the placeholder type does not
|
||||
// mislead them.
|
||||
Def::Class(_) | Def::Instance(_) => Type::Con {
|
||||
name: def.name().to_string(),
|
||||
args: vec![],
|
||||
},
|
||||
};
|
||||
symbols.insert(def.name().to_string(), (ty, h));
|
||||
}
|
||||
@@ -840,6 +852,10 @@ fn build_module_globals(
|
||||
name: def_name.to_string(),
|
||||
args: vec![],
|
||||
},
|
||||
// Iter 22b.1: class/instance defs do not contribute
|
||||
// monomorphic globals to the workspace symbol table
|
||||
// before 22b.3 monomorphisation runs. Skip them here.
|
||||
Def::Class(_) | Def::Instance(_) => continue,
|
||||
};
|
||||
globals.insert(def_name.to_string(), ty);
|
||||
}
|
||||
@@ -944,6 +960,14 @@ fn check_def(def: &Def, env: &Env) -> Result<()> {
|
||||
Def::Fn(f) => check_fn(f, env),
|
||||
Def::Const(c) => check_const(c, env),
|
||||
Def::Type(td) => check_type_def(td, env),
|
||||
// Iter 22b.1: schema-only landing for class/instance defs.
|
||||
// Class-schema validation (KindMismatch, InvalidSuperclassParam,
|
||||
// ConstraintReferencesUnboundTypeVar) and instance-body
|
||||
// typechecking (with class-method substitution) land in 22b.2.
|
||||
// Workspace-load coherence checks (Orphan / Duplicate /
|
||||
// MissingMethod) already fire from `workspace::build_registry`,
|
||||
// so a malformed instance never reaches this point.
|
||||
Def::Class(_) | Def::Instance(_) => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -99,6 +99,11 @@ pub fn lift_letrecs(m: &Module) -> Result<Module> {
|
||||
name: def.name().to_string(),
|
||||
args: vec![],
|
||||
},
|
||||
// Iter 22b.1: skip class/instance defs in the global-type
|
||||
// collection. Class methods become globally typed names
|
||||
// only after 22b.3 monomorphisation; until then there is
|
||||
// no concrete `Type` to register here.
|
||||
Def::Class(_) | Def::Instance(_) => continue,
|
||||
};
|
||||
env.globals.insert(def.name().to_string(), ty);
|
||||
}
|
||||
@@ -186,6 +191,10 @@ pub fn lift_letrecs(m: &Module) -> Result<Module> {
|
||||
c.value = lifter.lift_in_term(&c.value, &mut locals, &c.name)?;
|
||||
}
|
||||
Def::Type(_) => {}
|
||||
// Iter 22b.1: class/instance defs do not enter the lift
|
||||
// pass yet. 22b.2 will lift method-body lambdas in instance
|
||||
// methods; for 22b.1 this is a passthrough.
|
||||
Def::Class(_) | Def::Instance(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -205,6 +205,11 @@ pub(crate) fn check_module(m: &Module) -> Vec<Diagnostic> {
|
||||
ctors.insert(name.clone(), fields.clone());
|
||||
}
|
||||
}
|
||||
// Iter 22b.1: class/instance defs are skipped here. Once
|
||||
// 22b.3 monomorphisation materialises class methods as
|
||||
// ordinary `FnDef`s, the lift's globals map will pick
|
||||
// them up automatically.
|
||||
Def::Class(_) | Def::Instance(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -118,6 +118,12 @@ pub fn infer_module(m: &Module) -> UniquenessTable {
|
||||
globals.insert(c.name.clone(), strip_forall(&c.ty).clone());
|
||||
}
|
||||
Def::Type(_) => {}
|
||||
// Iter 22b.1: class/instance defs are skipped here. Class
|
||||
// method signatures contribute global names once
|
||||
// monomorphisation (22b.3) materialises them; the
|
||||
// pre-monomorphisation form has no concrete `Type` to
|
||||
// hand to the uniqueness pass.
|
||||
Def::Class(_) | Def::Instance(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -765,6 +765,13 @@ impl<'a> Emitter<'a> {
|
||||
// logical type. Heap boxes are allocated ad hoc via
|
||||
// GC_malloc (Boehm conservative collector, Iter 14f).
|
||||
}
|
||||
// Iter 22b.1: class/instance defs do not emit IR yet.
|
||||
// 22b.3 monomorphisation will rewrite class-method
|
||||
// calls into calls against synthesised monomorphic
|
||||
// FnDefs; once that pass runs, class/instance bodies
|
||||
// never reach the emit path on their own — they only
|
||||
// appear inlined into the synthesised fns.
|
||||
Def::Class(_) | Def::Instance(_) => {}
|
||||
}
|
||||
}
|
||||
// Drain the monomorphisation queue. Specialised fns may
|
||||
|
||||
@@ -57,9 +57,16 @@ pub struct Import {
|
||||
|
||||
/// A top-level definition — the unit that gets a content hash.
|
||||
///
|
||||
/// Discriminated by the JSON `kind` tag: `"fn"`, `"const"`, or
|
||||
/// `"type"`. Use [`def_name`] / [`def_kind`] (or the [`Def::name`]
|
||||
/// method) to inspect generically without matching every variant.
|
||||
/// Discriminated by the JSON `kind` tag: `"fn"`, `"const"`, `"type"`,
|
||||
/// `"class"`, or `"instance"`. Use [`def_name`] / [`def_kind`] (or the
|
||||
/// [`Def::name`] method) to inspect generically without matching every
|
||||
/// variant.
|
||||
///
|
||||
/// Iter 22b.1 (Decision 11): adds `Class` and `Instance`. Both are
|
||||
/// additive — pre-22b fixtures never produce these tags, so their
|
||||
/// canonical-JSON bytes (and therefore their content hashes) are
|
||||
/// unchanged. The `Def`-level match exhaustiveness in downstream
|
||||
/// crates is the only caller that has to acknowledge the variants.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "kind", rename_all = "lowercase")]
|
||||
pub enum Def {
|
||||
@@ -69,15 +76,26 @@ pub enum Def {
|
||||
Const(ConstDef),
|
||||
/// Type (ADT) definition; see [`TypeDef`].
|
||||
Type(TypeDef),
|
||||
/// Iter 22b.1: typeclass declaration; see [`ClassDef`].
|
||||
Class(ClassDef),
|
||||
/// Iter 22b.1: instance declaration; see [`InstanceDef`].
|
||||
Instance(InstanceDef),
|
||||
}
|
||||
|
||||
impl Def {
|
||||
/// Source-level name of the definition, regardless of kind.
|
||||
///
|
||||
/// For `Def::Instance` the "name" is the class being instantiated;
|
||||
/// instances are not separately named at the source level. The
|
||||
/// workspace registry keys instances by `(class, type-hash)`, so
|
||||
/// the class name alone is the closest analogue of a "name".
|
||||
pub fn name(&self) -> &str {
|
||||
match self {
|
||||
Def::Fn(f) => &f.name,
|
||||
Def::Const(c) => &c.name,
|
||||
Def::Type(t) => &t.name,
|
||||
Def::Class(c) => &c.name,
|
||||
Def::Instance(i) => &i.class,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,7 +110,7 @@ pub fn def_name(def: &Def) -> &str {
|
||||
}
|
||||
|
||||
/// External helper: discriminator tag of a definition (`"fn"`,
|
||||
/// `"const"`, `"type"`).
|
||||
/// `"const"`, `"type"`, `"class"`, `"instance"`).
|
||||
///
|
||||
/// Identical to the `kind` field in the JSON representation. Useful
|
||||
/// for tools that group or filter definitions by kind.
|
||||
@@ -101,6 +119,8 @@ pub fn def_kind(def: &Def) -> &'static str {
|
||||
Def::Fn(_) => "fn",
|
||||
Def::Const(_) => "const",
|
||||
Def::Type(_) => "type",
|
||||
Def::Class(_) => "class",
|
||||
Def::Instance(_) => "instance",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,6 +240,97 @@ pub struct Suppress {
|
||||
pub because: String,
|
||||
}
|
||||
|
||||
/// Iter 22b.1: a typeclass declaration (Decision 11).
|
||||
///
|
||||
/// Single-parameter, multi-method, optional-default, optional-superclass
|
||||
/// typeclass. The `param` is a single string — multi-param classes are
|
||||
/// rejected by Decision 11 axis 1, and the schema enforces it by shape
|
||||
/// (`param: String`, not `Vec<String>`).
|
||||
///
|
||||
/// `superclass`, when present, MUST have its `type` field equal to
|
||||
/// `param`. The check is enforced in 22b.2 via the
|
||||
/// `InvalidSuperclassParam` diagnostic; the schema does not encode it.
|
||||
///
|
||||
/// All optional fields are omitted from canonical JSON when absent /
|
||||
/// empty, so future schema evolution can land here without disturbing
|
||||
/// pre-22b hashes.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ClassDef {
|
||||
/// Class name (capitalised by convention).
|
||||
pub name: String,
|
||||
/// Single type-parameter name.
|
||||
pub param: String,
|
||||
/// Optional single-superclass relation.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub superclass: Option<SuperclassRef>,
|
||||
/// Methods of the class, in declaration order.
|
||||
pub methods: Vec<ClassMethod>,
|
||||
/// Optional source-level documentation string.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub doc: Option<String>,
|
||||
}
|
||||
|
||||
/// Iter 22b.1: reference to a superclass relation in [`ClassDef`].
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SuperclassRef {
|
||||
/// Superclass name.
|
||||
pub class: String,
|
||||
/// Type the superclass is applied to. MUST equal the parent
|
||||
/// `ClassDef.param` (validated in 22b.2 — schema does not enforce).
|
||||
#[serde(rename = "type")]
|
||||
pub type_: String,
|
||||
}
|
||||
|
||||
/// Iter 22b.1: one method declared in a [`ClassDef`].
|
||||
///
|
||||
/// `default` is `None` when the method is abstract-required (every
|
||||
/// instance must specify it); `Some(body)` when the method has a
|
||||
/// default body (instances may inherit or override).
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ClassMethod {
|
||||
/// Method name.
|
||||
pub name: String,
|
||||
/// Full method signature including any mode annotations. The
|
||||
/// class parameter appears as a [`Type::Var`] inside this signature.
|
||||
#[serde(rename = "type")]
|
||||
pub ty: Type,
|
||||
/// Default body. `None` ⇒ abstract-required; `Some(body)` ⇒
|
||||
/// default-with-fallback.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub default: Option<Term>,
|
||||
}
|
||||
|
||||
/// Iter 22b.1: an instance declaration (Decision 11).
|
||||
///
|
||||
/// `class` is the name of the class being instantiated. `type_` is the
|
||||
/// concrete type expression the class is applied to — never the class
|
||||
/// param. `methods` contains bodies for the required methods plus any
|
||||
/// overrides of default-bearing methods.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct InstanceDef {
|
||||
/// Class being instantiated.
|
||||
pub class: String,
|
||||
/// Concrete type the class is applied to.
|
||||
#[serde(rename = "type")]
|
||||
pub type_: Type,
|
||||
/// Method bodies in declaration order.
|
||||
pub methods: Vec<InstanceMethod>,
|
||||
/// Optional source-level documentation string.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub doc: Option<String>,
|
||||
}
|
||||
|
||||
/// Iter 22b.1: one method body in an [`InstanceDef`].
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct InstanceMethod {
|
||||
/// Method name (must match a method in the corresponding class).
|
||||
pub name: String,
|
||||
/// Method body. The class's declared method type with the class
|
||||
/// param substituted to the instance type is the body's expected
|
||||
/// type — checked in 22b.2.
|
||||
pub body: Term,
|
||||
}
|
||||
|
||||
/// A constant (top-level binding to a value).
|
||||
///
|
||||
/// Differs from a zero-arg [`FnDef`] in that it is evaluated once and
|
||||
|
||||
@@ -169,6 +169,18 @@ pub fn desugar_module(m: &Module) -> Module {
|
||||
Def::Type(td) => {
|
||||
used.insert(td.name.clone());
|
||||
}
|
||||
// Iter 22b.1: class/instance defs are passthrough for the
|
||||
// desugar pass — their bodies (default methods, instance
|
||||
// method bodies) will be desugared once 22b.2 wires the
|
||||
// class/instance arms into typecheck. For 22b.1 the names
|
||||
// still go into `used` so the synthetic-name generator
|
||||
// does not collide.
|
||||
Def::Class(c) => {
|
||||
used.insert(c.name.clone());
|
||||
}
|
||||
Def::Instance(i) => {
|
||||
used.insert(i.class.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
// Iter 16b.1: pre-collect every top-level def name. The LetRec
|
||||
@@ -248,6 +260,11 @@ pub fn desugar_module(m: &Module) -> Module {
|
||||
c.value = d.desugar_term(&c.value, &scope);
|
||||
}
|
||||
Def::Type(_) => {}
|
||||
// Iter 22b.1: class/instance defs are not desugared yet.
|
||||
// Their bodies (default methods, instance method bodies)
|
||||
// will be processed once 22b.2 lands the class/instance
|
||||
// arms in typecheck and 22b.3 in codegen.
|
||||
Def::Class(_) | Def::Instance(_) => {}
|
||||
}
|
||||
}
|
||||
// Iter 16b.1: append every lifted fn to the desugared module.
|
||||
|
||||
@@ -63,6 +63,11 @@ pub fn manifest(m: &Module) -> String {
|
||||
};
|
||||
("type", body)
|
||||
}
|
||||
// Iter 22b.1: one-line summary `class C a` / `instance C T`.
|
||||
// Full pretty rendering of method signatures and bodies
|
||||
// lands in 22b.4 alongside the prose projection arms.
|
||||
Def::Class(c) => ("class", format!("{} {}", c.name, c.param)),
|
||||
Def::Instance(i) => ("instance", format!("{} {}", i.class, type_to_string(&i.type_))),
|
||||
};
|
||||
writeln!(
|
||||
s,
|
||||
|
||||
@@ -272,6 +272,14 @@ fn spec_mentions_every_def_kind() {
|
||||
Def::Fn(_) => "fn",
|
||||
Def::Const(_) => "const",
|
||||
Def::Type(_) => "type",
|
||||
// Iter 22b.1: class/instance Def variants exist but are
|
||||
// not yet anchored in the prose-spec block. Once 22b.4
|
||||
// adds prose projection for them, the FORM_A_SPEC text
|
||||
// gains `(class ` / `(instance ` anchors and this match
|
||||
// will be exercised. For 22b.1 the exemplars list above
|
||||
// does not produce these variants.
|
||||
Def::Class(_) => "class",
|
||||
Def::Instance(_) => "instance",
|
||||
};
|
||||
assert!(
|
||||
FORM_A_SPEC.contains(anchor),
|
||||
|
||||
@@ -77,6 +77,28 @@ fn write_def(out: &mut String, def: &Def, level: usize) {
|
||||
Def::Type(td) => write_type_def(out, td, level),
|
||||
Def::Fn(fd) => write_fn_def(out, fd, level),
|
||||
Def::Const(cd) => write_const_def(out, cd, level),
|
||||
// Iter 22b.1: ClassDef / InstanceDef render as a single-line
|
||||
// placeholder so prose round-trip survives a module that
|
||||
// contains them. The full Form-B projection (mirror of the
|
||||
// ClassDef / InstanceDef Form-A schema, with method bodies
|
||||
// re-indented and superclass / default markers spelled out)
|
||||
// is deferred to 22b.4 alongside the prose-roundtrip parser
|
||||
// arms. The placeholder is informational only — the prose
|
||||
// round-trip mediator does not yet read these forms back.
|
||||
Def::Class(c) => {
|
||||
indent(out, level);
|
||||
out.push_str("// (class ");
|
||||
out.push_str(&c.name);
|
||||
out.push(' ');
|
||||
out.push_str(&c.param);
|
||||
out.push_str(") -- 22b.4 will render full Form-B\n");
|
||||
}
|
||||
Def::Instance(i) => {
|
||||
indent(out, level);
|
||||
out.push_str("// (instance ");
|
||||
out.push_str(&i.class);
|
||||
out.push_str(") -- 22b.4 will render full Form-B\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,6 +94,25 @@ fn write_def(out: &mut String, def: &Def, level: usize) {
|
||||
Def::Type(td) => write_type_def(out, td, level),
|
||||
Def::Fn(fd) => write_fn_def(out, fd, level),
|
||||
Def::Const(cd) => write_const_def(out, cd, level),
|
||||
// Iter 22b.1: surface (Form-B) printer arms for ClassDef /
|
||||
// InstanceDef are deferred to 22b.4 (along with the parse-side
|
||||
// arms in `surface/src/parse.rs`). For 22b.1 we emit a
|
||||
// single-line placeholder so a module containing these defs
|
||||
// still round-trips through `ail render` in print-only mode.
|
||||
Def::Class(c) => {
|
||||
indent(out, level);
|
||||
out.push_str("(class ");
|
||||
out.push_str(&c.name);
|
||||
out.push(' ');
|
||||
out.push_str(&c.param);
|
||||
out.push(')');
|
||||
}
|
||||
Def::Instance(i) => {
|
||||
indent(out, level);
|
||||
out.push_str("(instance ");
|
||||
out.push_str(&i.class);
|
||||
out.push(')');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user