|
|
|
@@ -64,8 +64,8 @@ pub struct Import {
|
|
|
|
|
/// [`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
|
|
|
|
|
/// `Class` and `Instance` are additive — fixtures that predate
|
|
|
|
|
/// the typeclass layer 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.
|
|
|
|
@@ -78,9 +78,9 @@ pub enum Def {
|
|
|
|
|
Const(ConstDef),
|
|
|
|
|
/// Type (ADT) definition; see [`TypeDef`].
|
|
|
|
|
Type(TypeDef),
|
|
|
|
|
/// Iter 22b.1: typeclass declaration; see [`ClassDef`].
|
|
|
|
|
/// Typeclass declaration; see [`ClassDef`].
|
|
|
|
|
Class(ClassDef),
|
|
|
|
|
/// Iter 22b.1: instance declaration; see [`InstanceDef`].
|
|
|
|
|
/// Instance declaration; see [`InstanceDef`].
|
|
|
|
|
Instance(InstanceDef),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -136,9 +136,8 @@ pub fn def_kind(def: &Def) -> &'static str {
|
|
|
|
|
pub struct TypeDef {
|
|
|
|
|
/// Type name (capitalised by convention).
|
|
|
|
|
pub name: String,
|
|
|
|
|
/// Type parameters (Iter 13a parameterised-ADT support). A
|
|
|
|
|
/// monomorphic ADT has `vars` empty and is serialized identically
|
|
|
|
|
/// to the pre-13a schema (the field is **omitted** when empty),
|
|
|
|
|
/// Type parameters for parameterised ADTs. A monomorphic ADT has
|
|
|
|
|
/// `vars` empty and the field is **omitted** when empty,
|
|
|
|
|
/// preserving the canonical-JSON hash of every existing module on
|
|
|
|
|
/// disk. See the regression test
|
|
|
|
|
/// `iter13a_schema_extension_preserves_pre_13a_hashes` in
|
|
|
|
@@ -151,7 +150,7 @@ pub struct TypeDef {
|
|
|
|
|
/// Optional source-level documentation string.
|
|
|
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
|
|
|
pub doc: Option<String>,
|
|
|
|
|
/// Iter 18e: opt-in `(drop-iterative)` annotation. When `true`,
|
|
|
|
|
/// Opt-in `(drop-iterative)` annotation. When `true`,
|
|
|
|
|
/// codegen emits `drop_<m>_<T>` with an iterative worklist body
|
|
|
|
|
/// instead of the recursive cascade — chosen by the LLM-author when
|
|
|
|
|
/// the type is expected to form long chains (millions of cells)
|
|
|
|
@@ -159,9 +158,9 @@ pub struct TypeDef {
|
|
|
|
|
///
|
|
|
|
|
/// Serialised as `"drop-iterative": true` (kebab-case) when set;
|
|
|
|
|
/// the field is omitted when `false` so canonical-JSON hashes of
|
|
|
|
|
/// every pre-18e fixture remain bit-stable. See the regression
|
|
|
|
|
/// test `iter18e_drop_iterative_default_preserves_hashes` in
|
|
|
|
|
/// [`crate::hash`].
|
|
|
|
|
/// every fixture that does not opt in remain bit-stable. See the
|
|
|
|
|
/// regression test `iter18e_drop_iterative_default_preserves_hashes`
|
|
|
|
|
/// in [`crate::hash`].
|
|
|
|
|
#[serde(
|
|
|
|
|
default,
|
|
|
|
|
rename = "drop-iterative",
|
|
|
|
@@ -219,24 +218,24 @@ pub struct FnDef {
|
|
|
|
|
/// the same additive-schema pattern as [`FnDef::doc`].
|
|
|
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
|
|
|
pub export: Option<String>,
|
|
|
|
|
/// Iter 19b: structured-diagnostic suppressions opted into for this
|
|
|
|
|
/// Structured-diagnostic suppressions opted into for this
|
|
|
|
|
/// fn. Each entry names a diagnostic code and an author-asserted
|
|
|
|
|
/// reason. Currently the only consumer is `over-strict-mode`
|
|
|
|
|
/// (Iter 19a / 19a.1) but the mechanism is generic across codes.
|
|
|
|
|
/// `because` must be non-empty — the typechecker emits
|
|
|
|
|
/// `empty-suppress-reason` (Error) otherwise.
|
|
|
|
|
/// reason. The only current consumer is `over-strict-mode`, but
|
|
|
|
|
/// the mechanism is generic across codes. `because` must be
|
|
|
|
|
/// non-empty — the typechecker emits `empty-suppress-reason`
|
|
|
|
|
/// (Error) otherwise.
|
|
|
|
|
///
|
|
|
|
|
/// Serialised with `skip_serializing_if = "Vec::is_empty"` so
|
|
|
|
|
/// every pre-19b fixture's canonical-JSON hash stays bit-identical.
|
|
|
|
|
/// The same additive-schema pattern is used by [`TypeDef::vars`]
|
|
|
|
|
/// (Iter 13a) and [`Type::Con::args`] (Iter 13a).
|
|
|
|
|
/// every pre-suppress fixture's canonical-JSON hash stays
|
|
|
|
|
/// bit-identical. The same additive-schema pattern is used by
|
|
|
|
|
/// [`TypeDef::vars`] and [`Type::Con::args`].
|
|
|
|
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
|
|
|
|
pub suppress: Vec<Suppress>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Iter 19b: one entry in [`FnDef::suppress`]. Marks a structured
|
|
|
|
|
/// diagnostic the author has consciously decided to allow on this
|
|
|
|
|
/// def, with a mandatory reason.
|
|
|
|
|
/// One entry in [`FnDef::suppress`]. Marks a structured diagnostic
|
|
|
|
|
/// the author has consciously decided to allow on this def, with a
|
|
|
|
|
/// mandatory reason.
|
|
|
|
|
///
|
|
|
|
|
/// `because` is non-empty by schema rule — the typechecker emits
|
|
|
|
|
/// `empty-suppress-reason` (Error severity) when it is empty or
|
|
|
|
@@ -256,20 +255,22 @@ pub struct Suppress {
|
|
|
|
|
pub because: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Iter 22b.1: a typeclass declaration (Decision 11).
|
|
|
|
|
/// A typeclass declaration (narrative in
|
|
|
|
|
/// `design/contracts/typeclasses.md`).
|
|
|
|
|
///
|
|
|
|
|
/// 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>`).
|
|
|
|
|
/// rejected by the typeclass design (concrete-types-only, kind `*`),
|
|
|
|
|
/// 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.
|
|
|
|
|
/// `param`. The check is enforced 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.
|
|
|
|
|
/// existing hashes.
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct ClassDef {
|
|
|
|
|
/// Class name (capitalised by convention). Bare — symmetric to
|
|
|
|
@@ -277,8 +278,7 @@ pub struct ClassDef {
|
|
|
|
|
/// a reference. Cross-module class references live in
|
|
|
|
|
/// `InstanceDef.class`, `Constraint.class`, and
|
|
|
|
|
/// `SuperclassRef.class`; those fields carry the canonical form
|
|
|
|
|
/// (bare for same-module, `<module>.<Class>` for cross-module)
|
|
|
|
|
/// per mq.1.
|
|
|
|
|
/// (bare for same-module, `<module>.<Class>` for cross-module).
|
|
|
|
|
pub name: String,
|
|
|
|
|
/// Single type-parameter name.
|
|
|
|
|
pub param: String,
|
|
|
|
@@ -292,20 +292,21 @@ pub struct ClassDef {
|
|
|
|
|
pub doc: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Iter 22b.1: reference to a superclass relation in [`ClassDef`].
|
|
|
|
|
/// Reference to a superclass relation in [`ClassDef`].
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct SuperclassRef {
|
|
|
|
|
/// Superclass name in canonical form (mq.1): bare for a
|
|
|
|
|
/// same-module class, `<module>.<Class>` for a cross-module
|
|
|
|
|
/// class. Symmetric to ct.1's `Type::Con.name` rule.
|
|
|
|
|
/// Superclass name in canonical form: bare for a same-module
|
|
|
|
|
/// class, `<module>.<Class>` for a cross-module class.
|
|
|
|
|
/// Symmetric to `Type::Con.name`'s canonical-form rule.
|
|
|
|
|
pub class: String,
|
|
|
|
|
/// Type the superclass is applied to. MUST equal the parent
|
|
|
|
|
/// `ClassDef.param` (validated in 22b.2 — schema does not enforce).
|
|
|
|
|
/// `ClassDef.param` (validated by typecheck — schema does not
|
|
|
|
|
/// enforce).
|
|
|
|
|
#[serde(rename = "type")]
|
|
|
|
|
pub type_: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Iter 22b.1: one method declared in a [`ClassDef`].
|
|
|
|
|
/// 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
|
|
|
|
@@ -324,7 +325,8 @@ pub struct ClassMethod {
|
|
|
|
|
pub default: Option<Term>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Iter 22b.1: an instance declaration (Decision 11).
|
|
|
|
|
/// An instance declaration (narrative in
|
|
|
|
|
/// `design/contracts/typeclasses.md`).
|
|
|
|
|
///
|
|
|
|
|
/// `class` is the name of the class being instantiated. `type_` is the
|
|
|
|
|
/// concrete type expression the class is applied to — never the class
|
|
|
|
@@ -332,9 +334,9 @@ pub struct ClassMethod {
|
|
|
|
|
/// overrides of default-bearing methods.
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct InstanceDef {
|
|
|
|
|
/// Class being instantiated, in canonical form (mq.1): bare for
|
|
|
|
|
/// a same-module class, `<module>.<Class>` for a cross-module
|
|
|
|
|
/// class. Symmetric to ct.1's `Type::Con.name` rule.
|
|
|
|
|
/// Class being instantiated, in canonical form: bare for a
|
|
|
|
|
/// same-module class, `<module>.<Class>` for a cross-module
|
|
|
|
|
/// class. Symmetric to `Type::Con.name`'s canonical-form rule.
|
|
|
|
|
pub class: String,
|
|
|
|
|
/// Concrete type the class is applied to.
|
|
|
|
|
#[serde(rename = "type")]
|
|
|
|
@@ -346,28 +348,29 @@ pub struct InstanceDef {
|
|
|
|
|
pub doc: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Iter 22b.1: one method body in an [`InstanceDef`].
|
|
|
|
|
/// 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.
|
|
|
|
|
/// type.
|
|
|
|
|
pub body: Term,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Iter 22b.2: a class constraint on a polymorphic function (Decision
|
|
|
|
|
/// 11). `(class, type)` pair where `class` is a class name and `type`
|
|
|
|
|
/// is a `Type` expression — typically a single `Type::Var` (e.g.
|
|
|
|
|
/// `(Eq, a)` for `Eq a => ...`). Concrete-type constraints are legal
|
|
|
|
|
/// schema-wise but fired as `no-instance` at typecheck time if no
|
|
|
|
|
/// matching registry entry exists.
|
|
|
|
|
/// A class constraint on a polymorphic function (narrative in
|
|
|
|
|
/// `design/contracts/typeclasses.md`). `(class, type)` pair where
|
|
|
|
|
/// `class` is a class name and `type` is a `Type` expression —
|
|
|
|
|
/// typically a single `Type::Var` (e.g. `(Eq, a)` for
|
|
|
|
|
/// `Eq a => ...`). Concrete-type constraints are legal schema-wise
|
|
|
|
|
/// but fired as `no-instance` at typecheck time if no matching
|
|
|
|
|
/// registry entry exists.
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct Constraint {
|
|
|
|
|
/// Class name in canonical form (mq.1): bare for a same-module
|
|
|
|
|
/// class, `<module>.<Class>` for a cross-module class.
|
|
|
|
|
/// Symmetric to ct.1's `Type::Con.name` rule.
|
|
|
|
|
/// Class name in canonical form: bare for a same-module class,
|
|
|
|
|
/// `<module>.<Class>` for a cross-module class. Symmetric to
|
|
|
|
|
/// `Type::Con.name`'s canonical-form rule.
|
|
|
|
|
pub class: String,
|
|
|
|
|
/// Type the class is applied to.
|
|
|
|
|
#[serde(rename = "type")]
|
|
|
|
@@ -408,11 +411,11 @@ pub enum Term {
|
|
|
|
|
/// Function application. `callee` is evaluated to a function value;
|
|
|
|
|
/// `args` are evaluated left-to-right.
|
|
|
|
|
///
|
|
|
|
|
/// Iter 14e: `tail` marks this call as occurring in tail position
|
|
|
|
|
/// (per Decision 8). When set, codegen lowers the call as
|
|
|
|
|
/// `musttail call`. The flag defaults to `false` and is omitted
|
|
|
|
|
/// during canonical-JSON serialisation when unset, so pre-14e
|
|
|
|
|
/// fixtures keep bit-identical hashes.
|
|
|
|
|
/// `tail` marks this call as occurring in tail position (see
|
|
|
|
|
/// `design/contracts/tail-calls.md`). When set, codegen lowers
|
|
|
|
|
/// the call as `musttail call`. The flag defaults to `false` and
|
|
|
|
|
/// is omitted during canonical-JSON serialisation when unset, so
|
|
|
|
|
/// pre-tail-flag fixtures keep bit-identical hashes.
|
|
|
|
|
App {
|
|
|
|
|
#[serde(rename = "fn")]
|
|
|
|
|
callee: Box<Term>,
|
|
|
|
@@ -426,7 +429,7 @@ pub enum Term {
|
|
|
|
|
value: Box<Term>,
|
|
|
|
|
body: Box<Term>,
|
|
|
|
|
},
|
|
|
|
|
/// Local recursive let-binding (Iter 16b.1). Always fn-shaped:
|
|
|
|
|
/// Local recursive let-binding. Always fn-shaped:
|
|
|
|
|
/// the bound name `name` is recursively visible inside `body`.
|
|
|
|
|
/// Eliminated by `crate::desugar` before typecheck — lifted to a
|
|
|
|
|
/// synthetic top-level fn when `body` does not capture any name
|
|
|
|
@@ -455,7 +458,7 @@ pub enum Term {
|
|
|
|
|
/// `match` in codegen (`lower_effect_op`); there is no
|
|
|
|
|
/// effect-handler table and no link-time resolution.
|
|
|
|
|
///
|
|
|
|
|
/// Iter 14e: see [`Term::App`] for the `tail` field semantics.
|
|
|
|
|
/// See [`Term::App`] for the `tail` field semantics.
|
|
|
|
|
Do {
|
|
|
|
|
op: String,
|
|
|
|
|
args: Vec<Term>,
|
|
|
|
@@ -479,7 +482,7 @@ pub enum Term {
|
|
|
|
|
scrutinee: Box<Term>,
|
|
|
|
|
arms: Vec<Arm>,
|
|
|
|
|
},
|
|
|
|
|
/// Anonymous function (Iter 8b). Captures any free variables of
|
|
|
|
|
/// Anonymous function. Captures any free variables of
|
|
|
|
|
/// `body` from the enclosing scope. Param/return types are
|
|
|
|
|
/// declared inline so the typechecker stays HM-monomorphic on
|
|
|
|
|
/// the inferred shape.
|
|
|
|
@@ -493,7 +496,7 @@ pub enum Term {
|
|
|
|
|
effects: Vec<String>,
|
|
|
|
|
body: Box<Term>,
|
|
|
|
|
},
|
|
|
|
|
/// Sequencing (Iter 10). `lhs` is evaluated for its effects and its
|
|
|
|
|
/// Sequencing. `lhs` is evaluated for its effects and its
|
|
|
|
|
/// result discarded; `rhs` is the value of the whole expression.
|
|
|
|
|
/// Equivalent to `let _ = lhs in rhs`, but with a dedicated node so
|
|
|
|
|
/// pretty-print and diagnostics read cleanly.
|
|
|
|
@@ -501,27 +504,25 @@ pub enum Term {
|
|
|
|
|
lhs: Box<Term>,
|
|
|
|
|
rhs: Box<Term>,
|
|
|
|
|
},
|
|
|
|
|
/// Iter 18c.1: explicit RC clone. Lowers identically to its inner
|
|
|
|
|
/// term in 18c.1; in 18c.3 the codegen will emit
|
|
|
|
|
/// Explicit RC clone. Codegen emits
|
|
|
|
|
/// `call void @ailang_rc_inc(ptr %v)` before returning `%v` under
|
|
|
|
|
/// `--alloc=rc`. The variant is additive: `(clone X)` round-trips
|
|
|
|
|
/// through every pre-18c.1 fixture without their hashes changing
|
|
|
|
|
/// because none of them use the new tag.
|
|
|
|
|
/// through every fixture that does not use the tag without its
|
|
|
|
|
/// hash changing.
|
|
|
|
|
Clone {
|
|
|
|
|
value: Box<Term>,
|
|
|
|
|
},
|
|
|
|
|
/// Iter 18d.1: explicit reuse-as hint. Wraps an allocating `body`
|
|
|
|
|
/// (typically `Term::Ctor`, also `Term::Lam`) and names a `source`
|
|
|
|
|
/// term whose memory slot the body's allocation should reuse.
|
|
|
|
|
/// Explicit reuse-as hint. Wraps an allocating `body` (typically
|
|
|
|
|
/// `Term::Ctor`, also `Term::Lam`) and names a `source` term
|
|
|
|
|
/// whose memory slot the body's allocation should reuse.
|
|
|
|
|
/// Conventionally `source` is a `Term::Var { name }` — the
|
|
|
|
|
/// linearity check rejects anything else with
|
|
|
|
|
/// `reuse-as-source-not-bare-var`. The body must be allocating;
|
|
|
|
|
/// non-allocating bodies are rejected at typecheck with
|
|
|
|
|
/// `reuse-as-non-allocating-body`. Lowers as identity in 18d.1
|
|
|
|
|
/// (returns `body`'s `(ssa, ty)`, ignores `source`); 18d.2 will
|
|
|
|
|
/// lower this as in-place rewrite under `--alloc=rc`. The variant
|
|
|
|
|
/// is additive — pre-18d fixtures keep their canonical-JSON hash
|
|
|
|
|
/// because none of them use the new tag.
|
|
|
|
|
/// `reuse-as-non-allocating-body`. Codegen lowers as in-place
|
|
|
|
|
/// rewrite under `--alloc=rc`. The variant is additive —
|
|
|
|
|
/// fixtures that do not use the tag keep their canonical-JSON
|
|
|
|
|
/// hash.
|
|
|
|
|
#[serde(rename = "reuse-as")]
|
|
|
|
|
ReuseAs {
|
|
|
|
|
source: Box<Term>,
|
|
|
|
@@ -685,7 +686,7 @@ pub enum Type {
|
|
|
|
|
/// and parameterised forms like `List<Int>`.
|
|
|
|
|
Con {
|
|
|
|
|
name: String,
|
|
|
|
|
/// Type arguments (Iter 13a). For pre-13a uses (`Int`, `Bool`,
|
|
|
|
|
/// Type arguments. For non-parameterised uses (`Int`, `Bool`,
|
|
|
|
|
/// non-parameterised user ADTs) this stays empty and is
|
|
|
|
|
/// **omitted** during serialization, so the canonical-JSON
|
|
|
|
|
/// hash of every pre-existing module remains bit-identical.
|
|
|
|
@@ -697,14 +698,15 @@ pub enum Type {
|
|
|
|
|
/// set; equality compares it modulo order (see the [`PartialEq`]
|
|
|
|
|
/// impl below).
|
|
|
|
|
///
|
|
|
|
|
/// Iter 18a (Decision 10): `param_modes` and `ret_mode` carry the
|
|
|
|
|
/// `(borrow T)` / `(own T)` wrappers from the surface form. They
|
|
|
|
|
/// are metadata on `Type::Fn`, not new `Type` variants — so
|
|
|
|
|
/// unification, occurs, apply, and every other `Type` match-arm
|
|
|
|
|
/// keeps working unchanged. `param_modes` is omitted from
|
|
|
|
|
/// canonical JSON when every entry is `Implicit`; `ret_mode` is
|
|
|
|
|
/// omitted when it is `Implicit`. Pre-18a fixtures therefore hash
|
|
|
|
|
/// bit-identically.
|
|
|
|
|
/// `param_modes` and `ret_mode` carry the `(borrow T)` /
|
|
|
|
|
/// `(own T)` wrappers from the surface form. They are metadata
|
|
|
|
|
/// on `Type::Fn`, not new `Type` variants — so unification,
|
|
|
|
|
/// occurs, apply, and every other `Type` match-arm keeps working
|
|
|
|
|
/// unchanged. `param_modes` is omitted from canonical JSON when
|
|
|
|
|
/// every entry is `Implicit`; `ret_mode` is omitted when it is
|
|
|
|
|
/// `Implicit`, so pre-mode-annotation fixtures hash
|
|
|
|
|
/// bit-identically. Full contract in
|
|
|
|
|
/// `design/contracts/memory-model.md`.
|
|
|
|
|
Fn {
|
|
|
|
|
params: Vec<Type>,
|
|
|
|
|
#[serde(default, skip_serializing_if = "all_implicit")]
|
|
|
|
@@ -726,9 +728,9 @@ pub enum Type {
|
|
|
|
|
/// names, instantiated fresh at each use site.
|
|
|
|
|
Forall {
|
|
|
|
|
vars: Vec<String>,
|
|
|
|
|
/// Iter 22b.2: class constraints quantified together with
|
|
|
|
|
/// `vars`. Empty for pre-22b.2 polymorphic types; serialised
|
|
|
|
|
/// with `skip_serializing_if = "Vec::is_empty"` so existing
|
|
|
|
|
/// Class constraints quantified together with `vars`. Empty
|
|
|
|
|
/// for unconstrained polymorphic types; serialised with
|
|
|
|
|
/// `skip_serializing_if = "Vec::is_empty"` so existing
|
|
|
|
|
/// canonical-JSON bytes stay bit-identical.
|
|
|
|
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
|
|
|
|
constraints: Vec<Constraint>,
|
|
|
|
@@ -759,11 +761,11 @@ impl Type {
|
|
|
|
|
Type::Con { name: "Float".into(), args: vec![] }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Iter 18a: build a `Type::Fn` with all parameter modes set to
|
|
|
|
|
/// `ParamMode::Implicit` and `ret_mode` set to `Implicit`. This is
|
|
|
|
|
/// the form every typechecker / desugar / codegen site that
|
|
|
|
|
/// Build a `Type::Fn` with all parameter modes set to
|
|
|
|
|
/// `ParamMode::Implicit` and `ret_mode` set to `Implicit`. This
|
|
|
|
|
/// is the form every typechecker / desugar / codegen site that
|
|
|
|
|
/// synthesises a fn-type should use, so that newly inferred
|
|
|
|
|
/// fn-types retain pre-18a canonical-JSON bytes.
|
|
|
|
|
/// fn-types retain pre-mode-annotation canonical-JSON bytes.
|
|
|
|
|
pub fn fn_implicit(params: Vec<Type>, ret: Type, effects: Vec<String>) -> Type {
|
|
|
|
|
let n = params.len();
|
|
|
|
|
Type::Fn {
|
|
|
|
@@ -776,21 +778,21 @@ impl Type {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Iter 18a (Decision 10): per-parameter / return mode marker on a
|
|
|
|
|
/// [`Type::Fn`].
|
|
|
|
|
/// Per-parameter / return mode marker on a [`Type::Fn`]. Full
|
|
|
|
|
/// contract lives in `design/contracts/memory-model.md`.
|
|
|
|
|
///
|
|
|
|
|
/// `Implicit` is the legacy state for fn-types that were constructed
|
|
|
|
|
/// before the borrow/own surface annotations existed. Semantically,
|
|
|
|
|
/// `Implicit ≡ Own` throughout the 18-series; the distinction exists
|
|
|
|
|
/// only so pre-18a JSON fixtures continue to serialize without a
|
|
|
|
|
/// `"mode"` wrapper and therefore keep their canonical-JSON hash.
|
|
|
|
|
/// `Implicit ≡ Own`; the distinction exists only so pre-annotation
|
|
|
|
|
/// JSON fixtures continue to serialize without a `"mode"` wrapper
|
|
|
|
|
/// and therefore keep their canonical-JSON hash.
|
|
|
|
|
///
|
|
|
|
|
/// `Own` and `Borrow` are author-asserted: the surface form
|
|
|
|
|
/// `(own T)` / `(borrow T)` round-trips through this enum.
|
|
|
|
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
#[serde(rename_all = "lowercase")]
|
|
|
|
|
pub enum ParamMode {
|
|
|
|
|
/// Pre-18a / unannotated. Treated as `Own` by the typechecker.
|
|
|
|
|
/// Unannotated / back-compat. Treated as `Own` by the typechecker.
|
|
|
|
|
#[default]
|
|
|
|
|
Implicit,
|
|
|
|
|
/// `(own T)` — caller transfers ownership; callee consumes.
|
|
|
|
@@ -807,21 +809,20 @@ impl ParamMode {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Iter 18a: serde helper for [`Type::Fn::param_modes`]. Returns
|
|
|
|
|
/// `true` when every entry is [`ParamMode::Implicit`] (or when the
|
|
|
|
|
/// list is empty), so canonical JSON omits the field for any fn-type
|
|
|
|
|
/// without explicit `(borrow)` / `(own)` annotations and pre-18a
|
|
|
|
|
/// Serde helper for [`Type::Fn::param_modes`]. Returns `true` when
|
|
|
|
|
/// every entry is [`ParamMode::Implicit`] (or when the list is
|
|
|
|
|
/// empty), so canonical JSON omits the field for any fn-type without
|
|
|
|
|
/// explicit `(borrow)` / `(own)` annotations and pre-annotation
|
|
|
|
|
/// fixtures keep bit-identical hashes.
|
|
|
|
|
fn all_implicit(modes: &[ParamMode]) -> bool {
|
|
|
|
|
modes.iter().all(|m| m.is_implicit())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Iter 18a: equality of [`ParamMode`] for the purposes of `Type`
|
|
|
|
|
/// equality. `Implicit` and `Own` are treated as the same mode
|
|
|
|
|
/// throughout the 18-series; `Borrow` is distinct. This keeps
|
|
|
|
|
/// pre-18a fixtures (whose fn-types serialize `Implicit`) compatible
|
|
|
|
|
/// with newly-written 18a fixtures that mark the same fn-type
|
|
|
|
|
/// explicitly with `(own T)`.
|
|
|
|
|
/// Equality of [`ParamMode`] for the purposes of `Type` equality.
|
|
|
|
|
/// `Implicit` and `Own` are treated as the same mode; `Borrow` is
|
|
|
|
|
/// distinct. This keeps pre-annotation fixtures (whose fn-types
|
|
|
|
|
/// serialize `Implicit`) compatible with newly-written fixtures
|
|
|
|
|
/// that mark the same fn-type explicitly with `(own T)`.
|
|
|
|
|
fn mode_eq(a: &ParamMode, b: &ParamMode) -> bool {
|
|
|
|
|
match (a, b) {
|
|
|
|
|
(ParamMode::Borrow, ParamMode::Borrow) => true,
|
|
|
|
@@ -831,7 +832,7 @@ fn mode_eq(a: &ParamMode, b: &ParamMode) -> bool {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Iter 18a: equality of two `param_modes` slices, robust to the
|
|
|
|
|
/// Equality of two `param_modes` slices, robust to the
|
|
|
|
|
/// "elided when all-implicit" representation used by typechecker /
|
|
|
|
|
/// desugar / codegen sites that construct fn-types with
|
|
|
|
|
/// `param_modes: vec![]`. Both slices are normalised to "implicit
|
|
|
|
@@ -897,9 +898,10 @@ impl Eq for Type {}
|
|
|
|
|
|
|
|
|
|
/// Serde helper for `#[serde(skip_serializing_if = "is_false")]`.
|
|
|
|
|
///
|
|
|
|
|
/// Used by [`Term::App::tail`] and [`Term::Do::tail`] (Iter 14e) so the
|
|
|
|
|
/// `tail` flag is omitted from the canonical JSON whenever it is false,
|
|
|
|
|
/// preserving bit-identical hashes for every pre-14e definition.
|
|
|
|
|
/// Used by [`Term::App::tail`] and [`Term::Do::tail`] so the `tail`
|
|
|
|
|
/// flag is omitted from the canonical JSON whenever it is false,
|
|
|
|
|
/// preserving bit-identical hashes for every fixture that does not
|
|
|
|
|
/// carry the flag.
|
|
|
|
|
#[allow(clippy::trivially_copy_pass_by_ref)]
|
|
|
|
|
fn is_false(b: &bool) -> bool {
|
|
|
|
|
!*b
|
|
|
|
|