Skip to content

0.703

Latest

Choose a tag to compare

@github-actions github-actions released this 12 Dec 20:12
Immutable release. Only release title and notes can be modified.
c33adf1

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 vastly improved: instead of talking about "X could not be converted into Y," we talk about "expecting Y" but "getting X."
  • 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>)
  • Fix instantiation picking unhelpful (and sometimes plain incorrect) bounds. Fixes #2125. Fixes #2118.
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 #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 #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 an NCG bug where, 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.

Co-authored-by: Andy Friesen [email protected]
Co-authored-by: Annie Tang [email protected]
Co-authored-by: Ariel Weiss [email protected]
Co-authored-by: Hunter Goldstein [email protected]
Co-authored-by: Ilya Rezvov [email protected]
Co-authored-by: Varun Saini [email protected]
Co-authored-by: Vyacheslav Egorov [email protected]