# Luau Recap: June 2022

Luau is our new language that you can read more about at https://luau-lang.org.

# Lower bounds calculation

EDIT: I have just discovered that this is not quite deployed yet. This should be available later this week. I think!

EDIT 2: The beta feature is live now! Sorry for the delay!

EDIT 3: We had to take the beta feature back down because it was causing crashes. Stay tuned!

A common problem that Luau has is that it primarily works by inspecting expressions in your program and narrowing the upper bounds of the values that can inhabit particular variables. In other words, each time we see a variable used, we eliminate possible sets of values from that variableâs domain.

There are some important cases where this doesnât produce a helpful result. Take this function for instance:

``````function find_first_if(vec, f)
for i, e in ipairs(vec) do
if f(e) then
return i
end
end

return nil
end
``````

Luau scans the function from top to bottom and first sees the line `return i`. It draws from this the inference that `find_first_if` must return the type of `i`, namely `number`.

This is fine, but things go sour when we see the line `return nil`. Since we are always narrowing, we take from this line the judgement that the return type of the function is `nil`. Since we have already concluded that the function must return `number`, Luau reports an error.

What we actually want to do in this case is to take these `return` statements as inferences about the lower bound of the functionâs return type. Instead of saying âthis function must return values of type `nil`,â we should instead say âthis function may also return values of type `nil`.â

Lower bounds calculation does precisely this. Moving forward, Luau will instead infer the type `number?` for the above function.

This does have one unfortunate consequence: If a function has no return type annotation, we will no longer ever report a type error on a `return` statement. We think this is the right balance but weâll be keeping an eye on things just to be sure.

Lower-bounds calculation is larger and a little bit riskier than other things weâve been working on so weâve set up a beta feature in Roblox Studio to enable them. It is called âExperimental Luau language features.â

Please try it out and let us know what you think!

## Known bug

We have a known bug with certain kinds of cyclic types when lower-bounds calculation is enabled. The following, for instance, is known to be problematic.

``````type T = {T?}? -- spuriously reduces to {nil}?
``````

We hope to have this fixed soon.

# All table literals now result in unsealed tables

Previously, the only way to create a sealed table was by with a literal empty table. We have relaxed this somewhat: Any table created by a `{}` expression is considered to be unsealed within the scope where it was created:

``````local T = {}
T.x = 5 -- OK

local V = {x=5}
V.y = 2 -- previously disallowed.  Now OK.

function mkTable()
return {x = 5}
end

local U = mkTable()
U.y = 2 -- Still disallowed: U is sealed
``````

# Other fixes

• Adjust indentation and whitespace when creating multiline string representations of types, resulting in types that are easier to read.
• Some small bugfixes to autocomplete
• Fix a case where accessing a nonexistent property of a table would not result in an error being reported.
• Improve parser recovery for the incorrect code `function foo() -> ReturnType` (the correct syntax is `function foo(): ReturnType`)
• Improve the parse error offered for code that improperly uses the `function` keyword to start a type eg `type T = function`
• Some small crash fixes and performance improvements

# Thanks!

A very special thanks to all of our open source contributors:

83 Likes

This topic was automatically opened after 9 minutes.

``````--!strict
local insert_service = game:GetService("InsertService")
local incorrectly_doesnt_warn: {} = insert_service + 1
``````

This will incorrectly not warn. The type of `insert_service` should be inferred to be `InsertService`, not `any`. This occurs for all objects, not just `InsertService`.

This will incorrectly warn with âExpected type table, got âInstanceâ insteadâ.

``````local function execute(object: Instance, name: string)
print(object[name])
end
``````

This wonât occur if the object is explicitly specified, so this will correctly not warn.

``````local function execute(name: string)
print(workspace[name])
end
``````
5 Likes

Unfortunately right now I think this is a necessary evil. It resolves to any because otherwise it can lead to false positive type errors when e.g. dot indexing children or mapping DataModel types to more general instance types. Autocomplete should still evaluate correctly though.

4 Likes

sir thats just LUA with a extra U

Will this be more advanced then Lua?

4 Likes

It already is: Lua doesnât have type checking, conditional expressions (sometimes), continue statements, operators like +=, and a few minor features Luau has.

14 Likes

LuaU is the language that ROBLOX uses when you script games. Its a variation of Lua that has been modified for ROBLOX.

7 Likes

Why not just have it be inferred as `InsertService | any` or `InsertService | {[string]: Instance]}`

1 Like

Hey @fun_enthusiast - I donât have permissions to post in the relevant category, so hopefully itâs alright by me posting a bug report here.
Iâve noticed that in strict-mode, the return of `Instance:FindFirstChild` returns `Instance` instead of the expected `Instance?` as per the documentation. As such, this code produces no warnings:

``````--!strict
Instance.new("Part"):FindFirstChild("Foo").Parent = workspace
``````

The expected behavior would be for `FindFirstChild` to return `Instance?`, thus emitting `Value of type Instance? could be nil` in the above code snippet.

1 Like

BTW, you can always DM bug reports to @Bug-Support in that case.

5 Likes

Iâd love to see this table intersection improvement go live in Studio, it has been present since Luau v0.523 released April 15th. This code wonât warn in Luau, but will in Studio.

``````--!strict
type a = {
name: string,
}
type b = {
identifier: number,
}
type c = a & b
type total = {
name: string,
identifier: number,
}

local function take_total(total: total) end

local value: c = {
name = "",
identifier = 1,
}
take_total(value) -- Warning in Studio.
local converted: total = value -- Warning in Studio.``````
1 Like

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.