Skip to main content

Metel Language Changelog

Changelog

v0.6.4

Module system technical debt. Shipped by Sprint 15 (sprint/15).

Internal improvements:

  • TypeDefinitionRegistry is now used as the cross-module type accumulator in check_graph, replacing the Vec<Decl> approach that cloned raw AST nodes; cross-module struct field type references now resolve correctly even when the field type comes from an indirect dependency (ADR-0032, METEL-3)
  • InferContext::new accepts imported_schemes directly, enforcing the dual-registration invariant (inference + construction passes both see imported names) at the type level (ADR-0022, METEL-6)
  • declared_names map added to ResolvedNames during name resolution, replacing an O(n) AST scan in build_import_schemes for T0009/T0003 distinction (METEL-4)
  • resolve_path_root extracted to src/module_paths.rs as a single shared implementation for both module_loader and name_resolver; fixed a regression where the Name path root incorrectly doubled the module name segment (ADR-0023, METEL-7)
  • StdPrelude::schemes() / evaluator builtin parity assertion added as a compile-time-checked test (ADR-0027, METEL-5)

Compatibility:

  • No language-visible changes.

v0.6.3

Module system - feature complete. Shipped by Sprint 14 (sprint/14).

Bug fixes:

  • return and break are now valid as bare match arm bodies without enclosing braces: arm => return value (#226)
  • Diamond module dependencies (same physical file reachable via two different logical paths) no longer fail with T0003; the name resolver now dereferences path aliases to their canonical form (#228)

Internal improvements:

  • ? operator desugared in a pre-pass (path_normalizer::desugar_propagate_error) rather than carried through inference and construction; Expr::PropagateError no longer exists after normalization (ADR-0030, #214)
  • Type::Perhaps and Type::Result convenience variants removed from the Type enum; both types are now represented uniformly as Type::Named("Perhaps", ...) and Type::Named("Result", ...) (#150)
  • Per-module isolated runtime environments validated with cross-module closure-capture and mutual-recursion tests (#189)
  • All aspect method dispatch key construction routed through ImplMethodKey::to_env_key(), eliminating ad-hoc format strings in the evaluator (#209)

Compatibility:

  • No language-visible changes except the match arm body fix (#226), which is purely additive.

v0.6.2

Evaluator normalization. Shipped by Sprint 13 (sprint/13).

Internal improvements:

  • Value::Perhaps and Value::Result dedicated variants removed; all Perhaps and Result values now use the general Value::Enum { name, variant, fields } representation, eliminating special-case dispatch throughout the evaluator (ADR-0028, #205)
  • evaluate_graph now initialises each module in its own isolated Environment seeded with builtins and cross-linked via the imported_names table populated by check_graph; replaces the flat-merge strategy from v0.5.0 (ADR-0029, #210)

Compatibility:

  • No language-visible changes. All existing programs produce identical output.

v0.6.1

Type system cleanup and std::core virtual module. Shipped by Sprint 12 (sprint/12).

Internal improvements:

  • Unified TypeDefinitionRegistry replaces four separate flat maps (struct_env, method_env, enum_env, aspect impls) in the type inference and construction passes; a single registry instance is now the source of truth for all type and impl data (#133)
  • ImplMethodKey enum replaces flat string concatenation for impl method dispatch keys in the evaluator
  • StdPrelude::default() is the single source of truth for all built-in function schemes, eliminating the previous divergence between the inference and construction registries

New language features:

  • std::core virtual module: Perhaps, Result, Display, Iterable, From, and all built-in functions are available in every module without any explicit import (#201, #202)
  • Glob import tiers: the runtime auto-imports std::core at Std tier (lowest priority); user import path::* declarations use User tier and silently win over Std tier without a conflict error (#206)

Compatibility:

  • All existing programs are unaffected; std::core names that were previously available globally continue to work without import statements

v0.6.0

Module semantics. Shipped by Sprint 11 (sprint/11).

Enforced module semantics (previously deferred from v0.5.0):

  • Visibility enforcement: pub is required for a declaration to be importable; private items produce a compile-time error (T0009) when referenced from another module
  • Import scoping: only names brought in scope by import are accessible; accessing an undeclared name is a compile-time error (T0003)
  • Alias resolution: import mod::name as alias makes alias callable and removes name from scope
  • Import conflict detection: two imports binding the same local name produce a compile-time error (T0011); explicit imports silently win over conflicting glob imports
  • Glob visibility filtering: import mod::* now includes only pub items from the source module; private items are excluded
  • Re-export propagation: names re-exported via export are part of the facade module's public API and importable by consumers without importing the underlying module directly
  • pub declarations require complete type annotations (T0010): every parameter and the return type must be annotated on a pub fun

Internal improvements:

  • Name resolver wired into the type-checking pipeline (load_root -> resolve -> normalize -> check_graph -> evaluate_graph)
  • Flat-merge compatibility shim (ADR-0019) and last-segment fallback (ADR-0020) removed
  • root::, self::, and super:: path roots now compute correct module paths in both the loader and name resolver

Compatibility:

  • Single-file programs and programs using only pub items across module boundaries are unaffected
  • Programs that imported private items or relied on global declaration visibility will need pub annotations added

v0.5.0

Module system. Shipped by Sprint 9 (sprint/9).

New language features:

  • Multi-file programs: each .mln file is a module; the module graph is built from import declarations
  • import path::Name; both loads the referenced file and brings Name into scope
  • Import forms: single name, alias (as), group ({A, B}), glob (*), module handle
  • export path::Name; re-exports a name from a submodule into the current module's public API
  • pub on fun, struct, enum, and aspect marks declarations as externally accessible
  • Absolute and relative path roots: root::, std::, self::, super::
  • Fully-qualified paths valid in type and expression position without a preceding import
  • Circular imports detected at load time with a full chain in the error message
  • Facade modules: parser.mln alongside parser/ directory - no special mod.mln file
  • File-to-module mapping via :: -> / with no special cases

Shipped in v0.6.1:

  • std::core auto-import and standard library core types (#150, #201, #202)

Compatibility:

  • Single-file programs with no import or export declarations remain valid without modification

v0.4.2

Evaluator refactor, test restructure, and keyword cleanup. Shipped by Sprint 8 (sprint/8).

Breaking changes:

  • Perhaps::Nope renamed to Perhaps::None; the standalone nope keyword is now None

v0.4.1

Technical debt, bug fixes, and internal cleanup. Shipped by Sprint 7 (sprint/7).

Bug fixes:

  • TypeErrorCode::T0005 ("Invalid operand types") is now emitted for arithmetic operators (+, -, *, /, %) applied to non-numeric types (e.g. true + false is now a type error)
  • Unary negation (-) on non-numeric types is now a type error
  • Ordering comparisons (<, <=, >, >=) on non-comparable types (non-Int, non-Float, non-String) are now type errors
  • Pattern::Nope latent bug eliminated - nope values are now exclusively Value::Perhaps(None), so the pattern can no longer silently miss the Value::Enum { name: "Perhaps", variant: "Nope" } form

Internal improvements:

  • Value::YoloResult renamed to Value::Result; Perhaps and Result values are now first-class runtime variants - no longer stored as Value::Enum
  • Large enum variants boxed in Decl, Stmt, TypedDecl, TypedStmt (stack frame sizes reduced from 896-1040 bytes to 8 bytes)
  • Dead utility methods removed (Program::new, Type::is_numeric, Type::is_unit); reserved fields annotated with #[allow(dead_code)]
  • All clippy style/idiom warnings resolved

v0.4.0

Aspects and upgraded builtins. Shipped by Sprint 6 (sprint/6).

New language features:

  • Aspect declarations - aspect Foo { fun method(self) -> T; }
  • impl Aspect for Type blocks with method dispatch via .method() syntax
  • Iterable<T> aspect - user-defined types usable in for-in loops
  • From<S> aspect - as cast desugars to T::from(value); user-defined casts for any type pair
  • Display aspect - .to_string() on Int, Float, Bool, String; print/println polymorphic via Display
  • ? operator now supports cross-type error coercion: if the function's error type E2 implements From<E1>, ? calls E2::from(e) automatically

Builtin changes:

  • print(v) and println(v) are now polymorphic (<T: Display>) - accept any Display type
  • Int::from(f: Float) and Float::from(n: Int) built-in From impls replace the hardcoded as special case
  • Deprecated: print_int, println_int, print_float, println_float, int_to_string, float_to_string, bool_to_string (use .to_string() and polymorphic print/println)

Bug fixes:

  • Keyword-prefix identifiers (break_sum, return_value, let_x) now parse correctly as identifiers
  • Multiple impl From<X> for Y blocks with different source types now dispatch independently

v0.3.0

Generics and type-inference improvements. Shipped by Sprint 5 (sprint/5).

New language features:

  • User-defined generic functions - fun id<T>(x: T) -> T - monomorphised at each call site
  • User-defined generic structs - struct Box<T> { value: T }, struct Pair<A, B> { ... }
  • User-defined generic enums - enum Maybe<T> { Some { value: T }, None {} }
  • Let-polymorphism - unannotated let-bound closures are generalised to polymorphic schemes (let id = fun(x) { x } works at Int, Bool, and String in the same scope)
  • Braceless if body - if (c) expr and if (c) a else b (RFC-0022)
  • struct and enum declarations are allowed inside function bodies

Type-inference improvements:

  • expected_ty propagates into match arm bodies - bare [] and nope resolve without ascription when the surrounding return type is known
  • Callee parameter types propagate into argument construction - find(words, nope) resolves without ascription when the parameter type is Perhaps<String>
  • Lvalue path assignment - obj.field = val and arr[i] = val work on non-bare receivers (e.g. get_foo().bar = 1)

v0.2.0

Evaluator improvements, DX features, and language quality fixes. Shipped by Sprint 3 (sprint/3).

New language features:

  • Type ascription operator : - [] : Int[] guides type inference without runtime cost (RFC-0021)
  • Shorthand struct field initialisation - Point { x, y } desugars to Point { x: x, y: y }
  • Trailing commas allowed in function parameter lists and argument lists

New built-in functions:

  • assert(cond: Bool) - panics with "assertion failed" if cond is false
  • assert_msg(cond: Bool, msg: String) - panics with msg if cond is false
  • dbg<T>(v: T) -> T - prints [dbg] <value> to stderr and returns the value unchanged
  • print_int(n: Int), println_int(n: Int) - print an Int without/with newline
  • print_float(f: Float), println_float(f: Float) - print a Float without/with newline

Bug fixes:

  • Arrays now have value semantics - binding an array to a new variable produces an independent copy
  • Error spans now report file:line:col instead of raw byte offsets
  • Complex expressions (field access, calls) are now valid array index operands

Developer experience:

  • Runtime panics now include a call-stack trace showing function name and call site

v0.1.0

Initial language version. Implemented by the tree-walk interpreter.

Features included:

  • Primitive types: Int, Float, Bool, String, ()
  • Variables: let (immutable), mut (mutable), lexical scoping, fun/type hoisting
  • Functions: first-class values, closures with mutable capture, ? operator (exact error type match only)
  • Structs: literals, field access, methods (impl), mut self, associated functions
  • Enums: unit and struct-like variants, impl blocks
  • Built-in generic types: Perhaps<T>, Result<T, E>, Array<T> / T[] (as special cases; user-defined generics are v0.3.0)
  • Exhaustive pattern matching: all pattern kinds (see Pattern Kinds)
  • Control flow: if/else, while, for, for-in (arrays and ranges only), loop, break/continue, return
  • Type casting: as for Int <-> Float
  • Never type (!)
  • Tuples
  • Built-in functions (see Built-in Functions)

Not included (v0.3.0+):

  • User-defined generic functions and types (see Generics)
  • User-defined aspects and impl Aspect for Type (see Aspects)
  • From-based ? coercion across different error types (see The ? Operator)
  • User-defined Iterable<T> implementations (see For-In)