l
GitHub ↗

Features

Where l stands,
row by row.

Compare l with other languages across execution, memory, language features, and integrated tools.

Compare with
Language and toolchain feature comparison
FeaturelCC++RustZigGoJavaC#TypeScriptPythonSwiftPHP
Execution

Execution

Compiled (native AOT)

Ahead-of-time compilation produces native machine code before a program runs, rather than executing bytecode or compiling hot paths at runtime. It matters for predictable startup, native deployment, and optimization before release. Packaging an interpreter inside an executable does not make the program native AOT.

Java · Supported

Compiled (native AOT)

GraalVM Native Image supplies native AOT. Ordinary javac bytecode compilation alone does not.

Source and details

C# · Supported

Compiled (native AOT)

.NET Native AOT produces native executables in supported publish configurations.

Source and details

Execution

Compile Time Execution

Compile-time execution runs ordinary user-written functions during compilation to compute values, types, or layouts before the program starts. It moves table generation, validation, and configuration out of runtime startup and lets the compiler check the results early. Folding literal constant expressions, or running a separate code generator, does not execute the program's own functions at compile time; restricting compile-time values to constant expressions is partial support.

C · Not supported

Compile Time Execution

C23 constexpr objects and constant expressions are folded by the compiler, but user functions cannot run at compile time.

Source and details

Rust · Supported

Compile Time Execution

const fn, const items, and const generics evaluate user code at compile time within const-context restrictions.

Source and details

Swift · Partial support

Compile Time Execution

@const compile-time values are limited to constant expressions, and macros run compiler plugins over syntax; ordinary Swift functions are not evaluated at compile time.

Source and details

Execution

Native cross-compilation

Cross-compilation produces native code for a different operating system or processor than the build machine. It lets one development or CI environment build for servers, devices, and other targets without running a full compiler on each of them. Distributing portable bytecode is not the same as producing a target-specific native binary.

l · Supported

Native cross-compilation

Target selection supports native code generation for other machines. Bundled linking covers Linux, macOS, and bare-metal ELF; other targets need external tools.

Source and details

C · Supported

Native cross-compilation

A target-configured GCC cross-compiler emits native code; target headers, libraries, and a linker may be needed.

Source and details

C++ · Supported

Native cross-compilation

A target-configured GCC C++ toolchain cross-compiles native code with matching target libraries and ABI.

Source and details

C# · Supported

Native cross-compilation

Native AOT supports documented cross-architecture combinations, subject to OS and native toolchain restrictions.

Source and details

Swift · Supported

Native cross-compilation

Swift cross-compiles with an appropriate, matching Swift SDK and target toolchain, such as the Static Linux SDK.

Source and details

Web Features

Web Features

Web Output

Web output compiles application code into a form that browsers and other web hosts execute directly: either JavaScript that an existing JavaScript runtime runs, or portable WebAssembly instructions for Wasm hosts. It matters for deploying to the web or integrating with web applications without rewriting the program. Full support includes a supported compile-and-link path to application modules in at least one of these forms; object-only emission or an experimental application path is partial. Hosting a Wasm module from another language, or porting only an interpreter to Wasm, does not compile the application's own code for the web.

l · Partial support

Web Output

The wasm32-unknown-unknown backend emits real relocatable Wasm objects, including aggregate types, but external wasm-ld and host imports are needed and l build has no end-to-end Wasm linker workflow. A JavaScript backend is planned.

Source and details; Toolchain details

C · Supported

Web Output

Clang plus external wasm-ld compiles and links C into Wasm modules; supply the target runtime/sysroot and host imports as needed. C application logic is not translated into JavaScript.

Source and details

C++ · Supported

Web Output

Clang plus external wasm-ld compiles and links C++ into Wasm modules with suitable runtime support and host imports. There is no JavaScript source backend.

Source and details

Rust · Supported

Web Output

rustc supports wasm32-unknown-unknown and WASI targets; the minimal target lacks many OS-backed standard-library operations. JavaScript binding glue is not JavaScript application output.

Source and details

Zig · Supported

Web Output

Zig 0.16 supports wasm32 targets and building Wasm modules; target/ABI choice determines available runtime and host services. Zig has no JavaScript source backend.

Source and details

Go · Supported

Web Output

GOARCH=wasm with GOOS=js or wasip1 produces Wasm applications. Browser builds need the matching wasm_exec.js host wrapper, which is not application logic in JavaScript.

Source and details

Java · Partial support

Web Output

Experimental Oracle GraalVM 25.1+ Web Image compiles applications to Wasm using --tool:svm-wasm and Binaryen behind a JavaScript launcher. There is no stable Wasm output and no JavaScript application output.

Source and details

C# · Supported

Web Output

Supported Blazor WebAssembly AOT compiles application code to Wasm with the optional wasm-tools workload and RunAOTCompilation=true; the default IL interpreter alone would not qualify. The application is not translated into JavaScript.

Source and details

TypeScript · Supported

Web Output

tsc erases TypeScript types and emits JavaScript for the configured ECMAScript target. It does not compile TypeScript into Wasm; hosting a Wasm module from emitted JavaScript is different.

Source and details

Swift · Supported

Web Output

The official, matching Swift SDK for Wasm compiles and links applications, including a full-language SDK and an optional Embedded subset. It emits Wasm, not JavaScript application source.

Source and details

Memory

Memory

Tracing-GC-free by default

This means ordinary memory management does not depend on a collector periodically tracing reachable objects to reclaim storage. It matters when collection pauses, memory headroom, or runtime size affect a system's design. It does not imply memory safety, free allocation, or zero runtime overhead; ownership, explicit deallocation, and reference counting have their own tradeoffs.

Memory

Bare-metal Support

Bare-metal programs can run without a hosted operating system, using the target's own startup and hardware interfaces. This enables firmware, kernels, and tightly constrained devices where a conventional application runtime is unavailable. It is a deployment capability, not a promise that every library or language facility works without supporting runtime code.

Rust · Supported

Bare-metal Support

no_std enables freestanding programs; target startup, panic handling, and any allocation support must be supplied.

Source and details

Swift · Supported

Bare-metal Support

Experimental Embedded Swift supplies a freestanding subset. The tick is for an available configuration, not every Swift library.

Source and details

Low-level Features

Low-level Features

First-class SIMD Support

First-class SIMD support gives application code vector types and element-wise operations that compile to the target's single-instruction, multiple-data hardware. It lets numeric, media, and parsing code process several lanes at once without hand-written assembly or per-architecture intrinsics. Full support means portable vector types in the language or standard library; architecture-specific intrinsics, compiler extensions, or experimental and incubating APIs are partial support.

C · Partial support

First-class SIMD Support

GCC vector extensions and target intrinsics expose SIMD, but standard C has no portable vector type or operations.

Source and details

C++ · Partial support

First-class SIMD Support

std::simd arrives in C++26; the compared C++23 toolchain relies on GCC vector extensions and target intrinsics.

Source and details

Rust · Partial support

First-class SIMD Support

Stable Rust exposes target intrinsics through core::arch; the portable std::simd module is nightly-only.

Source and details

Go · Partial support

First-class SIMD Support

Go 1.26's simd/archsimd package is experimental, amd64-only, and requires GOEXPERIMENT=simd; the language has no vector types.

Source and details

Java · Partial support

First-class SIMD Support

The Vector API remains an incubating jdk.incubator.vector module in JDK 26 and is not part of the standard API.

Source and details

Low-level Features

Advanced Bit Manipulation

Advanced bit manipulation goes beyond basic bitwise and shift operators to operations such as rotation, population count, leading and trailing zero counts, bit reversal, byte swapping, and explicit bit-width integers or bit-fields. These are building blocks for hashing, compression, protocols, and hardware register access, and they map to single instructions on most targets. Offering only a subset, or only on arbitrary-precision integers, is partial support.

TypeScript · Partial support

Advanced Bit Manipulation

JavaScript bitwise operators act on 32-bit integers and Math.clz32 exists, but rotate, population count, and wide fixed-width operations need manual code or BigInt.

Source and details

Python · Partial support

Advanced Bit Manipulation

int.bit_count and bit_length work on arbitrary-precision integers, but there are no rotate, leading-zero, or fixed-width integer types.

Source and details

Types

Types

Static type checking

Static type checking verifies that expressions and operations satisfy type rules before execution. Catching invalid combinations early makes refactoring safer and moves many failures out of production. Runtime checks and annotations that are not statically enforced do not provide the same assurance. Checking declaration compatibility without generally checking expression bodies is only partial static checking.

PHP · Partial support

Static type checking

PHP checks some declaration compatibility at compile time, but does not generally type-check expression bodies statically. Value type declarations are enforced at runtime.

Source and details

Types

Generics

Generics let functions or data types take user-defined type parameters, including template and compile-time type parameters. They make it possible to write reusable containers and algorithms without duplicating implementations for every element type or discarding type information.

Python · Supported

Generics

typing supports declared generic parameters. CPython does not statically enforce their use; generic declarations and static checking are separate rows.

Source and details

Types

Local variable type inference

Local inference derives a variable's static type from its initializer and surrounding constraints instead of requiring an explicit annotation at every declaration. It reduces repetitive type spelling while preserving static checking. This is different from a dynamically typed variable whose values can have unrelated types at runtime.

C · Supported

Local variable type inference

C23 adds auto inference for local declarations in supporting GCC modes; older C standards do not provide it.

Source and details

Types

Nominal Typing

Nominal typing distinguishes types by their declared identity rather than by their structure, so two declarations with identical members remain different types. It prevents accidental interchange of values that merely look alike, such as different units or identifiers with the same representation, and lets a type's name carry meaning and invariants. Structural typing, where compatible shapes are interchangeable, is the contrasting model; a language can use both for different constructs.

Go · Supported

Nominal Typing

Defined types are nominal for identity and method sets, while interface satisfaction is structural by method set.

Source and details

TypeScript · Not supported

Nominal Typing

TypeScript types are structural, so identically shaped types are interchangeable. Branding and private members only approximate nominal types.

Source and details

Python · Supported

Nominal Typing

Classes are nominal types for inheritance and isinstance checks. typing.Protocol adds opt-in structural contracts, and static enforcement is a separate row.

Source and details

Types

Compile Time Reflection

Compile-time reflection lets code inspect types, fields, variants, and other declarations during compilation and act on that information, for example to generate serializers, bindings, or validation without handwritten boilerplate. It keeps derived code in sync with the declarations it describes and avoids runtime reflection costs. Inspecting only syntax, only type-level properties, or only through a separate compiler plugin is partial support; runtime reflection is a different capability.

C++ · Partial support

Compile Time Reflection

Templates and type traits inspect type properties at compile time. Member enumeration needs C++26 static reflection, which the compared C++23 toolchain lacks.

Source and details

Rust · Partial support

Compile Time Reflection

Derive and procedural macros see an item's syntax, including field names and written types, but no resolved type information is available at compile time.

Source and details

Java · Partial support

Compile Time Reflection

Annotation processors inspect declarations and members through javax.lang.model during compilation; general reflection remains a runtime facility.

Source and details

C# · Partial support

Compile Time Reflection

Source generators inspect the Roslyn semantic model at compile time; ordinary System.Reflection is a runtime facility.

Source and details

TypeScript · Partial support

Compile Time Reflection

keyof, mapped, and conditional types inspect type structure during checking, but types are erased and nothing reaches emitted code.

Source and details

Swift · Partial support

Compile Time Reflection

Swift macros inspect attached declarations' syntax during compilation; Mirror-based reflection is a runtime facility.

Source and details

Object Oriented Programming Features

Object Oriented Programming Features

Methods

Methods associate operations with a type or a receiver value. They keep a type's behavior close to its data model and make its supported operations easier to discover. This organization is useful for data types as well as class-based objects, without requiring an inheritance hierarchy.

Object Oriented Programming Features

Declared interfaces / traits / protocols

A declared interface specifies a named contract that implementations must satisfy, such as a set of required operations. Traits, protocols, and abstract base classes can serve this role. Explicit contracts let callers depend on capabilities instead of concrete implementations and make those requirements checkable; handwritten dispatch tables or incidental matching of operations alone do not establish a declared contract.

Object Oriented Programming Features

Interface-based polymorphism

Interface-based polymorphism lets the same client code work with different implementations through a shared declared contract. It matters for substituting components, testing with alternative implementations, and extending behavior without rewriting callers. The shared interface defines what can be used even when concrete representation and dispatch strategy differ.

Functional Programming Features

Functional Programming Features

Algebraic data types (tagged sums)

A tagged sum represents one of a defined set of alternatives, each of which can carry its own payload, with type-checked selection of the active alternative. This makes states such as success or failure explicit and helps prevent invalid combinations of fields. Full support allows user-defined alternatives through the language or standard library; a fixed empty-or-value sum is a partial form. A manually maintained tag beside unchecked storage does not qualify.

C# · Partial support

Algebraic data types (tagged sums)

Nullable<T> supplies a fixed empty-or-value sum. General user-defined tagged sums are absent; arbitrary alternatives need another representation.

Source and details

Functional Programming Features

Capturing closures

A capturing closure combines callable behavior with values or references taken from its surrounding scope. It lets a callback carry the context it needs without globals or a manually threaded context parameter. This is especially useful for deferred work and customized transformations, while making the lifetime and ownership of captured state important.

Functional Programming Features

Tail-call optimization

A tail call returns the result of another call without further work in the caller. Tail-call optimization reuses or eliminates the caller's stack frame instead of retaining it. This can keep recursive processing and chains of delegated calls from consuming a growing call stack, making recursive formulations practical for long-running computations.

C · Supported

Tail-call optimization

GCC can optimize sibling/tail calls with suitable optimization settings and targets; this is not a language guarantee.

Source and details

C++ · Supported

Tail-call optimization

GCC can optimize eligible tail calls; destructors, cleanup work, options, and target constraints can prevent it.

Source and details

Rust · Supported

Tail-call optimization

Ordinary tail calls may be optimized by the backend, but stable Rust has no general guaranteed tail-call facility.

Source and details

Zig · Supported

Tail-call optimization

Zig supports tail-call control for eligible calls; target and calling-convention restrictions still apply.

Source and details

C# · Supported

Tail-call optimization

The .NET JIT can optimize eligible tail calls; support depends on runtime, target, and remaining caller work.

Source and details

TypeScript · Supported

Tail-call optimization

Emitted JavaScript can use qualifying strict-mode proper tail calls in JavaScriptCore. Node/V8 does not provide this support.

Source and details

Swift · Supported

Tail-call optimization

Optimized Swift can eliminate eligible tail calls; outstanding ARC or other cleanup can prevent it.

Source and details

Effects

Effects

Static Effect System

A static effect system records in a function's type which side effects it may perform, such as raising errors, suspending, performing I/O, or mutating state, and checks those effects at compile time. It lets callers see and constrain what a function can do, and makes pure code distinguishable from effectful code. Fixed, compiler-checked markers for a few effects, such as checked exceptions or async and throws specifiers, are partial support; a full system is general or user-extensible.

Rust · Partial support

Static Effect System

async, const fn, and unsafe are fixed, compiler-checked capability markers; there is no general or user-definable effect system.

Source and details

Zig · Partial support

Static Effect System

Error sets and inferred error unions statically track which errors a function can return; other effects are not tracked.

Source and details

Java · Partial support

Static Effect System

Checked exceptions statically track the throwing effect in method signatures; no other effects are tracked.

Source and details

Swift · Partial support

Static Effect System

async, throws, and typed throws are fixed effect specifiers checked by the compiler; there is no general effect system.

Source and details

Effects

Effect Handlers

Effect handlers let a program define its own effects, perform them from arbitrary code, and interpret them in an enclosing handler that can resume the computation where the effect was performed. They generalize exceptions, generators, and async into one mechanism, and let libraries such as schedulers and mock implementations be swapped by changing the handler. Exceptions that unwind without resumption and one-shot coroutines are not effect handlers.

Concurrency

Concurrency

Async Programming

Asynchronous programming lets a program start operations that wait on external events, such as I/O or timers, without blocking a thread, and resume where it left off when the result arrives. Language-level support such as async/await or coroutines lets that code read sequentially instead of as nested callbacks. Library-only futures, cooperative coroutines without asynchronous I/O, or runtime-managed blocking without asynchronous syntax are partial support.

C++ · Supported

Async Programming

C++20 coroutines provide co_await, co_yield, and co_return; the standard library supplies no default executor.

Source and details

Rust · Supported

Async Programming

async fn and .await are language features; running futures requires an executor, typically from a library.

Source and details

Zig · Partial support

Async Programming

std.Io supplies io.async, await, and cancelation as library calls, but Zig 0.16 ships only the thread-backed Io.Threaded implementation and has no language-level async syntax.

Source and details

Go · Partial support

Async Programming

Go has no async/await; goroutines, channels, and select express concurrent work with runtime-managed blocking.

Source and details

Java · Partial support

Async Programming

Java has no async/await; CompletableFuture and virtual threads provide library and runtime asynchrony.

Source and details

PHP · Partial support

Async Programming

Fibers supply cooperative coroutines; asynchronous I/O and scheduling need an external event loop library.

Source and details

Concurrency

Fearless Concurrency

Fearless concurrency means the compiler rejects data races on shared memory before the program runs, so concurrent code that compiles cannot corrupt shared state through unsynchronized access. It removes a class of intermittent bugs that runtime detectors can only sample. Runtime race detectors, single-threaded execution models, and synchronization discipline enforced by convention do not provide this guarantee.

Swift · Supported

Fearless Concurrency

Swift 6 language mode enforces Sendable and actor isolation at compile time to prevent data races.

Source and details

Concurrency

Green Threads

Green threads are lightweight threads scheduled by the language runtime and multiplexed over a smaller number of operating-system threads. They make it practical to have very many concurrent activities, such as one per connection, without the memory and switching costs of OS threads. Tasks that suspend only at explicit await points, or an experimental scheduler, are partial support; OS threads and single-threaded coroutines are not green threads.

Zig · Partial support

Green Threads

Io.Evented implements M:N green threads with stackful coroutines but is experimental and incomplete in Zig 0.16; Io.Threaded uses OS threads.

Source and details

Python · Not supported

Green Threads

threading uses OS threads and asyncio tasks are explicit coroutines; greenlet-style libraries are third-party.

Source and details

Swift · Partial support

Green Threads

Swift tasks are runtime-scheduled on a cooperative thread pool but suspend only at explicit await points, not at arbitrary blocking calls.

Source and details

Safety

Safety

Compile-time reference lifetime checking

Lifetime checking enforces static rules that prevent a reference from outliving the storage it can access. It catches dangling-reference errors before execution and helps make borrowed access safe without relying solely on runtime memory management. Best-effort diagnostic warnings are not the same as an enforced validity rule. Full support covers ordinary borrowed references; checking only specialized scoped or reference-like value families is partial support.

C# · Partial support

Compile-time reference lifetime checking

ref safety enforces escape/lifetime rules for scoped and by-reference values. It is not general borrowing analysis for all objects or unsafe pointers.

Source and details

Swift · Partial support

Compile-time reference lifetime checking

Compile-time checks cover nonescapable values and safe interoperation. This is not a general lifetime discipline for all values and unsafe pointers.

Source and details

C interoperation

C interoperation

C Interoperability

C interoperability covers three capabilities. C ABI interoperability lets code call or expose functions using compatible calling conventions, data layouts, and linker symbols, giving access to native libraries, operating-system APIs, and components written in other languages. Direct header import preprocesses C headers and exposes supported declarations without manually rewriting their signatures, so bindings do not drift away from the library they describe. Automatic header export generates C declarations for selected C-compatible APIs defined in the language, so native libraries are easier to consume. Full support provides all three in the bundled toolchain; lacking direct header import or automatic header export, or offering only a narrow or experimental FFI, is partial support.

l · Partial support

C Interoperability

C-compatible scalar and raw-pointer calls work through the native ABI; by-value C aggregates and variadic calls are not supported. @CType maps selected quoted declarations rather than preprocessed headers, and bundle --header emits an l header, not a C header; whole-header import and C header export are planned.

Source and details

C · Supported

C Interoperability

The C compiler implements its target's native calling conventions and data layout, and the preprocessor consumes C headers directly. Header export is the language's own declaration syntax; -aux-info additionally emits prototypes.

Source and details; Toolchain details

C++ · Partial support

C Interoperability

C linkage and compatible types support C ABI calls, and compatible C headers can be included directly. The compared toolchain does not generate a C API header; handwritten extern C headers are not automatic export.

Source and details; Toolchain details

Rust · Partial support

C Interoperability

extern C declarations support native calls behind unsafe boundary code, but foreign signatures must be written or generated by separate bindgen, and C header export uses separate cbindgen rather than the bundled toolchain.

Source and details; Toolchain details

Zig · Partial support

C Interoperability

Extern declarations, C-compatible types, and build-system C translation support native calls and direct header import. The -femit-h option has a documented nonfunctional implementation, so headers are not exported.

Source and details; Toolchain details

Go · Supported

C Interoperability

cgo calls C through generated bridge code, preprocesses included headers to expose supported declarations, and generates C declarations for exported Go functions in library build modes. It needs an external C compiler and enforces pointer/ABI restrictions.

Source and details

Java · Partial support

C Interoperability

The Foreign Function and Memory API provides native C ABI calls with explicit layouts, and Native Image generates C headers for exported entry points. CContext consumes header metadata but Java signatures must still be written; automatic binding generation requires separate jextract.

Source and details; Toolchain details

C# · Partial support

C Interoperability

P/Invoke supports C ABI calls with suitable signatures and marshalling, but declarations must be written or separately generated, and the compared .NET compiler does not generate C headers for native exports.

Source and details; Toolchain details

TypeScript · Partial support

C Interoperability

Emitted JavaScript can use experimental node:ffi in Node 26.1+ builds with --experimental-ffi, which is a Node-only FFI subset that needs supplied signatures. tsc does not import C headers or emit them.

Source and details

Python · Partial support

C Interoperability

ctypes calls C ABI functions through manually declared types and signatures; it does not preprocess C headers, and CPython does not generate C headers from Python functions. External binding generators are excluded.

Source and details

Swift · Supported

C Interoperability

The Clang importer consumes supported C declarations from headers exposed through modules and supports native calls through the C ABI. Swift 6.3's @c attribute generates C declarations for selected compatible functions and enums.

Source and details

PHP · Partial support

C Interoperability

The optional bundled FFI extension calls C functions using declared C-compatible types. FFI::load parses only a supported subset of C declarations without preprocessing or #include, and no C headers are generated from PHP code.

Source and details

Integrated tooling

Integrated tooling

Project build tooling

Project build tools coordinate program or module compilation and its dependencies beyond invoking a compiler on one file. A shared build description gives developers and CI a repeatable way to assemble an application, and can avoid rebuilding unchanged work. An individual-file compiler driver alone does not supply that project-level workflow.

Integrated tooling

Package management

Package management fetches reusable code and manages declared dependencies so a program can build on packages outside its own source tree. It makes dependency setup repeatable and reduces manual copying and path configuration. Fetching and locating packages without a managed dependency workflow is partial support; a full workflow can use explicit, content-addressed versions rather than requiring an automatic version solver.

l · Partial support

Package management

Package fetching and location work, and use links libraries found on a -L search path, but there is no managed or versioned dependency-resolution workflow.

Source and details

Python · Supported

Package management

Bundled ensurepip bootstraps pip for dependency installation/resolution. Some OS distributors package ensurepip separately.

Source and details

Integrated tooling

Test runner

A test runner discovers or executes tests and reports their outcomes through a consistent command. It makes regression checks part of the normal development and CI workflow instead of a collection of ad hoc scripts. A bundled test command counts even when a test framework is supplied as a package; source-tree maintenance harnesses alone do not.

Integrated tooling

API documentation generation

API documentation tools derive a browsable or structured description of public declarations from source code. They help users discover available types and operations while reducing the amount of reference material that must be maintained by hand. Structured output such as XML counts, even when rendering a complete documentation website is a separate step.

l · Supported

API documentation generation

The documenter emits signature-based HTML/JSON API documentation. Documentation comment text is not yet included.

Source and details

Go · Supported

API documentation generation

go doc generates textual API documentation; a rendered documentation website is not required for this row.

Source and details

C# · Supported

API documentation generation

The C# compiler emits structured XML documentation. Rendering a documentation website requires another tool.

Source and details

Supported
Partial support
Not supported
Upcoming: planned for l, not implemented
Undecided: may come to l later, not on the current roadmap