Skip to content

Release 0.2

Incan 0.2 is the release where the language surface becomes more explicit, more modular, and much more serious about Rust interop.

The headline breaking change is that stdlib imports and decorators now live under the std.* namespace. That affects everyday code immediately, but it is not cosmetic churn. It is the release boundary that moves Incan away from a compiler with lots of baked-in domain knowledge and toward a language where web, testing, async, derives, and trait surfaces are authored and imported as named library modules.

If you already know Python and Rust, the point of 0.2 is not to hide Rust from you. It is to give you a Python-shaped language with a clearer path into Rust crates, Rust types, and Rust-backed capabilities, while still treating borrowing and most Rust-specific mechanics as compiler concerns rather than user-facing bookkeeping.

That same theme shows up across the rest of the release:

  • projects can now distinguish Incan library dependencies from Rust crate dependencies in incan.toml
  • Rust interop is no longer just an escape hatch; it has a clearer authoring model, stronger docs, and much better compiler/runtime support
  • library projects can export public APIs for other Incan projects to consume
  • modules can now own runtime state through static
  • generic calls can now spell explicit call-site type arguments
  • the stdlib/compiler boundary is much cleaner than it was in 0.1

If you are migrating from 0.1, focus on the migration section first. If you want the short version, most existing projects need three concrete updates:

  1. move stdlib imports and decorators to std.*
  2. rename Rust dependency tables in incan.toml
  3. add explicit std.async / std.testing imports in files that use those language surfaces

For the core reference material, start with Imports and modules, the standard library reference, and the language reference index.

What 0.2 is about

The 0.1 line proved out the compiler, core language, and early tooling. 0.2 turns that into a more coherent platform:

  • the stdlib is no longer presented as a bag of magical global names
  • compiler features that used to feel special-cased are pushed toward import-driven library surfaces
  • the Rust boundary is more explicit: crate dependencies, rust:: imports, rusttype, interop: adapters, and trait/capability-oriented interop all fit together better than they did in 0.1
  • projects now have a clearer dependency model and a first real story for sharing Incan code across repositories
  • the language picks up a few missing “this should just exist” capabilities such as static, list concatenation, list extension, and explicit call-site generic arguments

This is still an incremental release rather than a final shape. Libraries are explicitly experimental, some runtime/library edges are still being refined, and 0.3 will continue improving ownership ergonomics and generated-runtime behavior. But 0.2 does make the language and tooling feel more coherent than 0.1.

If you want the broader conceptual backdrop, the best follow-up pages are Imports and modules, Static storage, Derives and traits, and How Incan works.

Migrating from 0.1

Use this when moving a 0.1 codebase to 0.2.

1. Move stdlib imports to std.*

All stdlib imports move from bare module names to the std.* namespace:

# Before (0.1)
from web import App, Response, Json
from testing import assert_eq, assert_ne

# After (0.2)
from std.web import App, Response, Json
from std.testing import assert_eq, assert_ne

This also applies to async modules:

# Before (0.1)
from async.time import sleep

# After (0.2)
from std.async.time import sleep

Types like App, Response, Html, Json, and Query are no longer implicitly available. Import them from std.web in each file that uses them.

See also: Imports and modules (reference), Stdlib index

2. Move decorators onto their namespaced modules

Decorators now resolve by module path rather than as ambient global names. The preferred style is to import the decorator and use the short local name:

from std.web.routing import route

@route("/hello")
async def hello() -> str:
    return "Hello!"

The canonical fully qualified path also works:

@std.web.routing.route("/hello")
async def hello() -> str:
    return "Hello!"

The same rule applies to testing markers:

import std.testing as testing

@testing.skip("not implemented")
def test_future() -> None:
    pass

@skip, @xfail, @slow, @fixture, and @parametrize are test-runner metadata from std.testing. They are not runtime function calls.

See also: std.testing (reference), Testing in Incan

3. Split Incan dependencies from Rust dependencies

0.2 separates Incan Library dependencies from Rust crate dependencies in incan.toml:

  • [dependencies] is now for Incan libraries
  • [rust-dependencies] is for Rust crates
  • [rust-dev-dependencies] replaces [dev-dependencies] for Rust dev crates

If you leave Rust crates under [dependencies], the CLI emits a targeted migration error instead of silently accepting the old structure.

This is also the release where the Rust boundary becomes easier to understand as a system: Rust crates belong in manifest-level Rust dependency tables, while Incan code imports them through from rust import ... / from rust::... import ... and can describe wrapped Rust types with rusttype.

See also: Rust interop, Understanding Rust types (coming from Python)

4. Import std.async and std.testing in the files that use them

async / await and assert are import-activated surfaces in 0.2.

If a file uses async syntax, import std.async or one of its submodules in that same file. If a file uses the assert statement or testing decorators, import std.testing in that file as well.

See also: Async programming, std.async (reference)

5. Avoid reserved namespace names

std and rust are now reserved at module scope and can no longer be used as declaration names.

Major additions

A namespaced, source-authored stdlib

The most important architectural change in 0.2 is that stdlib features are now presented as explicit modules instead of ambient compiler surface. Web, testing, async, derives, traits, and related surfaces are increasingly defined through ordinary stdlib modules and metadata rather than bespoke compiler branches. That gives users a more readable import story and gives contributors a cleaner place to evolve the standard library.

For the new stdlib surface, start with std.* module reference, std.testing, std.async, std.traits, and std.derives.

A much stronger Rust interop story

If 0.1 established that Incan could talk to Rust, 0.2 is the release where that boundary becomes much more usable in practice.

You can now describe Rust-backed types more directly, declare Rust crate dependencies more clearly, import Rust items with better diagnostics, carry more borrowed-type information through the pipeline, and rely on much stronger docs for rusttype, interop: adapters, coercions, and capability-oriented generic bounds. That matters because Rust interop is not a niche escape hatch in Incan; it is part of the main workflow for real projects.

For that part of the release, start with Rust interop, Understanding Rust types (coming from Python), Traits and derives reference, and std.traits.

Incan Libraries (Experimental)

0.2 introduces the first cut of Incan Libraries. You can now build an Incan project as a library and consume its public API from another Incan project. The current scope is intentionally conservative, but it is the first real answer to “how do I share Incan code across projects?”

Building a library

incan build --lib path/to/project

This generates a .incnlib library manifest describing the public API of your library.

Consuming a library

Add the library to your incan.toml:

[dependencies]
mylib = { path = "../mylib" }

And import it in your Incan code using the pub:: namespace prefix:

from pub::mylib import my_function

def main() -> None:
    my_function()

Library support is still early. The current release is built around local path dependencies and public API manifests, not a full package ecosystem.

For adjacent docs, the best starting points are Imports and modules and Build your first API, which show the project/module organization style 0.2 now leans into.

Module Static Storage

0.2 adds a new top-level declaration form: static (RFC 052, #242).

Use static when a module needs one live storage cell that persists across calls, for example counters, registries, caches, or shared mutable collections.

static is intentionally distinct from const:

  • const remains compile-time, deeply immutable, and baked into the output program
  • static is runtime-initialized, live, and mutable through the ordinary assignment, method, and indexing paths

This release also includes pub static, so library modules can export live shared state through library manifests and pub:: imports rather than only exporting frozen compile-time constants.

Example:

analytics.incn
pub static hits: int = 0

pub def record_hit() -> None:
    hits += 1
consumer.incn
from analytics import hits, record_hit

def main() -> None:
    record_hit()
    println(hits)  # reads the current live value

See also: Static storage (reference), Module state, Static storage (explanation)

Call-site type arguments (RFC 054, #266)

Calls and method calls may now supply explicit generic type arguments in brackets immediately before the value argument list, for example id[int](1) or box.get[int](1). The _ placeholder still allows inference for selected positions, so forms like pair_map[int, _](1, 2) remain concise. Unsupported call forms now fail clearly instead of silently ignoring explicit brackets. See Call-site type arguments for the full rules and examples.

Further reading

Other 0.2 areas that are worth reading directly in the docs are Rust interop, Reflection, Traits and derives, book chapter: modules and imports, and book chapter: traits and derives.

Detailed inventory

The sections above are the release story. The list below is the detailed inventory of language, compiler, runtime, tooling, and docs changes that shipped in 0.2, grouped roughly by theme rather than by the order work happened to land.

Core language and stdlib surface

  • Compiler: Namespaced stdlib imports under std.* — all stdlib modules use qualified paths (RFC 022).
  • Compiler: New static keyword for module-owned runtime state, plus pub static exports through library manifests and pub:: imports (RFC 052, #242). See Module Static Storage.
  • Parser/formatter: Parenthesized multi-line import lists are supported for both from module import (...) and from rust::crate import (...), with optional per-item aliases and formatter-aware wrapping (#116).
  • Compiler: Import-activated soft keywords — async and await are identifiers by default and become reserved only when import std.async (or from std.async...) is present. Forgetting the import produces a targeted diagnostic with a fix suggestion. Similarly, std.testing activates the assert statement syntax (RFC 023).
  • Compiler: Surface Semantics Engine — soft keywords (assert, async, await) and decorator semantics are routed through a registry-driven dispatch system (SurfaceSemanticsPack). The parser emits generic Surface AST nodes keyed by SurfaceFeatureKey; downstream stages dispatch on that key rather than per-keyword AST variants. Adding a new soft keyword requires no parser, AST, or IR changes — only a keyword descriptor and a semantics-pack handler (RFC 023).
  • Compiler: incan-vocab establishes a standalone, registry-driven keyword/manifest/desugaring contract for future library vocabulary extensions, and core tooling consumers now read keyword surface data through that shared registry path (RFC 027, #161).
  • Compiler: First-class named function references — named def functions can now be passed as values, stored in variables, and placed in collections without wrapping in a closure. Callable[Params, R] is accepted as syntactic sugar for (Params) -> R and desugared at parse time. Generic function references are explicitly rejected with a clear diagnostic (RFC 035, #169).
  • Compiler: Explicit call-site type arguments on direct calls and method calls (name[T](...), recv.method[T](...)), including _ placeholders for positions inferred from value arguments; unsupported call forms error instead of ignoring brackets (RFC 054, #266). See Call-site type arguments.
  • Runtime / Compiler: Strict collection indexing/method failures and strict string-to-number conversions now surface canonical Incan runtime diagnostics such as KeyError, IndexError, and ValueError instead of leaking raw backend panic wrappers (#81).
  • Compiler: Traits are now always abstract, trait names and generic trait instantiations are valid annotation types, and traits may adopt supertraits with with, enabling transitive capability hierarchies like DataSet[T] / BoundedDataSet[T] (RFC 042, #179).
  • Compiler: Namespaced decorator resolution — decorators resolved by module path (e.g., from std.web.routing import route enables @route(...)) with alias support (RFC 022).
  • Compiler: @rust.extern marker for Rust-backed function stubs, replacing the internal @std.builtin decorator (RFC 022, RFC 023).
  • Compiler: @staticmethod decorator for type-scoped methods with no self receiver, available on class, model, and newtype declarations. Required for @rust.extern methods on types (instance delegation is not supported).
  • Testing: std.testing is now surface-first: assertion and marker-facing API is defined in ordinary stdlib source, while runtime-backed helpers are kept only where required for semantics. fail_t remains the panic host boundary, marker functions keep metadata-backed runtime entry points, and assert_raises is tracked as a known blocker until parser-level assert <expr> raises <Type> support and panic payload capture are both implemented. (#302)
  • Runtime / Compiler: std.serde.json.Serialize now provides a source-defined default to_json() path for explicit with Serialize adopters, while lowering forwards that trait adoption into the matching Rust serde::Serialize derive so the direct-interop body compiles honestly. Deserialize.from_json() remains the irreducible host boundary for now. (#303)
  • Compiler / Stdlib: std.reflection now typechecks __class_name__() and __fields__() against their real surface types (str and FrozenList[FieldInfo]) instead of backend-only unknown fallbacks. Plain reflection use no longer requires importing FieldInfo; explicit type annotations still do. (#304)
  • Compiler / Stdlib: std.derives.{comparison,copying,string} no longer model derive-provided methods as rust.module() helper shims. These traits now live as source-defined capability contracts, while codegen continues to realize @derive(...) through ordinary Rust derive attributes on the adopting type. (#305)
  • Compiler / Stdlib: std.traits.* now has a curated stdlib reference entry, and std.traits.{convert,ops,error,indexing,callable,prelude} now has compiled-source coverage proving they compile directly from .incn source without rust.module() / @rust.extern helper shims. std.traits.convert now closes through @classmethod conversion hooks for from / try_from; broader Rust trait-impl authoring on wrappers remains RFC 043 work. (#306, #344)
  • Testing / Stdlib: RFC 023’s remaining behavioral-equivalence lane is now covered by an integration fixture that exercises migrated source-defined stdlib behavior end-to-end: explicit std.serde.json.Serialize adoption, std.reflection metadata, and derive-backed Clone / Eq / Ord / Default behavior on the same model. (#158)
  • Runtime / Compiler: std.math closeout alignment now keeps direct Rust interop wrappers while ensuring the module contract is consistent across typechecking, codegen, and docs (including round, log2, atan2, and hypot; uppercase constants such as math.PI) (#307).
  • Runtime / Compiler: Issue #301 std.async direct-interoperability closeout for std.async.time and std.async.select is complete. These modules now expose their runtime-facing API via direct Rust imports (clamp_seconds / clamp_millis helpers, tokio time primitives) while preserving the public std.async.* contract and error/return behavior. Remaining async modules (task, sync, channel, prelude) are still under conversion to preserve behaviorful/native wrappers.
  • Compiler: Stdlib namespace registry (STDLIB_NAMESPACES) replaces the flat stdlib-module list — convention-based submodule discovery, registry-driven unknown-module diagnostics, and soft-keyword activation metadata.
  • Compiler: Reserved root namespaces — std and rust cannot be used as user declaration names.
  • Compiler: Stdlib types require explicit imports with actionable error messages when used without import.

Rust interop, libraries, and project model

  • Compiler: from rust import <crate> syntax for importing Rust crates — the compiler validates imports against [rust-dependencies] in incan.toml and emits the correct use statements in generated Rust (RFC 013).
  • Compiler: rust:: paths accept segments whose spellings match Incan keywords (for example Rust modules named type), and from rust::... import can import such symbols — use as for a safe local name (e.g. from rust::my_crate::proto import type as proto_type) (#197).
  • Compiler: rust-inspect and generated interop metadata are now handled much more robustly. Inspection is caller-owned again instead of ambient in TypeChecker, project-local metadata is cached under target/incan_lock, borrowed Rust free-function contracts are preserved further through the pipeline, and rust-analyzer-degraded borrowed pointee types such as &? are recovered from source more reliably during interop analysis (#257, #259).
  • Tooling: incan test now avoids a large amount of repeated harness work. The generated target/incan_lock workspace is fingerprinted and reused when inputs match, test discovery/runtime metadata is cached across equivalent cases, and all collected tests from a single .incn file now run through one generated Cargo test crate instead of repeated per-test invocations (#268, #269, #271).
  • Compiler: rusttype backing types may use Rust-style qualified paths after a rust:: import binding (e.g. type Binary = rusttype proto_type::Binary when proto_type is an imported module). Generics on qualified paths (a::B[T]) are rejected at parse time with a clear diagnostic (#197).
  • Tooling: make build in the Incan repository symlinks ~/.cargo/bin/incan and ~/.cargo/bin/incan-lsp to the workspace target/debug binaries when not running under CI (opt out with INCAN_SKIP_CARGO_BIN_LINK=1), so the CLI and language server on your PATH match the checkout without a separate cargo install.
  • Compiler: Rust crate dependency management with inline version annotations, manifest-based configuration, version validation (semver), and lock files for reproducible builds (RFC 013).
  • Compiler: RFC 041 first-class Rust interop authoring surface: rusttype declarations, interop: edge blocks, and std.rust capability markers used in generic with bounds (RFC 041, #175).
  • Compiler/LSP: Imported prost oneof payloads now keep their concrete field/variant typing through CLI and editor analysis, so matches like Some(RelType.Read(read)) on imported Rust-backed fields resolve read reliably instead of degrading to placeholder T (#218).
  • Compiler: Empty list literals now preserve destination element typing in typed call, assignment, and test-runner codegen paths, avoiding generated Rust inference failures for cases like List[str] parameters receiving [] (#229).
  • Compiler: List[Self] method returns now accept list literals containing explicit instances of the enclosing class when those constructor arguments determine the same owner type, instead of degrading to List[Owner[?]] mismatches (#230).
  • Compiler: Generic model and class field access now resolves declared fields through the concrete owner instantiation, so expressions like boxed.value typecheck correctly for receivers such as Boxed[T] instead of reporting a false missing-field error (#231).
  • Tooling: LSP document symbols, hover, and completions now include newtype/rusttype and type alias declarations. Hovering over a rusttype shows the canonical Rust path of the underlying type (RFC 041).
  • Compiler: Convention-based source root resolution — the compiler and test runner resolve user module imports against src/ (or an explicit [build] source-root) so imports are uniform across source and test files (RFC 015).
  • Web: Compiler-to-stdlib routing handoff via incan_stdlib macros (RFC 022).
  • Web: Web-free compiler — all web-specific knowledge (route registration, extractor types, response builders) lives in std.web stdlib modules and the incan_web_macros proc-macro crate. The compiler uses generic decorator passthrough (@rust.extern + rust.module()) and derive passthrough (@derive(X) from rust.module() traits) with no domain-specific web logic in the compiler core (RFC 023).
  • Web: Import-driven web feature activation — importing std.web automatically enables axum, incan_web_macros, and inventory dependencies in the generated project (RFC 023).
  • Runtime: incan_stdlib::r#async now provides the narrow Rust runtime leaves behind the source-declared std.async modules, rather than exposing the async surface as a raw Tokio re-export facade.
  • Tooling: incan init scaffolds a full project: src/main.incn, tests/test_main.incn, and incan.toml with [project.scripts] main set (RFC 015).
  • Tooling: incan lock generates incan.lock for reproducible builds; --locked and --frozen flags enforce lock freshness in CI (RFC 013).
  • Tooling: Test runner resolves imports against the project source root, enabling tests to import directly from source modules (e.g., from greet import greet) without duplication (RFC 015).
  • Tooling: Test marker semantics (@skip, @xfail, @slow, @fixture, @parametrize) are derived from std.testing metadata instead of brittle string/path hardcoding (RFC 023).
  • Parser: async is allowed as a module path segment in imports (e.g., from std.async.time import sleep).
  • Diagnostics: Clearer error messages for unknown decorators and reserved namespace violations.
  • Diagnostics: Enhanced lock-out-of-date error with expected vs actual fingerprint for easier debugging.
  • Compiler: rust::core and rust::alloc are now rejected at the typechecker level with a targeted diagnostic pointing users to rust::std::... instead (reserved for future no_std/target work) (RFC 005).
  • Diagnostics: Rust interop dependency errors now include the import site path and a hint to verify the crate/module/item path or add version/features in incan.toml (RFC 005, RFC 013).
  • Docs: "Your first Incan project" tutorial covering incan init, modules, and testing.
  • Docs: Rust interoperability how-to expanded with rusttype, interop: adapters, capability bounds from std.rust, and explicit Rust-boundary coercion guidance (RFC 041, #175). The same guide now covers keyword-named Rust path segments, qualified rusttype backing paths, and the LSP page recommends make build for keeping CLI and incan-lsp aligned (#197).
  • Docs: Contributor docs now describe the registry-driven keyword path, including incan-vocab, parser Ident keyword handling, and generated VS Code grammar synchronization (RFC 027).
  • Codegen: String literals passed to external Rust type methods now emit .into() instead of .to_string(), letting the Rust compiler resolve custom string types (e.g. Polars' PlSmallStr) via the Into trait.
  • Compiler: MSRV bumped from 1.85 to 1.91 (required by the current compiler/runtime/tooling dependency set, including wasmtime 40.x and rust-analyzer metadata crates).
  • Compiler/Tooling: AST expression traversal for serde runtime detection and test-fixture teardown discovery is now centralized through a shared walker, closing false-negative gaps in contexts like elif branches and loop condition/iterator expressions (#123).
  • Compiler: Explicit generic with bounds now use nominal trait conformance (including transitive supertraits and trait-typed arguments) for non-builtin trait capabilities, so capability-constrained APIs are enforced without receiver-name special-casing (#299).

Stabilization and bugfixes

  • Compiler/Codegen: A broad ownership/materialization pass now keeps generated Rust much closer to ordinary Incan value semantics. Filtered comprehensions, Result[str, E] payloads, clone(self) -> Self struct constructors, borrowed enum loops, repeated helper calls over shared list[str] values, and list[str].append("...") all stopped requiring downstream ownership workarounds in real projects (#364, #366, #367, #372, #383, #391).
  • Parser/Compiler: Python-shaped iteration and pattern forms are now materially stronger. Qualified enum constructor patterns like ConformanceRel.Filter => resolve correctly in match arms, for idx, name in enumerate(xs): now parses and lowers correctly, and enumerate(...) emits ordinary Incan int (i64) indices instead of leaking Rust usize values through tuple-unpacked loop bindings (#374, #389).
  • Compiler/Codegen: Imported and user-defined helpers that share names with core builtins now keep their ordinary function resolution through typechecking, lowering, and Rust emission, so aggregate-builder libraries no longer need aliasing workarounds like sum as agg_sum, and async sleep now consistently follows stdlib import semantics instead of a legacy builtin side path (#377).
  • Tooling: Generated incan test harnesses now reset their runtime cwd to the owning project root, so fixture paths like tests/fixtures/orders.csv resolve the same way in single-file and batched test runs (#378).
  • Compiler: len(...) now lowers to a parse-safe usize -> i64 expression in generated Rust comparisons, so expressions like len(xs) < 2 no longer fail emission with a Rust syntax error (#380).
  • Compiler: pub from ... import ... now works in any module under src/ (not just src/lib.incn), which unblocks package submodule facades that re-export symbols for parent imports like from session import Session (#287).
  • Compiler/Codegen: Multi-file generated Rust now escapes keyword-named module declarations consistently with explicit #[path = ...] attributes, so top-level and nested modules whose names collide with Rust keywords build and run cleanly through the full pipeline (#122).
  • Compiler / Stdlib: Lists now support list + list concatenation and list.extend(other) with Python-like source-list preservation, so combining lists no longer fails at typecheck/codegen time or consumes the original bindings unexpectedly (#181).
  • Tooling: incan.toml parsing and validation errors now include manifest line/column locations across malformed TOML, conflicting dependency sections, unknown fields, and dependency-entry shape/type errors, making migration and dependency configuration failures much easier to locate (#130).
  • Tooling: incan test now isolates requested single-file runs correctly and reuses shared build/rust-inspect metadata more aggressively across sessions, which fixes cross-wired outcomes and reduces repeated cold-start overhead in real test workflows (#288).
  • Compiler/Tooling: await expr? now lowers/emits in Rust-safe order (expr.await?), and incan fmt now preserves more source intent when round-tripping code: escaped f-string newlines stay as \\n text, qualified enum/constructor match patterns stay in dot form, numeric literals keep their source spelling, blank-line normalization stays stable inside blocks, mut markers on parameters are preserved, and output ends with a single trailing newline instead of formatter-induced drift (#235, #250, #264, #289).
  • Compiler: Cyclic cross-module projects now resolve imported dependencies during per-module typechecking by import graph (not discovery order), so direct explicit call-site generic calls like collect_with_active_session[T](...) no longer fail with a false unsupported-call-form diagnostic in incan run / incan --check flows (#279).
  • Tooling/Codegen: Generated projects now resolve relative entry paths correctly and emit cleaner Rust for internal module calls and module statics, reducing false errors and compiler-noise in real project workflows.
  • Compiler/Core: Shared language registries now resolve builtin/trait/operator metadata through exhaustive enum matches instead of panic-backed .expect(...) lookups, tightening internal compiler invariants and guardrail coverage (#346).
  • Compiler: Generic instance methods on classes, traits, models, and newtypes now parse and flow through formatting, typechecking, lowering, and library export metadata correctly (#253).
  • Compiler: Locals initialized from call expressions now preserve the callee's declared result type through later field access, method chaining, and match scrutinees. This fixes false receiver-typed errors after static factory calls such as Session.default() and the related local-inference family regressions (#252, #255).
  • Tooling: incan run now defaults to Cargo's debug profile for faster cold-start iteration, supports explicit --release for optimized runs, and prints clear build-phase progress before program execution so first-run compiles no longer look hung (#276).
  • Compiler/Codegen: Checked newtype constructors no longer emit .expect(...) extraction in generated Rust. The success path is lowered as an explicit match, keeping emitted Rust valid while preserving the existing panic-on-invalid-construction contract.
  • Compiler: pub:: library consumers now preserve transitive imported method result types and trait/supertrait compatibility, so calls like lazy.collect()? keep their Result[...] contract and DataFrame[T] values satisfy DataSet[T] parameters across library boundaries (#277, #278).
  • Parser/formatter: Leading """...""" docstrings at the start of model, class, enum, trait, and newtype bodies are stored on the AST and round-trip through incan fmt instead of being discarded. Public (pub) type-like declarations expose that doc text to library tooling via exported_type_like_docs (#247).
  • Compiler: Method dispatch on generic classes and models now substitutes Self in formal parameters and return types using the instantiated receiver at the call site, so carriers like LazyFrame[Order] no longer mis-type chained calls as bare Self (#237).
  • Compiler: Call-site codegen no longer applies Incan’s generic .clone() materialization to mutable aggregate parameters (lists, dicts, and similar) that are lowered as &mut in Rust, fixing rustc E0308 mismatches (expected &mut Vec, found Vec) for recursive or loop-nested calls that re-use the same mut collections (#244).
  • Compiler: Receiver-sensitive known-method lowering prevents non-string .join(...) calls from mis-emitting as incan_stdlib::strings::str_join (#236).
  • Compiler: match on rusttype-wrapped Rust enums and oneof-shaped values now binds variant payload names inside the arm body (same scoping rules as Incan enums), fixing unknown-symbol errors when inspecting imported protobuf-style types (RFC 005, RFC 041, #217).
  • Compiler: await is rejected when it appears outside an async def or async method body, matching async/await scoping rules (#210).
  • Compiler: for ... in over a variable-bound list of enums now iterates with owned enum values in generated Rust, so comparisons such as item == some_enum compile without comparing &E to E (#195).
  • Compiler: list.pop() no longer lowers to Vec::pop().unwrap_or_default(), so element types do not need Default (e.g. Clone-only models). Popping an empty list now raises IndexError: pop from empty list, matching Python (#194).
  • Compiler: Types with @derive(Clone) now typecheck direct .clone() on concrete receivers (models and enums), matching behavior already allowed through unconstrained generics; builtin trait vocabulary stubs load method signatures from stdlib when needed (#193).
  • Diagnostics: Unknown symbol errors in f-string interpolations now point to the precise {expr} span (including nested interpolation expressions) instead of the full f-string (RFC 011, #71).
  • Compiler: Fixed project root resolving to empty string when running incan run src/main.incn from the project directory, causing "No such file or directory" errors.
  • Compiler: Trait-typed values are now correctly assignable to supertrait return types in function returns and generic upcasts with compatible type arguments (RFC 042, #184).
  • Docs: Tutorial/reference examples were updated to match the new 0.2 surface, including std.web imports, namespaced decorators, and @rust.extern usage.
  • Compiler: Trait bound inference now covers trait methods and impl methods, not just standalone functions. Generic bounds required by return types (e.g., impl BoundedDataSet<T>) are properly inferred and emitted in the Rust codegen, even when the bounds aren't used in the function body (#196).
  • Compiler/Tooling: Library builds now treat mod.incn as a directory entrypoint instead of a semantic module segment, so nested package modules no longer leak crate::<module>::r#mod paths or stale flat-vs-nested Rust files during incan build --lib (#227).

Known limitations (0.2)

  • Web route handlers and their request models must be declared pub for cross-module wrapper generation (#117).
  • Rust macro syntax (e.g., serde_json::json!(...)) is not supported in Incan source — use Rust crate functions and types directly instead.
  • incan fmt remains intentionally conservative and can still leave ordinary signatures, constructor calls, and nested expressions beyond the documented 120-character line-length target (#248). The broader formatter contract work, including vertical spacing rules, is tracked separately in RFC 053 / #336.
  • Compiler: Ownership/borrow lowering is still handled by a set of compiler heuristics rather than a complete, centralized ownership planner. 0.2 fixed the known release-blocking cases, but the broader duckborrowing design is tracked for 0.3 in #121; field-backed method-call .clone() workarounds remain one visible symptom (#241).

RFCs implemented

  • First-class named function references and Callable sugar: RFC 035
  • Namespaced stdlib modules and compiler-to-stdlib handoff: RFC 022
  • Compilable stdlib and Rust module binding (web-free compiler): RFC 023
  • Library vocabularies, registry-driven keyword activation, and vocab desugaring: RFC 027
  • Module static storage: RFC 052
  • Rust crate dependency management: RFC 013
  • Hatch-like tooling (partial: incan init, project discovery, source root resolution): RFC 015
  • Rust interop surface (rust:: imports, type mapping, string borrow adaptation, curated derives): RFC 005
  • Precise f-string interpolation diagnostics: RFC 011
  • Trait supertrait assignability and generic upcasts: RFC 042
  • Trait bound inference for trait/impl methods and return types: RFC 023
  • Explicit call-site generic arguments (including _ inference placeholders): RFC 054