Compatibility

    Our overall goal is to incorporate features from the later versions of Lua when it makes sense for us to do so - the motivations behind some newer features are unclear or don’t apply to the domain Luau is used in, and many features carry costs that don’t always make sense to pay. The rest of this document describes the status of all features of Lua 5.2 and beyond, with the following classification:

    • ✔️ - the feature is available in Luau
    • 😞 - the feature is not available in Luau because of compatibility/sandboxing concerns
    • 🔜 - the feature is not available in Luau yet but we’d like to include it and are possibly working on it
    • 🤷‍♀️ - the feature is not available in Luau yet; we don’t have strong opinions on it so it might make it at some point

    Please note that all of these decisions are not final, they are just our current stance. In some cases evolution of our VM may make a feature that was previously impractical to support due to performance complications feasible. In some cases a feature that didn’t have a strong use case gains one, so we can implement it.

    Luau has certain limitations around the number of local variables, registers, upvalues, constants and instructions. These limits are often different from the limits imposed by various versions of Lua, and are documented here without promising that future versions will adhere to these. Note that writing code that is close to any of these limits is dangerous because this code may become invalid as our codegen evolves.

    • Local variables: 200 per function (same as all versions of Lua, this includes function arguments)
    • Upvalues: 200 per function (up from 60 in Lua 5.1)
    • Registers: 255 per function (same as all versions of Lua, this includes local variables and function arguments)
    • Constants: 2^23 per function (up from 2^18 in Lua 5.1)
    • Nested functions: 2^15 per function (down from 2^18 in Lua 5.1)
    • Stack depth: 20000 Lua calls per Lua thread, 200 C calls per C thread (e.g. coroutine.resume/pcall nesting is limited to 200)

    Note that Lua 5.3 has a larger upvalue limit (255) and a larger constant limit (2^26); existing Luau limits are likely sufficient for reasonable use cases.

    Lua 5.1

    Sandboxing challenges are .

    Two things that are important to call out here are various new metamethods for tables and yielding in metamethods. In both cases, there are performance implications to supporting this - our implementation is very highly tuned for performance, so any changes that affect the core fundamentals of how Lua works have a price. To support yielding in metamethods we’d need to make the core of the VM more involved, since almost every single “interesting” opcode would need to learn how to be resumable - which also complicates future JIT/AOT story. Metamethods in general are important for extensibility, but very challenging to deal with in implementation, so we err on the side of not supporting any new metamethods unless a strong need arises.

    For __pairs/__ipairs, we aren’t sure that this is the right design choice - self-iterating tables via __iter are very appealing, and if we can resolve some challenges with array iteration order, that would make the language more accessible so we may go that route instead.

    Ephemeron tables may be implemented at some point since they do have valid uses and they make weak tables semantically cleaner, however the cleanup mechanism for these is expensive and complicated, and as such this can only be considered after the pending GC rework is complete.

    Lua 5.3

    If integers are taken out of the equation, bitwise operators make much less sense; additionally, bit32 library is more fully featured (includes commonly used operations such as rotates and arithmetic shift; bit extraction/replacement is also more readable). Adding operators along with metamethods for all of them increases complexity, which means this feature isn’t worth it on the balance.

    Floor division is less harmful, but it’s used rarely enough that math.floor(a/b) seems like an adequate replacement; additionally, // is a comment in C-derived languages and we may decide to adopt it in addition to at some point.

    Lua has a beautiful syntax and frankly we’re disappointed in the <const>/<close> which takes away from that beauty. Taking syntax aside, <close> isn’t very useful in Luau - its dominant use case is for code that works with external resources like files or sockets, but we don’t provide such APIs - and has a very large complexity cost, evidences by a lot of bug fixes since the initial implementation in 5.4 work versions. <const> in Luau doesn’t matter for performance - our multi-pass compiler is already able to analyze the usage of the variable to know if it’s modified or not and extract all performance gains from it - so the only use here is for code readability, where the <const> syntax is… suboptimal.

    If we do end up introducing const variables, it would be through a const var = value syntax, which is backwards compatible through a context-sensitive keyword similar to type.

    Differences from Lua

    • Tail calls are not supported to simplify implementation, make debugging/stack traces more predictable and allow deep validation of caller identity for security
    • Order of table assignment in table literals follows program order in mixed tables (Lua 5.x assigns array elements first in some cases)
    • Equality comparisons call __eq metamethod even when objects are rawequal (which matches other metamethods like <= and facilitates NaN checking)
    • expressions may reuse a previously created closure in certain scenarios (when all upvalues captured are the same) for efficiency, which changes object identity but doesn’t change call semantics – this is different from Lua 5.1 but similar to Lua 5.2/5.3
    • os.time returns UTC timestamp when called with a table for consistency