Skip to content

Conversation

@hgoldstein
Copy link
Contributor

@hgoldstein hgoldstein commented Dec 12, 2025

This will be Luau's last release for the year! Happy holidays and we'll see y'all in 2026.

Analysis

  • Fix a bug in type checking functions where oversaturation (passing too many arguments to a function) was sometimes missed, for example:
type T = { method: (T, number) -> number }

local function onT(a: T)
    a:method(5, 7) -- This would not error prior
end
  • Type mismatch error wording has been improved: instead of talking about "X could not be converted into Y," we talk about "expecting X" but "getting Y."
  • Builtin type functions now use improved logic for overload resolution.
  • Type annotations on for-in loops are now respected, for example:
function my_iter(): any
    return {}
end

for index: number, value: string in my_iter() do
    -- This now errors, whereas prior we'd infer `index` to be of type `any`
    local ohno: boolean = index
end
  • Function statements for table members now have their types checked, for example the following used to not error:
local Library: { isnan: (number) -> number } = {} :: any

function Library.isnan(s: string): boolean
    return s == "NaN"
end
  • Nil-able lambdas can now participate in bidirectional inference, for example:
local listdir: (string, ((string) -> boolean)?) -> { string } = nil :: any
listdir("my_directory", function (path)
    -- `path` will now have type `string` here
end)
  • Internal compiler errors when operating on exceptionally large types should be less common, thanks to some non-recursive implementations of common visitors.
  • Fix a crash that could occur when deserializing user defined type functions, one such example being:
type function identity(t: type)
    return t
end
type func<parameters...> = typeof(function(...: parameters...) end)
local whomp: <T>(arg1: T) -> identity<T>
whomp(function(...) end :: func<any>)
local foo: <P>(constructor: (P) -> any) -> (P) -> any = (nil :: any)
-- Prior `fn` would be of type `(never) -> any`, and will now be the more useful
-- ({ test: true }) -> any
local fn = foo(function (value: { test: true })
    return value.test
end)
  • Fix a class of constraint solving incomplete errors that could occur when referencing globals inside lambdas.
  • types.unionof and types.intersectionof will now attempt to simplify their arguments, avoiding producing nested intersections and unions, as well as removing identity types like never and unknown.
  • The setmetatable type function now waits for its arguments before resolving, fixing a class of constraint ordering bugs. Fixes New solver: return value type is always any in some cases #2106:
local MyClass = {}
local MyClassMetatable = table.freeze({  __index = MyClass })

type MyClass = setmetatable<{ name: string }, typeof(MyClassMetatable)>

function MyClass.new(name: string): MyClass
    return setmetatable({ name = name }, MyClassMetatable)
end

function MyClass.hello(self: MyClass): string
    return `Hello, {self.name}!`
end

local instance = MyClass.new("World")
 -- This would sometimes be inferred as `any` due to constraint ordering bugs.
local g = instance:hello()

Native Codegen

We now internally encode true float registers, as opposed to treating all floats as doubles (as Luau does). This allows us to skip conversions to and from a double when lowering operations on floats. Additionally, we've added several optimizations for converting in between different kinds of numbers (32-bit integers, 32-bit unsigned integers, floats, doubles, etc.).

Consequently, an important notice for users of CodeGen::HostIrHooks: we are updating the behavior of LOAD_FLOAT, DOT_VEC, EXTRACT_VEC, BUFFER_READF32, BUFFER_WRITEF32, and STORE_VECTOR to produce or consume single floating-point number instead of a double. When FFlag::LuauCodegenSplitFloat is enabled, FLOAT_TO_NUM and NUM_TO_FLOAT have to be used for explicit conversions.

  • Double the amount of registers that are spilt for ints and floats on x64. This fixes NCG failing to compile snippets that end up generating many transient numbers. Fixes Native code generation issues with buffers #1468.
  • Fix a bug where NCG might improperly claim a stored value (and its tag) can be elided after a GC check.
  • NCG now optimizes buffer and userdata loads that can be computed from previous stores (e.g. if you write 42 to the nth byte in a buffer and then immediately read said byte).
  • NCG will now try to remove unused temporary userdata allocations
  • Fix a bug where NCG might claim a spilt register has been restored but not actually do so, causing a garbage read from a register.
  • NCG now optimizes upvalue loads and stores.
  • NCG now optimizes and and or without generating basic blocks, allowing more optimizations to occur.
  • Fix a bug where NCG on arm64, restoring spilling registers may require using an (already occupied) temporary register.
  • Runtime type checks internal to a function that are redundant with type checks added through annotations are now elided.
  • NCG had an off-by-one bug where loop step detection was not kicking in for non-constant end ranges in for-in loops: i = 1,10 would be optimized and i = 1,#t would not be.

Require

  • Added a to_alias_override callback: this allows embedders to provide an alias that cannot be overridden by users.

hgoldstein and others added 30 commits January 10, 2025 09:13
Note: Fixed conflicts by hand in:
- Analysis/src/ConstraintGenerator.cpp
- CodeGen/src/OptimizeConstProp.cpp
- VM/src/lmathlib.cpp
- tests/Conformance.test.cpp
a03c92095f6 #unflagged CLI-141149 Added lua_clonetable (#97902)
d66c43f36aa #flagged CLI-140903: Do not crash on duplicate keys in table literals (#97833)
8a2687f1690 #nonprod Luau: fix a test configuration issue breaking OSS CI. (#97878)
7add6d9dde9 #nojira CI-debugger revert #unflagged CLI-132461 Luau: refactor subtyping to also include the generic mapping it came up with. (#97872)
0c731fd3cc4 #flag-removal Clip `LuauNewSolverVisitErrorExprLvalues` (#96942)
2fe783b9615 #unflagged CLI-132461 Luau: refactor subtyping to also include the generic mapping it came up with. (#97803)
0cc41a0afae #unflagged CLI-141053 Luau buffer readbits/writebits implementation for big endian machines (#97826)
ae50bf04f99 #nonprod CLI-140027 Simplify require-by-string path resolution, fix bug when running CLI tools on unprefixed path (#97468)
77004599a6f #flag-removal Cleanup Luau VM and Compiler flags (#97799)
86777e269dd #flag-removal Remove LuauUserTypeFunPrintToError, LuauUserTypeFunNoExtraConstraint, LuauUserTypeFunUpdateAllEnvs, LuauUserTypeFunThreadBuffer and LuauUserTypeFunExportedAndLocal (#97624)
349b133fdc4 #nojira CI-debugger revert #unflagged CLI-132461 Luau: refactor subtyping to also include the generic mapping it came up with. (#97759)
2e820646ffa #flagged CLI-139615: Treat user defined type functions as opaque in eqSatSimplify (#96897)
c0845205e37 #flag-removal CLI-140688 Clean up `FFlagLuauIntersectNormalsNeedsToTrackResourceLimits` as `true`. (#97719)
3e07876a043 #flagged CLI-140571: Track interior free table types generated during constraint solving (#97498)
199b558dc36 #unflagged CLI-132461 Luau: refactor subtyping to also include the generic mapping it came up with. (#95646)
fd9255f62d3 #nonprod CLI-140762 unittest for fragment ac crash (#97669)
e9c710e017f #flag-removal FFlagLuauStoreCommentsForDefinitionFiles (#93359)
b184b940d55 CLI-140702 #unflagged Fix most clang-tidy warnings in TypeChecker2 (#97604)
1fc857f1bb3 CLI-140489 #unflagged Fix a potential hash collision bug in StringCache. (#97539)
58f62f900a2 #nonprod Some new unit testing macros for Luau tests. (#97336)
ab43a354b25 #flag-removal Remove LuauVectorMetatable and LuauVectorDefinitionsExtra (#97528)
c4c28694390 #flagged CLI-140485 Do not retain the Def/RefinementKey arenas when retainFullTypeGraphs is false (#97422)
This test fails due to a bad interaction when `FFlagLuauStoreCSTData` is
enabled, whilst `FFlagLexerFixInterpStringStart` is disabled.
The OSS test suite, when run with `--fflags=true`, enables all
flags starting with a `Luau` prefix, but does not enable any other flag.

To resolve this, we explicitly enable both fflags for the failing
interpolated string test cases. As a consequence, we technically lose
the check that these tests pass when all flags are disabled.
hgoldstein and others added 22 commits October 24, 2025 12:32
Hello all! Another week, another release!

* Fixed `table.clone` in the old solver to ensure intersections of tables were being entirely cloned: prior only the _first_ table in the intersection would be copied:
```
type FIRST = { some: string }
type SECOND = FIRST & { thing: string }
local b: SECOND
-- c's type used to be FIRST, but should be the full type of SECOND
local c = table.clone(b)
```
* Fixed `table.clone` in the old solver to be more permissive and treat a variadic return as having _at least_ one element. This works around some unfortunate behavior in the old solver version of nonstrict mode, see:
```
-- A.luau
--!nonstrict
return function()
    return {}
end
-- B.luau
local A = require("A")
-- This line would previously error as `A` has type `() -> (...any)`, so
-- we might not be providing enough parameters to `table.clone`.
local _ = table.clone(A())
```
* Fixed a bug in the new solver where error suppression was not kicking in for indexing, as in:
```
local function f(value: any)
    if value ~= nil then
        for k = 1, #value do
            -- Previously this would not error, claiming you cannot index into a `*error-type* | ~nil`
            local _ = value[k]
        end
    end
end
```
* Fix `getmetatable` in the new solver to accept `*error-type*` and `table` as valid type inputs.
* Changed how error reporting for invalid `for ... in` loops works: for now this may result in slightly worse error messages (see the example below), but should mean error suppression is more consistent going forward:
```
function my_iter(state: string, index: number)
    return state, index
end
local my_state = {}
local first_index = "first"
-- Prior we would claim `my_state` and `first_index` are the incorrect types,
-- now we claim that `my_iter` is of the incorrect type, which is still true
-- but less helpful.
for a, b in my_iter, my_state, first_index do
end
```

* Introduced `lua_rawgetptagged` and `lua_rawsetptagged`, as well as Lua 5.2+ compatibility macros `lua_rawgetp` and `lua_rawsetp`, to be able to perform lookups into tables using tagged or untagged `lightuserdata` without additional calls and stack manipulation. This enables a more efficient lookup way for `lightuserdata` keys similar to how `lua_rawgetfield` and `lua_rawgeti` avoid extra operations for their corresponding types.
@hgoldstein hgoldstein marked this pull request as ready for review December 12, 2025 19:06
@hgoldstein hgoldstein merged commit c33adf1 into master Dec 12, 2025
8 checks passed
PhoenixWhitefire added a commit to PhoenixWhitefire/luau that referenced this pull request Dec 13, 2025
vegorov-rbx pushed a commit that referenced this pull request Dec 14, 2025
The latest sync to 703 (#2146)
added an explicit constructor to `Luau::RequireAlias`. However, this is
only a single argument constructor for the alias, and does not handle
setting the tags.

This broke luau-lsp compilation of code that did
`Luau::RequireAlias{alias, {"tagname"}}`, and now our only option is to
first construct the RequireAlias into a separate local, and then set the
tags afterwards, then pass the require alias to where we were using it
originally.

For simplicity, this PR adds a two-argument explicit constructor to also
set the tags
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

9 participants