news-2022-08-29-luau-recap-august-2022.md
August 29, 2022
[Cross-posted to the Roblox Developer Forum.]
__len metamethodSee the RFC Support __len metamethod for tables and rawlen function for more details.
With generalized iteration released in May, custom containers are easier than ever to use. The only thing missing was the fact that tables didn’t respect __len.
Simply, tables now honor the __len metamethod, and rawlen is also added with similar semantics as rawget and rawset:
never and unknown typesSee the RFC never and unknown types for more details.
We’ve added two new types, never and unknown. These two types are the opposites of each other by the fact that there’s no value that inhabits the type never, and the dual of that is every value inhabits the type unknown.
Type inference may infer a variable to have the type never if and only if the set of possible types becomes empty, for example through type refinements.
This is useful because we still needed to ascribe a type to x here, but the type we used previously had unsound semantics. For example, it was possible to be able to expand the domain of a variable once the user had proved it impossible. With never, narrowing a type from never yields never.
Conversely, unknown can be used to enforce a stronger contract than any. That is, unknown and any are similar in terms of allowing every type to inhabit them, and other than unknown or any, any allows itself to inhabit into a different type, whereas unknown does not.
To be able to do this soundly, you must apply type refinements on a variable of type unknown.
A use case of unknown is to enforce type safety at implementation sites for data that do not originate in code, but from over the wire.
We had a bug in the parser which erroneously allowed argument names in type packs that didn’t fold into a function type. That is, the use of Foo below did not generate a parse error when it should have.
See the announcement for more details. We include this here for posterity.
We’ve introduced a new lint called IntegerParsing. Right now, it lints three classes of errors:
For 1.) and 2.), they are currently not planned to become a parse error, so action is not strictly required here.
For 3.), this will be a breaking change! See the rollout plan for details.
We’ve also introduced a new lint called ComparisonPrecedence. It fires in two particular cases:
not X op Y where op is == or ~=, orX op Y op Z where op is any of the comparison or equality operators.In languages that uses ! to negate the boolean i.e. !x == y looks fine because !x visually binds more tightly than Lua’s equivalent, not x. Unfortunately, the precedences here are identical, that is !x == y is (!x) == y in the same way that not x == y is (not x) == y. We also apply this on other operators e.g. x <= y == y.
As a special exception, this lint pass will not warn for cases like x == not y or not x == not y, which both looks intentional as it is written and interpreted.
Fix a bug where widening was a little too happy to fire in the case of function calls returning singleton types or union thereof. This was an artifact of the logic that knows not to infer singleton types in cases that makes no sense to.
string can be a subtype of a table with a shape similar to stringThe function my_cool_lower is a function <a...>(t: t1) -> a... where t1 = {+ lower: (t1) -> a... +}.
Even though t1 is a table type, we know string is a subtype of t1 because string also has lower which is a subtype of t1’s lower, so this call site now type checks.
string.gmatch/string.match/string.find may now return more precise type depending on the patterns used: callsstring.formatfoo:bar now produces a more precise error messagestring.byte("A") is compiled to a constantlocal x = y :: T is free iff neither x nor y is mutated/captureddebug.traceback performance by 1.15-1.75x depending on the platform __newindex was defined: we now use Lua 5.2 semantics and call __ newindex, which results in less wasted space, support for NaN keys in __newindex path and correct support for frozen tablesbit32.extract/replace when width is implied (~3% faster chess)bit32.extract when field/width are constants (~10% faster base64)string.format now supports a new format specifier, %*, that accepts any value type and formats it using tostring rulesThanks for all the contributions!