diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..8ef604e5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# Emacs +*~ + +# MacOS +.DS_Store diff --git a/docs/_config.yml b/docs/_config.yml index bd9cec37..2c1b28db 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -20,3 +20,11 @@ defaults: layout: "single" sidebar: nav: "pages" + # _posts + - scope: + path: "" + type: "posts" + values: + layout: single + related: true + show_date: true diff --git a/docs/_data/navigation.yml b/docs/_data/navigation.yml index 55234617..d60430a5 100644 --- a/docs/_data/navigation.yml +++ b/docs/_data/navigation.yml @@ -1,6 +1,6 @@ main: - - title: Home - url: / + - title: News + url: /news - title: Getting Started url: /getting-started diff --git a/docs/_pages/news.md b/docs/_pages/news.md new file mode 100644 index 00000000..80f22644 --- /dev/null +++ b/docs/_pages/news.md @@ -0,0 +1,6 @@ +--- +title: "Luau News" +permalink: /news/ +layout: posts +sidebar: "none" +--- \ No newline at end of file diff --git a/docs/_posts/2019-11-11-luau-recap-november-2019.md b/docs/_posts/2019-11-11-luau-recap-november-2019.md new file mode 100644 index 00000000..253d59c5 --- /dev/null +++ b/docs/_posts/2019-11-11-luau-recap-november-2019.md @@ -0,0 +1,108 @@ +--- +layout: single +title: "Luau Recap: November 2019" +--- + +A few months ago, we’ve released our new Lua implementation, Luau ([Faster Lua VM Released](https://devforum.roblox.com/t/faster-lua-vm-released/339587)) and made it the default for most platforms and configurations. Since then we’ve shipped many smaller changes that improved performance and expanded the usability of the VM. Many of them have been noted in release notes but some haven’t, so here’s a recap of everything that has happened in the Lua land since September! + +[Originally posted to the [Roblox Developer Forum](https://devforum.roblox.com/t/luau-recap-november-2019/).] + +## Debugger beta + +When we launched the new VM, we did it without the full debugger support. The reason for this is that the new VM is substantially different and the old implementation of the debugger (that relied on line hooks) just doesn’t work. + +We had to rebuild the low level implementation of the debugger from scratch - this is a tricky problem and it took time! We are excited to share a beta preview of this with you today. + +To use this, simply make sure that you’re enrolled in the new Lua VM beta: + +![Enable New Lua VM]({{ site.url }}{{ site.baseurl }}/assets/images/luau-recap-november-2019-option.png) + +After this you can use the debugger as usual. If you see any bugs, please feel free to report them! + +## Performance improvements + + * The for loop optimization that specializes `pairs/ipairs` now works for localized versions of these globals as well, as well as `next, table` expressions + * a^k expressions are now faster for some trivial values of k such as 2 and 0.5 + * Calling methods and accessing properties on deeply nested Roblox objects is now significantly faster than it used to be (~2x faster for objects that have an 8-deep nesting) - the cost is now independent of the hierarchy depth. + * Accessing .X/.Y/.Z properties on Vector types is now ~30% faster + * On Windows and Xbox, we’ve tuned our interpreter to be ~5-6% faster on Lua-intensive code + * For a set of builtin functions, we now support very quickly calling them from VM via a new fastcall mechanism. + +Fastcall requires the function call to be present in source as a global or localized global access (e.g. either `math.max(x, 1)` or `max(x, 1) where local max = math.max`). This can be substantially faster than normal calls, e.g. this makes SHA256 benchmark ~1.7x faster. We are currently optimizing calls to `bit32`, `math` libraries and additionally `assert` and `type`. Also, just like other global-based optimizations, this one is disabled if you use `getfenv`/`setfenv`. + +## Lua library extensions + +We’ve implemented most library features available in later versions of upstream Lua, including: + + * `table.pack` and `table.unpack` from Lua 5.2 (the latter is same as global `unpack`, the former helps by storing the true argument count in `.n` field) + * `table.move` from Lua 5.3 (useful for copying data between arrays) + * `coroutine.isyieldable` from Lua 5.3 + * `math.log` now accepts a second optional argument (as seen in Lua 5.2) for the logarithm base + +We’ve also introduced two new functions in the table library: + + * `table.create(count, value)` can create an array-like table quickly + * `table.find(table, value [, init])` can quickly find the numeric index of the element in the table + +Autocomplete support for `table.create`/`table.find` will ship next week + +## Lua syntax extensions + +We’ve started taking a look at improving the Lua syntax. To that end, we’ve incorporated a few changes from later versions of Lua into the literal syntax: + + * String literals now support `\z` (skip whitespace), `\x` (hexadecimal byte) and `\u` (Unicode codepoint) escape sequences + +and implemented a few extra changes: + + * Number literals now support binary literals, e.g. `0b010101` + * Number literals now support underscores anywhere in the literal for easier digit grouping, e.g. `1_000_000` + +Note that the literal extensions aren’t currently supported in syntax highlighter in Studio but this will be amended soon. + +## Error messages + +Error messages are slowly getting a bit of love. We’ve improved some runtime errors to be nicer, in particular: + + * When indexing operation fails, we now specify the key name or type, e.g. “attempt to index foo with ‘Health’” + * When arithmetic operations fails, we now specify the type of arithmetic operation, e.g. “attempt to perform arithmetic (add) on table and number” + +We’ve also improved some parse errors to look nicer by providing extra context - for example, if you forget parentheses after function name in a function declaration, we will now say `Expected '(' when parsing function, got 'local'`. + +We are looking into some reports of misplaced line numbers on errors in multi-line expressions but this will only ship later. + +## Correctness fixes + +There are always a few corner cases that we miss - a new Lua implementation is by necessity subtly different in a few places. Our goal is to find and correct as many of these issues as possible. In particular, we’ve: + + * Fixed some cases where we wouldn’t preserve negative zero (`-0`) + * Fixed cases where `getfenv(0)` wouldn’t properly deoptimize access to builtin globals + * Fixed cases where calling a function with >255 parameters would overflow the stack + * Fixed errors with very very very long scripts and control flow around large blocks (thousands of lines of code in a single if/for statement) + * Fixed cases where in Studio on Windows, constant-time comparisons with `NaNs` didn’t behave properly (`0/0==1`) + +Also, the upvalue limit in the new VM has been raised to 200 from 60; the limit in Lua 5.2 is 255 but we decided for now to match the local limit. + +## Script analysis + +Along with the compiler and virtual machine, we’ve implemented a new linting framework on top of Luau which is similar to our old script analysis code but is richer. In particular, we support a few more checks that are enabled by default: + + * Unreachable code warning, for cases where function provably doesn’t reach a specific point, such as redundant return after a set of if/else statements where every branch returns or errors. + * Unknown type warning, which was emitted before for `Instance.new/GetService/IsA` calls, is now also emitted when the result of `type/typeof` is compared to a string literal + * We now recognize and flag mistaken attempts to iterate downwards with a for loop (such as `for i=9,1` or `for i=#t,1` as well as cases where numeric for loop doesn’t reach the stated target (`for i=1,4.5`) + * We now detect and flag cases where in assignment expressions variables are implicitly initialized with nil or values are dropped during assignment + * “Statement spans multiple lines” warning now does not trigger on idiomatic constructs involving setting up locals in a block (`local name do ... name = value ... end`) + +We also have implemented a few more warnings for common style/correctness issues but they aren’t enabled yet - we’re looking into ways for us to enable them without too much user impact: + + * Local variables that shadow other local variables / global variables + * Local variables that are assigned but never used + * Implicit returns, where functions that explicitly return values in some codepaths can reach the end of the function and implicitly return no values (which is error-prone) + +## Future plans + +There’s a fair bit of performance improvements that we haven’t gotten to yet that are on the roadmap - this includes general VM optimizations (faster function calls, faster conditional checks, faster error handling including `pcall`) and some library optimizations (in particular, Vector3 math performance improvements). And we’re starting to look into some exciting ways for us to make performance even better in the future. + +Also we’re still working on the type system! It’s starting to take shape and we should have something ready for you by the end of the year, but you’ll learn about it in a separate post :smiley: + +As always don’t hesitate to reach out if you have any issues or have any suggestions for improvements. + diff --git a/docs/_posts/2020-01-16-luau-type-checking-beta.md b/docs/_posts/2020-01-16-luau-type-checking-beta.md new file mode 100644 index 00000000..eb6a8b79 --- /dev/null +++ b/docs/_posts/2020-01-16-luau-type-checking-beta.md @@ -0,0 +1,160 @@ +--- +layout: single +title: "Luau Type Checking Beta" +--- + +Hello! + +We’ve been quietly working on building a type checker for Lua for quite some time now. It is now far enough along that we’d really like to hear what you think about it. + +I am very happy to offer a beta test into the second half of the Luau effort. + +[Originally posted on the [Roblox Developer Forum](https://devforum.roblox.com/t/luau-type-checking-beta/).] + +## Beta Test + +First, a word of caution: In this test, we are changing the syntax of Lua. We are pretty sure that we’ve mostly gotten things right, but part of the reason we’re calling this a beta is that, if we learn that we’ve made a mistake, we’re going to go back and fix it even if it breaks compatibility. + +Please try it out and tell us what you think, but be aware that this is not necessarily our final form. 🙂 + +Beta testers can try it out by enabling the “Enable New Lua Script Analysis” beta feature in Roblox Studio. + +## Overview + +Luau is an ahead-of-time typechecking system that sits atop ordinary Lua code. It does not (yet) feed into the runtime system; it behaves like a super powerful lint tool to help you find bugs in your code quickly. + +It is also what we call a gradual type system. This means that you can choose to add type annotations in some parts of your code but not others. + +## Two Modes + +Luau runs in one of two modes: strict, and nonstrict. + +### Nonstrict Mode + +Nonstrict mode is intended to be as helpful as possible for programs that are written without type annotations. We want to report whatever we can without reporting an error in reasonable Lua code. + + * If a local variable does not have a type annotation and it is not initially assigned a table, its type is any + * Unannotated function parameters have type any + * We do not check the number of values returned by a function + * Passing too few or too many arguments to a function is ok + +### Strict Mode + +Strict mode is expected to be more useful for more complex programs, but as a side effect, programs may need a bit of adjustment to pass without any errors. + + * The types of local variables, function parameters, and return types are deduced from how they are used + * Errors are produced if a function returns an inconsistent number of parameters, or if it is passed the wrong number of arguments + +Strict mode is not enabled by default. To turn it on, you need to add a special comment to the top of your source file. +``` +--!strict +``` + +## New syntax + +You can write type annotations in 5 places: + + * After a local variable + * After a function parameter + * After a function declaration (to declare the function’s return type) + * In a type alias, and + * After an expression using the new as keyword. + +``` +local foo: number = 55 + +function is_empty(param: string) => boolean + return 0 == param:len() +end + +type Point = {x: number, y: number} + +local baz = quux as number +``` + +## Type syntax +### Primitive types + +`nil`, `number`, `string`, and `boolean` + +### any +The special type any signifies that Luau shouldn’t try to track the type at all. You can do anything with an any. + +### Tables +Table types are surrounded by curly braces. Within the braces, you write a list of name: type pairs: +``` +type Point = {x: number, y: number} +``` +Table types can also have indexers. This is how you describe a table that is used like a hash table or an array. +``` +type StringArray = {[number]: string} + +type StringNumberMap = {[string]: number} +``` + +### Functions + +Function types use a `=>` to separate the argument types from the return types. +``` +type Callback = (string) => number +``` +If a function returns more than one value, put parens around them all. +``` +type MyFunction = (string) => (boolean, number) +``` + +### Unions + +You can use a `|` symbol to indicate an “or” combination between two types. Use this when a value can have different types as the program runs. +``` +function ordinals(limit) + local i = 0 + return function() => number | nil + if i < limit then + local t = i + i = i + 1 + return t + else + return nil + end + end +end +``` + +### Options + +It’s pretty commonplace to have optional data, so there is extra syntax for describing a union between a type and `nil`. Just put a `?` on the end. Function arguments that can be `nil` are understood to be optional. +``` +function foo(x: number, y: string?) end + +foo(5, 'five') -- ok +foo(5) -- ok +foo(5, 4) -- not ok +``` + +### Type Inference + +If you don’t write a type annotation, Luau will try to figure out what it is. +``` +--!strict +local Counter = {count=0} + +function Counter:incr() + self.count = 1 + return self.count +end + +print(Counter:incr()) -- ok +print(Counter.incr()) -- Error! +print(Counter.amount) -- Error! +``` + +## Future Plans + +This is just the first step! + +We’re excited about a whole bunch of stuff: + + * Nonstrict mode is way more permissive than we’d like + * Generics! + * Editor integration \ No newline at end of file diff --git a/docs/_posts/2020-02-25-luau-recap-february-2020.md b/docs/_posts/2020-02-25-luau-recap-february-2020.md new file mode 100644 index 00000000..fc8b6e6d --- /dev/null +++ b/docs/_posts/2020-02-25-luau-recap-february-2020.md @@ -0,0 +1,93 @@ +--- +layout: single +title: "Luau Recap: February 2020" +--- + +We continue to iterate on our language stack, working on many features for type checking, performance, and quality of life. Some of them come with announcements, some come with release notes, and some just ship - here we will talk about all things that happened since November last year. + +A lot of people work on these improvements; thanks @Apakovtac, @EthicalRobot, @fun_enthusiast, @xyzzyismagic, @zeuxcg! + +[Originally posted to the [Roblox Developer Forum](https://devforum.roblox.com/t/luau-recap-february-2020/).] + +We were originally intending to ship the beta last year but had to delay it due to last minute bugs. However, it’s now live as a beta option on production! Go here to learn more: + +EDIT: Please DO NOT publish places with type annotations just yet as they will not work on production! This is why it’s a beta 🙂 However, please continue to experiment in Studio and give us feedback. We are reading everything and will be fixing reported bugs and discussing syntax / semantics issues some people brought up. Hello! We’ve been quietly working on building a type checker for Lua for quite some time now. It is now far enough along that we’d really like to hear what… + +We’re continuing to iterate on the feedback we have received here. Something that will happen next is that we will enable type annotations on live server/clients - which will mean that you will be able to publish source code with type annotations without breaking your games. We still have work to do on the non-strict and strict mode type checking before the feature can move out of beta though, in particular we’ve implemented support for require statement and that should ship next week 🤞 + +We also fixed a few bugs in the type definitions for built-in functions/API and the type checker itself: + + * `table.concat` was accidentally treating the arguments as required + * `string.byte` and `string.find` now have a correct precise type + * `typeof` comparisons in if condition incorrectly propagated the inferred type into `elseif` branches + +We are also making the type checker more ergonomic and more correct. Two changes I want to call out are: + + * Type aliases declared with `type X = Y` are now co-recursive, meaning that they can refer to each other, e.g. + +``` +type array = { [number]: T } + +type Wheel = { radius: number, car: Car } +type Car = { wheels: array } +``` + +* We now support type intersections `(A & B)` in addition to type unions `(A | B)`. Intersections are critical to modeling overloaded functions correctly - while Lua as a language doesn’t support function overloads, we have various APIs that have complex overloaded semantics - one of them that people who tried the beta had problems with was UDim2.new - and it turns out that to correctly specify the type, we had to add support for intersections. This isn’t really intended as a feature that is used often in scripts developers write, but it’s important for internal use. + +## Debugger (beta) + +When we shipped the original version of the VM last year, we didn’t have the debugger fully working. Debugger relies on low-level implementation of the old VM that we decided to remove from the new VM - as such we had to make a new low-level debugging engine. + +This is now live under the Luau VM beta feature, see [this post](https://devforum.roblox.com/t/luau-in-studio-beta/456529) for details. + +If you use the debugger at all, please enable the beta feature and try it out - we want to fix all the bugs we can find, and this is blocking us enabling the new VM everywhere. + +(a quick aside: today the new VM is enabled on all servers and all clients, and it’s enabled in Studio “edit” mode for plugins - but not in Studio play modes, and full debugger support is what prevents us from doing so) + +## Language + +This section is short and sweet this time around: + +* You can now use continue statement in for/while/repeat loops. :tada: + +Please note that we only support this in the new VM, so you have to be enrolled in Luau VM beta to be able to use it in Studio. It will work in game regardless of the beta setting as per above. + +## Performance + +While we have some really forward looking ideas around multi-threading and native code compilation that we’re starting to explore, we also continue to improve performance across the board based on our existing performance backlog and your feedback. + +In particular, there are several memory and performance optimizations that shipped in the last few months: + + * Checking for truth (`if foo or foo and bar`) is now a bit faster, giving 2-3% performance improvements on some benchmarks + * `table.create` (with value argument) and `table.pack` have been reimplemented and are ~1.5x faster than before + * Internal mechanism for filling arrays has been made faster as well, which makes `Terrain:ReadVoxels` ~10% faster + * Catching engine-generated errors with pcall/xpcall is now ~1.5x faster (this only affects performance of calls that generated errors) + * String objects now take 8 bytes less memory per object (and in an upcoming change we’ll save a further 4 bytes) + * Capturing local variables that are never assigned to in closures is now much faster, takes much less memory and generates much less GC pressure. This can make closure creation up to 2x faster, and improves some Roact benchmarks by 10%. This is live in Studio and will ship everywhere else shortly. + * The performance of various for loops (numeric & ipairs) on Windows regressed after a VS2017 upgrade; this regression has been fixed, making all types of loops perform roughly equally. VS2017 upgrade also improved Luau performance on Windows by ~10% across the board. + * Lua function calls have been optimized a bit more, gaining an extra 10% of performance in call-heavy benchmarks on Windows. + * Variadic table constructors weren’t compiled very efficiently, resulting in surprisingly low performance of constructs like `{...}`. Fixing that made `{...}` ~3x faster for a typical number of variadic arguments. + +## Diagnostics + +We spent some time to improve error messages in various layers of the stack based on the reports from community. Specifically: + + * The static analysis warning about incorrect bounds for numeric for loops is now putting squigglies in the right place. + * Fixed false positive static analysis warnings about unreachable code inside repeat…until loops in certain cases. + * Multiline table construction expressions have a more precise line information now which helps in debugging since callstacks are now easier to understand + * Incomplete statements (e.g. foo) now produce a more easily understandable parsing error + * In some cases when calling the method with a `.` instead of `:`, we emitted a confusing error message at runtime (e.g. humanoid.LoadAnimation(animation)). We now properly emit the error message asking the user if `:` was intended. + * The legacy global `ypcall` is now flagged as deprecated by script analysis + * If you use a Unicode symbol in your source program outside of comments or string literals, we now produce a much more clear message, for example: +``` +local pi = 3․13 -- spoiler alert: this is not a dot! +``` +produces `Unexpected Unicode character: U+2024. Did you mean '.'?` + +## LoadLibrary removal + +Last but not least, let’s all press [F for LoadLibrary](https://devforum.roblox.com/t/loadlibrary-is-going-to-be-removed-on-february-3rd/382516). + +It was fun while it lasted, but supporting it caused us a lot of pain over the years and prevented some forward-looking changes to the VM. We don’t like removing APIs from the platform, but in this case it was necessary. Thanks to the passionate feedback from the community we adjusted our initial rollout plans to be less aggressive and batch-processed a lot of gear items that used this function to stop using this function. The update is in effect and LoadLibrary is no more. + +As usual, if you have any feedback about any of these updates, suggestions, bug reports, etc., post them in this thread or (preferably for bugs) as separate posts in the bug report category. diff --git a/docs/_posts/2020-05-18-luau-recap-may-2020.md b/docs/_posts/2020-05-18-luau-recap-may-2020.md new file mode 100644 index 00000000..a2b4cdbc --- /dev/null +++ b/docs/_posts/2020-05-18-luau-recap-may-2020.md @@ -0,0 +1,100 @@ +--- +layout: single +title: "Luau Recap: May 2020" +--- + +Luau (lowercase u, “l-wow”) is an umbrella initiative to improve our language stack - the syntax, compiler, virtual machine, builtin Lua libraries, type checker, linter (known as Script Analysis in Studio), and more related components. We continuously develop the language and runtime to improve performance, robustness and quality of life. Here we will talk about all things that happened since the update in March! + +[Originally posted on the [Roblox Developer Forum](https://devforum.roblox.com/t/luau-recap-may-2020/).] + +## New function type annotation syntax + +As noted in the previous update, the function type annotation syntax now uses `:` on function definitions and `->` on standalone function types: +``` +type FooFunction = (number, number) -> number + +function foo(a: number, b: number): number + return a + b +end +``` +This was done to make our syntax more consistent with other modern languages, and is easier to read in type context compared to our old `=>`. + +This change is now live; the old syntax is still accepted but it will start producing warnings at some point and will be removed eventually. + +## Number of locals in each function is now limited to 200 +As detailed in [Upcoming change to (correctly) limit the local count to 200](https://devforum.roblox.com/t/upcoming-change-to-correctly-limit-the-local-count-to-200/528417) (which is now live), when we first shipped Luau we accidentally set the local limit to 255 instead of 200. This resulted in confusing error messages and code that was using close to 250 locals was very fragile as it could easily break due to minor codegen changes in our compiler. + +This was fixed, and now we’re correctly applying limits of 200 locals, 200 upvalues and 255 registers (per function) - and emit proper error messages pointing to the right place in the code when either limit is exceeded. + +This is technically a breaking change but scripts with >200 locals didn’t work in our old VM and we felt like we had to make this change to ensure long-term stability. + +## Require handling improvements in type checker + export type + +We’re continuing to flesh out the type checker support for modules. As part of this, we overhauled the require path tracing - type checker is now much better at correctly recognizing (statically) which module you’re trying to require, including support for `game:GetService`. + +Additionally, up until now we have been automatically exporting all type aliases declared in the module (via `type X = Y`); requiring the module via `local Foo = require(path)` made these types available under `Foo.` namespace. + +This is different from the explicit handling of module entries, that must be added to the table returned from the `ModuleScript`. This was highlighted as a concern, and to fix this we’ve introduced `export type` syntax. + +Now the only types that are available after require are types that are declared with `export type X = Y`. If you declare a type without exporting it, it’s available inside the module, but the type alias can’t be used outside of the module. That allows to cleanly separate the public API (types and functions exposed through the module interface) from implementation details (local functions etc.). + +## Improve type checker robustness + +As we’re moving closer to enabling type checking for everyone to use (no ETA at the moment), we’re making sure that the type checker is as robust as possible. + +This includes never crashing and always computing the type information in a reasonable time frame, even on obscure scripts like this one: +``` +type ( ... ) ( ) ; +( ... ) ( - - ... ) ( - ... ) +type = ( ... ) ; +( ... ) ( ) ( ... ) ; +( ... ) "" +``` +To that end we’ve implemented a few changes, most of them being live, that fix crashes and unbounded recursion/iteration issues. This work is ongoing, as we’re fixing issues we encounter in the testing process. + +## Better types for Lua and Roblox builtin APIs + +In addition to improving the internals of the type checker, we’re still working on making sure that the builtin APIs have correct type information exposed to the type checker. + +In the last few weeks we’ve done a major audit and overhaul of that type information. We used to have many builtin methods “stubbed” to have a very generic type like `any` or `(...) -> any`, and while we still have a few omissions we’re much closer to full type coverage. + +One notable exception here is the `coroutine.` library which we didn’t get to fully covering, so the types for many of the functions there are imprecise. + +If you find cases where builtin Roblox APIs have omitted or imprecise type information, please let us know by commenting on this thread or filing a bug report. + +The full set of types we expose as of today is listed here for inquisitive minds: [https://gist.github.com/zeux/d169c1416c0c65bb88d3a3248582cd13](https://gist.github.com/zeux/d169c1416c0c65bb88d3a3248582cd13) + +## Removal of __gc from the VM +A bug with `continue` and local variables was reported to us a few weeks ago; the bug was initially believed to be benign but it was possible to turn this bug into a security vulnerability by getting access to `__gc` implementation for builtin Roblox objects. After fixing the bug itself (the turnaround time on the bug fix was about 20 hours from the bug report), we decided to make sure that future bugs like this don’t compromise the security of the VM by removing `__gc`. + +`__gc` is a metamethod that Lua 5.1 supports on userdata, and future versions of Lua extend to all tables; it runs when the object is ready to be garbage collected, and the primary use of that is to let the userdata objects implemented in C to do memory cleanup. This mechanism has several problems: + + * `__gc` is invoked by the garbage collector without context of the original thread. Because of how our sandboxing works this means that this code runs at highest permission level, which is why `__gc` for newproxy-created userdata was disabled in Roblox a long time ago (10 years?) + * `__gc` for builtin userdata objects puts the object into non-determinate state; due to how Lua handles `__gc` in weak keys (see [https://www.lua.org/manual/5.2/manual.html#2.5.2](https://www.lua.org/manual/5.2/manual.html#2.5.2)), these objects can be observed by external code. This has caused crashes in some Roblox code in the past; we changed this behavior at some point last year. + * Because `__gc` for builtin objects puts the object into non-determinate state, calling it on the same object again, or calling any other methods on the object can result in crashes or vulnerabilities where the attacker gains access to arbitrarily mutating the process memory from a Lua script. We normally don’t expose `__gc` because the metatables of builtin objects are locked but if it accidentally gets exposed the results are pretty catastrophic. + * Because `__gc` can result in object resurrection (if a custom Lua method adds the object back to the reachable set), during garbage collection the collector has to traverse the set of userdatas twice - once, to run `__gc` and a second time to mark the survivors. + +For all these reasons, we decided that the `__gc` mechanism just doesn’t pull its weight, and completely removed it from the VM - builtin userdata objects don’t use it for memory reclamation anymore, and naturally declaring `__gc` on custom userdata objects still does nothing. + +Aside from making sure we’re protected against these kinds of vulnerabilities in the future, this makes garbage collection ~25% faster. + +## Memory and performance improvements + +It’s probably not a surprise at this point but we’re never fully satisfied with the level of performance we get. From a language implementation point of view, any performance improvements we can make without changing the semantics are great, since they automatically result in Lua code running faster. To that end, here’s a few changes we’ve implemented recently: + + * ~~A few string. methods, notably string.byte and string.char, were optimized to make it easier to write performant deserialization code. string.byte is now ~4x faster than before for small numbers of returned characters. For optimization to be effective, it’s important to call the function directly (`string.byte(foo, 5)`) instead of using method calls (`foo:byte(5)`).~~ This had to be disabled due to a rare bug in some cases, this optimization will come back in a couple of weeks. + * `table.unpack` was carefully tuned for a few common cases, making it ~15% faster; `unpack` and `table.unpack` now share implementations (and the function objects are equal to each other). + * While we already had a very efficient parser, one long standing bottleneck in identifier parsing was fixed, making script compilation ~5% faster across the board, which can slightly benefit server startup times. + * Some builtin APIs that use floating point numbers as arguments, such as various `Vector3` constructors and operators, are now a tiny bit faster. + * All string objects are now 8 bytes smaller on 64-bit platforms, which isn’t a huge deal but can save a few megabytes of Lua heap in some games. + * Debug information is using a special compact format that results in ~3.2x smaller line tables, which ends up making function bytecode up to ~1.5x smaller overall. This can be important for games with a lot of scripts. + * Garbage collector heap size accounting was cleaned up and made more accurate, which in some cases makes Lua heap ~10% smaller; the gains highly depend on the workload. + +## Library changes + +The standard library doesn’t see a lot of changes at this point, but we did have a couple of small fixes here: + + * `coroutine.wrap` and `coroutine.create` now support C functions. This was the only API that treated Lua and C functions differently, and now it doesn’t. + * `require` silently skipped errors in module scripts that occurred after the module scripts yielding at least once; this was a regression from earlier work on yieldable pcall and has been fixed. + +As usual, if you have questions, comments, or any other feedback on these changes, feel free to share it in this thread or create separate posts for bug reports. \ No newline at end of file diff --git a/docs/_posts/2020-06-20-luau-recap-june-2020.md b/docs/_posts/2020-06-20-luau-recap-june-2020.md new file mode 100644 index 00000000..18276219 --- /dev/null +++ b/docs/_posts/2020-06-20-luau-recap-june-2020.md @@ -0,0 +1,115 @@ +--- +layout: single +title: "Luau Recap: June 2020" +--- + +… otherwise known as “This Month in Luau” I guess? You know the drill by now. We’ll talk about exciting things that happened to Luau - our new language stack. + +anxiously glances at FIB3 thread that casts a huge shadow on this announcement, but hopefully somebody will read this + +Many people work on these improvements; thanks @Apakovtac, @EthicalRobot, @fun_enthusiast, @zeuxcg! + +[Originally posted on the [Roblox Developer Forum](https://devforum.roblox.com/t/luau-recap-june-2020/).] + +## We have a website! + +Many developers told us on many occasions that as much as they love the recaps, it’s hard to know what the state of the language or libraries is if the only way to find out is to read through all updates. What’s the syntax extensions that Luau supports now? How do I use type checking? What’s the status of from Lua 5.x? + +Well, you can find all of this out here now: [https://roblox.github.io/luau/](https://roblox.github.io/luau/) + +Please let us know if this documentation can be improved - what are you missing, what could be improved. For now to maximize change velocity this documentation is separate from DevHub; it’s also meant as an external resource for people who don’t really use the language but are curious about the differences and novelties. + +Also, `_VERSION` now returns "Luau" because we definitely aren’t using Lua 5.1 anymore. + +## Compound assignments + +A long-standing feature request for Lua is compound assignments. Somehow Lua never got this feature, but Luau now implements `+=`, `-=`, `*=`, `/=`, `%=`, `^=` and `..=` operators. We decided to implement them because they are absolutely ubiquitous among most frequently used programming languages, both those with C descent and those with different lineage (Ruby, Python). They result in code that’s easier to read and harder to make mistakes in. + +We do not implement `++` and `--`. These aren’t universally adopted, `--` conflicts with comment syntax and they are arguably not as intuitively obvious. We trust everyone to type a few extra characters for `+= 1` without too much trouble. + +Two important semantical notes are that the expressions on the left hand side are only evaluated once, so for example `table[makeIndex()] += 1` only runs `makeIndex` once, and that compound assignments still call all the usual metamethod (`__add` et al, and `__index`/`__newindex`) when necessary - you don’t need to change any data structures to work with these. + +There’s no noticeable performance improvement from these operators (nor does using them carry a cost) - use them when they make sense for readability. + +## Nicer error messages + +Good errors are critical to be able to use Luau easily. We’ve spent some time to improve the quality of error messages during parsing and runtime execution: + + * In runtime type errors, we now often use the “Roblox” type name instead of plain userdata, e.g. `math.abs(v)` now says `number` expected, got `Vector3` + * When arguments are just missing, we now explicitly say that they are missing in libraries like math/table; the old message was slightly more confusing + * `string.format` in some cases produced error messages that confused missing arguments for incorrect types, which has been fixed + * When a builtin function such as `math.abs` fails, we now add the function name to the error message. This is something that used to happen in Lua, then we lost this in Luau because Luau removes a very fragile mechanism that supported that, but we now have a new, robust way to report this so you can have the function name back! The message looks like this now: `invalid argument #1 to 'abs' (number expected, got nil)` + * In compile-time type errors, we now can identify the case when the field was mistyped with a wrong case (ha), and tell you to use the correct case instead. + * When you forget an `end` statement, we now try to be more helpful and point you to the problematic statement instead of telling you that the end is missing at the very end of the program. This one is using indentation as a heuristic so it doesn’t always work perfectly. + * We now have slightly more helpful messages for cases when you forget parentheses after a function call + * We now have slightly more helpful messages for some cases when you accidentally use `( ... )` instead of `{ ... }` to create a table literal +Additionally two places had very lax error checking that made the code more fragile, and we fixed those: + + * `xpcall` now fails immediately when the error function argument is not a function; it used to work up until you get an error, and failed at that point, which made it hard to find these bugs + * `tostring` now enforces the return type of the result to be a string - previously `__tostring` could return a non-string result, which worked fine up until you tried to do something like passing the resulting value to `string.format` for `%s`. Now `tostring` will fail early. +Our next focus here is better error messages during type checking - please let us know if there are other errors you find confusing and we could improve! + +## Type checker improvements + +We’re getting closer and closer to be able to move out of beta. A big focus this month was on fixing all critical bugs in the type checker - it now should never hang or crash Studio during type checking, which took a bit of work to iron out all the problems. + +Notably, typing function string.length no longer crashes Studio (although why you’d do that is unclear), and Very Large Scripts With Tons Of Nested Statements And Expressions should be stable as well. + +We’ve also cleaned up the type information for builtin libraries to make it even more precise, including a few small fixes to `string/math` functions, and a much more precise coroutine library type information. For the latter we’ve introduced a primitive type `thread`, which is what `coroutine` library works with. + +## Linter improvements + +Linter is the component that produces warnings about scripts; it’s otherwise known as “Static Analysis” in Studio, although that is now serving as a place where we show type errors as well. + +Most of the changes here this month are internal as they concern warnings that aren’t yet enabled in Studio (the web site linked above documents all warnings including ones that aren’t active yet but may become active), but once notable feature is that you can now opt out of individual warnings on a script-by-script basis by adding a --!nolint comment to the top of the script. For example, if you really REALLY *REALLY* like the `Game` global, you can add this to the top of the script: + +``` +--!nolint DeprecatedGlobal +``` +Or, if you basically just want us to not issue any warnings ever, I guess you can add this: +``` +--!nocheck +--!nolint +``` +and live happily ignorant of all possible errors up until you run your code. (please don’t do that) + +## os. enhancements + +Our overall goal is to try to be reasonably compatible with Lua 5.x in terms of library functions we expose. This doesn’t always work - in some cases we have to remove library features for sandboxing reasons, and in others the library functions don’t make sense in context of Roblox. However, some of these decisions can be revised later. In particular, when we re-added `os.` library to Roblox, we limited it to `os.date`, `os.time` and `os.difftime` (although why `difftime` is a thing isn’t clear), omitting `os.clock` and restricting inputs to `os.date` to return a table with date components, whereas Lua 5.x supports format strings. + +Well, this changes today. `os.clock` is now available if you need a high-precision time for benchmarking, and `os.date` can now return formatted date using Lua 5.x format string that you can read about here [https://www.lua.org/pil/22.1.html](https://www.lua.org/pil/22.1.html) (we support all these specifiers: aAbBcdHIjmMpSUwWxXyYzZ). + +While `os.date()` is hopefully welcome, `os.clock` may raise some eyebrows - aren’t there enough timing functions in Roblox already? Well, this is nice if you are trying to port code from Lua 5.x to Luau, and there’s this + +![Oblig. xkcd]({{ site.url }}{{ site.baseurl }}/assets/images/luau-recap-june-2020-xkcd.png) + +But really, most existing Roblox timing functions are… problematic. + + * `time()` returns the total amount of time the game has been running simulation for, it’s monotonic and has reasonable precision. It’s fine - you can use it to update internal gameplay systems without too much trouble. It should’ve been called “tick” perhaps but that ship has sailed. + * `elapsedTime` and its close cousin `ElapsedTime`, are telling you “how much time has elapsed since the current instance of Roblox was started.”. While technically true, this isn’t actually useful because on mobile the “start” time here can be days in the past. It’s also inadequate for performance measurements as on Windows, it has a 1ms resolution which isn’t really enough for anything interesting. We’re going to deprecate this in the future. + * `tick()` sounds perfect - it has a high resolution (usually around 1 microsecond), and a well-defined baseline - it counts since UNIX epoch! Or, well, it actually doesn’t. On Windows, it returns you a variant of the UNIX timestamp in local time zone. In addition, it can be off by 1 second from the actual, real UNIX timestamp, and might have other idiosyncrasies on non-Windows platforms. We’re going to deprecate this in the future + +So, if you need a UNIX timestamp, you should use `os.time()`. You get a stable baseline (from 1970’s) and 1s resolution. If you need to measure performance, you should use `os.clock()`. You don’t get a stable baseline, but you get ~1us resolution. If you need to do anything else, you should probably use `time()`. + +## Performance optimizations + +As you can never have too much performance, we’re continuing to work on performance! We’re starting to look into making Vector3 faster and improving the garbage collector, with some small changes already shipping, but overall it’s a long way out so here are the things that did get visibly better: + + * A few `string.` methods, notably `string.byte` and `string.char`, were optimized to make it easier to write performant deserialization code. string.byte is now ~4x faster than before for small numbers of returned characters. For optimization to be effective, it’s important to call the function directly ( `string.byte(foo, 5)` ) instead of using method calls ( `foo:byte(5)` ) + * Optimize coroutine resumption, making some code that is heavily reliant on `coroutine`. library ~10% faster. We have plans to improve this further, watch this space. + * Optimize `typeof()` to run ~6x faster. It used to be that `type()` was much faster than `typeof()` but they now should be more or less comparable. + * Some secret internal optimizations make some scripts a few percent faster + * The memory allocator used in Luau was rewritten using a new, more efficient, implementation. There might be more changes here in the future to save some memory, but for now this makes some allocation-intensive benchmarks ~15% faster. + * Using tables with keys that are not strings or numbers is a fair bit more efficient now (most commonly comes up when Instance is used as a key in a hash table), on par with using strings. + +Also we found a bug with some of our optimizations (which delayed the string. performance improvement above, but also could affect some math. calls) where in some complex functions you would see valid calls to math. etc. breaking with non-sensical errors such as “expected number, got table” - this has been fixed! + +## Memory optimizations + +As with performance, our goal here is simple - the more efficient internal Luau structures can become, the less memory will Lua heap take. This is great for both memory consumption, and for garbage collection performance as the collector needs to traverse less data. There’s a few exciting changes in this area this month: + + * Non-array-like tables now take 20% less space. This doesn’t affect arrays but can be observed on object-like tables, both big and small. This is great because some of you are using a lot of large tables apparently, since this resulted in very visible reduction in overall Lua heap sizes across all games. + * Function objects now take up to 30% less space. This isn’t as impactful since typically function objects are not created very frequently and/or don’t live for very long, but it’s nice nonetheless. + * New allocator mentioned in the previous section can save up to 5-6% of Lua heap memory as well, although these gains are highly dependent on the workload, and we usually see savings in the 1-2% range. + +And that’s it! Till next time. As usual let us know if you have questions, suggestions or bug reports. diff --git a/docs/_posts/2020-08-11-luau-recap-august-2020.md b/docs/_posts/2020-08-11-luau-recap-august-2020.md new file mode 100644 index 00000000..5d149c18 --- /dev/null +++ b/docs/_posts/2020-08-11-luau-recap-august-2020.md @@ -0,0 +1,93 @@ +--- +layout: single +title: "Luau Recap August 2020" +--- + +As everyone knows by now, Luau is our new language stack that you can read more about at [https://roblox.github.io/luau](https://roblox.github.io/luau) and the month following June is August so let’s talk about changes, big and small, that happened since June! + +Many people work on these improvements, with the team slowly growing - thanks @Apakovtac, @EthicalRobot, @fun_enthusiast, @mrow_pizza and @zeuxcg! + +[Originally posted on the [Roblox Developer Forum](https://devforum.roblox.com/t/luau-recap-august-2020/).] + +## Type annotations are safe to use in production! + +When we started the Luau type checking beta, we’ve had a big warning sign in the post saying to not publish the type-annotated scripts to your production games which some of you did anyway. This was because we didn’t want to commit to specific syntax for types, and were afraid that changing the syntax would break your games. + +This restriction is lifted now. All scripts with type annotations that parse & execute will continue to parse & execute forever. Crucially, for this to be true you must not be using old fat arrow syntax for functions, which we warned you about for about a month now: + +![Fat arrow deprecated]({{ site.url }}{{ site.baseurl }}/assets/images/luau-recap-august-2020-arrow.png) + +… and must not be using the `__meta` property which no longer holds special meaning and we now warn you about that: + +![meta deprecated]({{ site.url }}{{ site.baseurl }}/assets/images/luau-recap-august-2020-meta.png) + +Part of the syntax finalization also involved changing the precedence on some type annotations and adding support for parentheses; notably, you can now mix unions and intersections if you know what that means (`(A & B) | C` is valid type syntax). Some complex type annotations changed their structure because of this - previously `(number) -> string & (string) -> string` was a correct way to declare an intersection of two function types, but now to keep it parsing the same way you need to put each function type in parentheses: `((number) -> string) & ((string) -> string)`. + +Type checking is not out of beta yet - we still have some work to do on the type checker itself. The items on our list before going out of beta right now include: + + * Better type checking for unary/binary operators + * Improving error messages to make type errors more clear + * Fixing a few remaining crashes for complex scripts + * Fixing conflation of warnings/errors between different scripts with the same path in the tree + * Improving type checking of globals in nonstrict mode (strict mode will continue to frown upon globals) + +Of course this doesn’t mark the end of work on the feature - after type checking goes out of beta we plan to continue working on both syntax and semantics, but that list currently represents the work we believe we have left to do in the first phase - please let us know if there are other significant issues you are seeing with beta beyond future feature requests! + +## Format string analysis + +A few standard functions in Luau are using format strings to dictate the behavior of the code. There’s `string.format` for building strings, `string.gmatch` for pattern matching, `string.gsub`'s replacement string, `string.pack` binary format specification and `os.date` date formatting. + +In all of these cases, it’s important to get the format strings right - typos in the format string can result in unpredictable behavior at runtime including errors. To help with that, we now have a new lint rule that parses the format strings and validates them according to the expected format. + +![String format]({{ site.url }}{{ site.baseurl }}/assets/images/luau-recap-august-2020-format.png) + +Right now this support is limited to direct library calls (`string.format("%.2f", ...)` and literal strings used in these calls - we may lift some of these limitations later to include e.g. support for constant locals. + +Additionally, if you have type checking beta enabled, string.format will now validate the argument types according to the format string to help you get your `%d`s and `%s`es right. + +![String format]({{ site.url }}{{ site.baseurl }}/assets/images/luau-recap-august-2020-format2.png) + +## Improvements to string. library + +We’ve upgraded the Luau string library to follow Lua 5.3 implementation; specifically: + + * `string.pack/string.packsize/string.unpack` are available for your byte packing needs + * `string.gmatch` and other pattern matching functions now support `%g` and `\0` in patterns + +This change also [inadvertently] makes `string.gsub` validation rules for replacement string stricter - previously `%` followed by a non-digit character was silently accepted in a replacement string, but now it generates an error. This accidentally broke our own localization script [Purchase Prompt broken in some games (% character in title)](https://devforum.roblox.com/t/purchase-prompt-broken-in-some-games-character-in-title/686237)), but we got no other reports, and this in retrospect is a good change as it makes future extensions to string replacement safe… It was impossible for us to roll the change back and due to a long release window because of an internal company holiday we decided to keep the change as is, although we’ll try to be more careful in the future. + +On a happier note, string.pack may seem daunting but is pretty easy to use to pack binary data to reduce your network traffic (note that binary strings aren’t safe to use in DataStores currently); I’ve posted an example in the release notes thread [Release Notes for 441](https://devforum.roblox.com/t/release-notes-for-441/686773) that allows you to pack a simple character state in 16 bytes like this: +``` +local characterStateFormat = "fffbbbB" + +local characterState = string.pack(characterStateFormat, + posx, posy, posz, dirx * 127, diry * 127, dirz * 127, health) +``` +And unpack it like this after network transmission: +``` +local posx, posy, posz, dirx, diry, dirz, health = + string.unpack(characterStateFormat, characterState) +dirx /= 127 +diry /= 127 +dirz /= 127 +``` + +## Assorted fixes + +As usual we fixed a few small problems discovered through testing. We now have an automated process that generates random Luau code in semi-intelligent ways to try to break different parts of our system, and a few fixes this time are a direct result of that. + + * Fix line debug information for multi-line function calls to make sure errors for code like `foo.Bar(...)` are generated in the appropriate location when foo is nil + * Fix debug information for constant upvalues; this fixes some bugs with watching local variables from the nested functions during debugging + * Fix an off-by-one range check in string.find for init argument that could result in reading uninitialized memory + * Fix type confusion for table.move target table argument that could result in reading or writing arbitrary memory + * Fix type confusion for `debug.getinfo` in some circumstances (we don’t currently expose getinfo but have plans to do so in the future) + * Improve out of memory behavior for large string allocations in string.rep and some other functions like `table.concat` to handle these conditions more gracefully + * Fix a regression with `os.time` from last update, where it erroneously reverted to Lua 5.x behavior of treating the time as a local time. Luau version (intentionally) deviates from this by treating the input table as UTC, which matches `os.time()` behavior with no arguments. + +## Performance improvements + +Only two changes in this category here this time around; some larger scale performance / memory improvements are still pending implementation. + + * Constant locals are now completely eliminated in cases when debugging is not available (so on server/client), making some scripts ~1-2% faster + * Make script compilation ~5% faster by tuning the compiler analysis and code generation more carefully +Oh, also `math.round` is now a thing which didn’t fit into any category above. \ No newline at end of file diff --git a/docs/_posts/2020-10-30-luau-recap-october-2020.md b/docs/_posts/2020-10-30-luau-recap-october-2020.md new file mode 100644 index 00000000..727fef70 --- /dev/null +++ b/docs/_posts/2020-10-30-luau-recap-october-2020.md @@ -0,0 +1,91 @@ +--- +layout: single +title: "Luau Recap: October 2020" +--- + +Luau is our new language that you can read more about at [https://roblox.github.io/luau](https://roblox.github.io/luau); we’ve been so busy working on the current projects that we didn’t do an update in September, so let’s look at changes that happened since August! + +Many people work on these improvements, with the team slowly growing - thanks @Apakovtac, @EthicalRobot, @fun_enthusiast, @machinamentum, @mrow_pizza and @zeuxcg! + +[Originally posted on the [Roblox Developer Forum](https://devforum.roblox.com/t/luau-recap-october-2020/).] + +## Types are very close + +We’ve been in beta for a while now, but we’re steadily marching towards getting the first release of the type checker, what we call “types v0”, out of the door. It turns out that we’ve substantially underestimated the effort required to make the type system robust, strike the balance between “correct” and “usable” and give quality diagnostics in the event we do find issues with your code 🙂 + +Because of this, we’re changing the original plans for the release a bit. We’re actively working on a host of changes that we consider to be part of the “v0” effort, and when they are all finished - which should happen next month, fingers crossed - we’re going to be out of beta! + +However, by default, on scripts with no annotations, we won’t actually activate type checking. You would have to opt into the type checking by using `--!nonstrict` or `--!strict`, at the top of each script. We are also going to open the second beta, “All scripts use non-strict mode by default” or something along these lines. + +This is important because we found that our non-strict mode still needs some more work to be more tolerant to some code that occurs commonly in Roblox and is correct, but doesn’t type-check. We’re going to evaluate what changes specifically are required to make this happen, but we didn’t want the extra risk of a flood of reports about issues reported in existing code to shift the release date in an unpredictable fashion. + +To that end, we’ve been working on Lots and Lots and Lots and Lots and Lots of changes to finish the first stage. Some of these changes are already live and some are rolling out; the amount of changes is so large that I can’t possibly list the up-to-date status on each one as these recaps are synthesized by the human who is writing this on a Friday night, so here’s just a raw list of changes that may or may not have been enabled: + + * Strict mode is now picky about passing extra arguments to functions, even though they are discarded silently at runtime, as this can hide bugs + * The error message about using a : vs . during type checking is now much more precise + * Recursive type annotations shouldn’t crash the type checker now, and we limit the recursion and iteration depth during type checking in a few cases in general in an effort to make sure type checker always returns in finite time + * Binary relational operators (`<` et al) are now stricter about the argument types and infer the argument types better + * Function argument and return types are now correctly contra- and co-variant; if this looks like gibberish to you, trust me - it’s for the best! + * Fixed a few problems with indexing unions of tables with matching key types + * Fixed issues with tracing types across modules (via require) in non-strict mode + * Error messages for long table types are now trimmed to make the output look nicer + * Improve the interaction between table types of unknown shape (`{ [string]: X }`) and table types of known shape. + * Fix some issues with type checking table assignments + * Fix some issues with variance of table fields + * Improve the legibility of type errors during function calls - errors now point at specific arguments that are incorrect, and mismatch in argument count should clearly highlight the problem + * Fix types for many builtins including `ipairs`, `table.create`, `Color3.fromHSV`, and a few others + * Fix missing callbacks for some instance types like `OnInvoke` for bindables (I think this one is currently disabled while we’re fixing a semi-related bug, but should be enabled soon!) + * Rework the rules under which globals are okay to use in non-strict mode to mostly permit valid scripts to type-check; strict mode will continue to frown upon the use of global variables + * Fix a problem with the beta where two scripts with identical names would share the set of errors/warnings, resulting in confusing error highlights for code that doesn’t exist + * Improve the legibility of type errors when indexing a table without a given key + * Improve the parsing error when trying to return a tuple; `function f(): string, number` is invalid since the type list should be parenthesized because of how our type grammar is currently structured + * Type checker now clearly reports cases where it finds a cyclic dependency between two modules + * Type errors should now be correctly sorted in the Script Analysis widget + * Error messages on mismatches between numbers of values in return statements should now be cleaner, as well as the associated type mismatch errors + * Improve error messages for comparison operators + * Flag attempts to require a non-module script during type checking + * Fix some cases where a type/typeof guard could be misled into inferring a non-sensible type + * Increase the strictness of return type checks in strict mode - functions now must conform to the specified type signature, whereas before we’d allow a function to return no values even in strict mode + * Improve the duplicate definition errors to specify the line of the first definition + * Increase the strictness of binary operators in strict mode to enforce the presence of the given operator as a built-in or as part of the metatable, to make sure that strict mode doesn’t infer types when it can’t guarantee correctness + * Improve the type errors for cyclic types to make them more readable + * Make type checker more friendly by rewording a lot of error messages + * Fix a few crashes in the type checker (although a couple more remain - working on them!) + * … I think that’s it? + * …edit ah, of course I forgot one thing - different enums that are part of the Roblox API now have distinct types and you can refer to the types by name e.g. `Enum.Material`; this should go live next week though. +If you want to pretend that you’ve read and understood the entire list above, just know that we’ve worked on making sure strict mode is more reliably reporting type errors and doesn’t infer types incorrectly, on making sure non-strict mode is more forgiving for code that is probably valid, and on making the type errors more specific, easier to understand, and correct. + +## Type syntax changes + +There’s only two small changes here this time around - the type syntax is now completely stable at this point, and any existing type annotation will continue parsing indefinitely. We of course reserve the right to add new syntax that’s backwards compatible :slight_smile: + +On that note, one of the small changes is that we’ve finally removed support for fat arrows (`=>`); we’ve previously announced that this would happen and that thin arrows (`->`) are the future, and had warnings issued on the legacy syntax for a while. Now it’s gone. + +On a positive note, we’ve added a shorter syntax for array-like table types. Whereas before you had to use a longer `{ [number]: string }` syntax to declare an array-like table that holds strings, or had to define an `Array` type in every. single. module. you. ever. write. ever., now you can simply say `{string}`! This syntax is clean, in line with the value syntax for Lua table literals, and also was chosen by other research projects to add type annotations to Lua. + +(if you’re a monster that uses mixed tables, you’ll have to continue using the longer syntax e.g. `{ [number]: string, n: number }`) + +## Library changes + +There’s only a few small tweaks here this time around on the functionality front: + + * `utf8.charpattern` is now exactly equal to the version from Lua 5.3; this is now possible because we support `\0` in patterns, and was suggested by a user on devforum. We do listen! + * `string.pack` now errors out early when the format specifier is Way Too Large. This was reported on dev forum and subsequently fixed. Note that trying to generate a Moderately Large String (like, 100 MB instead of 100 GB) will still succeed but may take longer than we’d like - we have a plan to accelerate operations on large strings substantially in the coming months. + +## Performance improvements + +We were super focused on other things so this is very short this time around. We have a lot of ideas here but they are waiting for us to finish some other large projects! + + * Method calls on strings via `:` are now ~10% faster than before. We still recommend using fully-qualified calls from string library such as `string.foo(str)`, but extra performance never hurts! + * Speaking of string methods, string.sub is now ~20% faster than before with the help of voodoo magic. + +## Miscellaneous fixes + +There were a few small fixes that didn’t land into any specific category that I wanted to highlight: + + * In some rare cases, debug information on conditions inside loops have been fixed to stop debugger from incorrectly suggesting that the current line is inside a branch that wasn’t taken. As usual, if you ever see debugger misbehaving, please file bugs on this! + * Code following `assert(false)` is now treated as an unreachable destination from the linting and type checking point of view, similarly to error calls. + * Linting support for various format strings has been greatly improved based on fantastic feedback from @Halalaluyafail3 (thanks!). + +Ok, phew, that’s what I get for skipping a month again. Please don’t hesitate to report bugs or suggestions, here or via separate posts. Due to our usual end-of-year code freeze there’s going to be one more recap at the end of the year where we will look back at 2020 and take a small peek into the future. + diff --git a/docs/_posts/2020-11-19-luau-type-checking-release.md b/docs/_posts/2020-11-19-luau-type-checking-release.md new file mode 100644 index 00000000..35879573 --- /dev/null +++ b/docs/_posts/2020-11-19-luau-type-checking-release.md @@ -0,0 +1,109 @@ +--- +layout: single +title: "Luau Type Checking Release" +--- + +10 months ago, we’ve started upon the journey of helping Roblox scripters write robust code by introducing [an early beta of type checking](https://devforum.roblox.com/t/luau-type-checking-release). We’ve received a lot of enthusiastic feedback and worked with the community on trying to make sure critical issues are addressed, usability is improved and the type system is ready for prime time. + +Today I’m incredibly excited to announce that the first release of [Luau](https://roblox.github.io/luau/) type checking is officially released! Thanks a lot to @Apakovtac, @EthicalRobot, @fun_enthusiast, @machinamentum, @mrow_pizza and @zeuxcg! + +[Originally posted on the [Roblox Developer Forum](https://devforum.roblox.com/t/luau-type-checking-release/).] + +## What is type checking? + +When Luau code runs, every value has a certain type at runtime - a kind of value it stores. It could be a number, a string, a table, a Roblox Instance or one of many others. Thing is, some operations work on some types but don’t work on others! + +Consider this: +``` +local p = Instance.new("Part") +p.Positio = Vector3.new(1,2,3) +``` +Is this code correct? No - there’s a typo. The way you get to find this typo is by running your code and eventually seeing an error message. Type checker tries to analyze your code before running, by assigning a type to each value based on what we know about how that value was produced, or based on the type you’ve explicitly told us using a new syntax extension, and can produce an error ahead of time: + +!["Positio not found in class Part"]({{ site.url }}{{ site.baseurl }}/assets/images/luau-type-checking-release-screenshot.png) + +This can require some effort up front, especially if you use strict mode, but it can save you valuable time in the future. It can be especially valuable if you have a large complex code base you need to maintain for years, as is the case with many top Roblox games. + +## How do I use type checking? + +A very important feature of Luau type checking you need to know about is that it has three modes: + + * `nocheck`, where we don’t type check the script in question. + * `nonstrict`, where we type check the script but try to be lenient to allow commonly seen patterns even if they may violate type safety + * `strict`, where we try to make sure that every single line of code you write is correct, and every value has a known type. + +The modes can be selected per script by writing a comment at the top of the script that starts with `--!`, e.g. `--!strict`. + +As of this release, the default mode is nocheck. This means by default you actually won’t see the type checking produce feedback on your code! We had to use nocheck by default because we aren’t fully ready to unleash nonstrict mode on unsuspecting users - we need to do a bit more work to make sure that most cases where we tell you that something is wrong are cases where yes, something is actually wrong. + +However we highly encourage trying at least non-strict mode on your codebase. You can do this by opting into a different default via a Studio beta: + +!["Studio option"]({{ site.url }}{{ site.baseurl }}/assets/images/luau-type-checking-release-studio-option.png) + +This beta only changes the default mode. Another way to change the mode is to prepend a `--!` comment to the script - you can do this manually for now, but if anyone in the community wants to release a plugin that does it automatically on selected scripts (+ descendants), that would be swell! + +If you really want your code to be rock solid, we recommend trying out strict mode. Strict mode will require you to use type annotations. + +## What are type annotations and how do I use them? + +Glad you asked! (please pretend you did) Type annotations are a way to tell the type checker what the type of a variable is. Consider this code in strict mode: +``` +function add(x, y) + return x + y +end +``` +Is this code correct? Well, that depends. `add(2, 3)` will work just fine. `add(Vector3.new(1, 2, 3), Vector3.new(4, 5, 6))` will work as well. But `add({}, nil)` probably isn’t a good idea. + +In strict mode, we will insist that the type checker knows the type of all variables, and you’ll need to help the type checker occasionally - by adding types after variable names separated by `:`: +``` +function add(x: number, y: number) + return x + y +end +``` +If you want to tell the type checker “assume this value can be anything and I will take responsibility”, you can use `any` type which will permit any value of any type. + +If you want to learn more about the type annotation syntax, you should read this [documentation on syntax](https://roblox.github.io/luau/syntax.html#type-annotations). We also have a somewhat more complete guide to type checking than this post can provide, that goes into more details on table types, OOP, Roblox classes and enums, interaction with require and other topics - [read it if you’re curious!](https://roblox.github.io/luau/typecheck.html). + +## What happens when I get a type error? + +One concept that’s very important to understand is that right now type errors do not influence whether the code will run or not. + +If you have a type error, this means that our type checker thinks your code has a bug, or doesn’t have enough information to prove the code works fine. But if you really want to forge ahead and run the code - you should feel free to do so! + +This means that you can gradually convert your code to strict mode by adding type annotations and have the code runnable at all times even if it has type errors. + +This also means that it’s safe to publish scripts even if type checker is not fully happy with them - type issues won’t affect script behavior on server/client, they are only displayed in Studio. + +## Do I have to re-learn Lua now?!? + +This is a question we get often! The answer is “no”. + +The way the type system is designed is that it’s completely optional, and you can use as many or as few types as you’d like in your code. + +In non-strict mode, types are meant as a lightweight helper - if your code is likely wrong, we’re going to tell you about it, and it’s up to you on whether to fix the issue, or even disable the type checker on a given problematic file if you really don’t feel like dealing with this. + +In strict mode, types are meant as a power user tool - they will require more time to develop your code, but they will give you a safety net, where changing code will be much less likely to trigger errors at runtime. + +## Is there a performance difference? + +Right now type annotations are ignored by our bytecode compiler; this means that performance of the code you write doesn’t actually depend on whether you use strict, nonstrict or nocheck modes or if you have type annotations. + +This is likely going to change! We have plans for using the type information to generate better bytecode in certain cases, and types are going to be instrumental to just-in-time compilation, something that we’re going to invest time into next year as well. + +Today, however, there’s no difference - type information is completely elided when the bytecode is built, so there is zero runtime impact one way or another. + +## What is next for types? + +This is the first full release of type checking, but it’s by far the last one. We have a lot more ground to cover. Here’s a few things that we’re excited about that will come next: + + * Making nonstrict mode better to the point where we can enable it as a default for all Roblox scripts + + * Adding several features to make strict mode more powerful/friendly, such as typed variadics, type ascription and better generics support + + * Improving type refinements for type/typeof and nil checks + + * Making it possible to view the type of a variable in Studio + + * Reworking autocomplete to use type information instead of the current system + +If you have any feedback on the type system, please don’t hesitate to share it here or in dedicated bug report threads. We’re always happy to fix corner cases that we’ve missed, fix stability issues if they are discovered, improve documentation when it’s not clear or improve error messages when they are hard to understand. \ No newline at end of file diff --git a/docs/_posts/2021-03-01-luau-recap-february-2021.md b/docs/_posts/2021-03-01-luau-recap-february-2021.md new file mode 100644 index 00000000..1179eaea --- /dev/null +++ b/docs/_posts/2021-03-01-luau-recap-february-2021.md @@ -0,0 +1,83 @@ +--- +layout: single +title: "Luau Recap: February 2021" +--- + +Luau is our new language that you can read more about at [https://roblox.github.io/luau](https://roblox.github.io/luau). It's been a busy few months in Luau! + +[Cross-posted to the [Roblox Developer Forum](https://devforum.roblox.com/t/luau-recap-february-2021/).] + +## Infallible parser + +Traditional compilers have focused on tasks that can be performed on complete programs, such as type-checking, static analysis and code generation. This is all good, but most programs under development are incomplete! They may have holes, statements that will be filled in later, and lines that are in the middle of being edited. If we'd like to provide support for developers while they are writing code, we need to provide tools for incomplete programs as well as complete ones. + +The first step in this is an *infallible* parser, that always returns an Abstract Syntax Tree, no matter what input it is given. If the program is syntactically incorrect, there will also be some syntax errors, but the parser keeps going and tries to recover from those errors, rather than just giving up. + +The Luau parser now recovers from errors, which means, for example, we can give hints about programs in an IDE. + +![A type error after a syntax error]({{ site.url }}{{ site.baseurl }}/assets/images/type-error-after-syntax-error.png) + +## Type assertions + +The Luau type checker can't know everything about your code, and sometimes it will produce type errors even when you know the code is correct. For example, sometimes the type checker can't work out the intended types, and gives a message such as "Unknown type used... consider adding a type annotation". + +!["Consider adding a type annotation"]({{ site.url }}{{ site.baseurl }}/assets/images/type-annotation-needed.png) + +Previously the only way to add an annotation was to put it on the *declaration* of the variable, but now you can put it on the *use* too. A use of variable `x` at type `T` can be written `x :: T`. For example the type `any` can be used almost anywhere, so a common usage of type assertions is to switch off the type system by writing `x :: any`. + +!["A type assertion y:any"]({{ site.url }}{{ site.baseurl }}/assets/images/type-annotation-provided.png) + +## Typechecking improvements + +We've made various improvements to the Luau typechecker: + + * We allow duplicate function definitions in non-strict mode. + * Better typechecking of `and`, `(f or g)()`, arrays with properties, and `string:format()`. + * Improved typechecking of infinite loops. + * Better error reporting for function type mismatch, type aliases and cyclic types. + +## Performance improvements + +We are continuing to work on optimizing our VM and libraries to make sure idiomatic code keeps improving in performance. Most of these changes are motivated by our benchmark suite; while some improvements may seem small and insignificant, over time these compound and allow us to reach excellent performance. + + * Table key assignments as well as global assignments have been optimized to play nicer with modern CPUs, yielding ~2% speedup in some benchmarks + * Luau function calls are now ~3% faster in most cases; we also have more call optimizations coming up next month! + * Modulo operation (%) is now a bit faster on Windows, resulting in ~2% performance improvement on some benchmarks + +!["Benchmark vs Lua 5.3"]({{ site.url }}{{ site.baseurl }}/assets/images/luau-recap-february-2021-benchmark.png) + +## Debugger improvements + +Our Luau VM implementation is focused on performance and provides a different API for implementation of debugger tools. But it does have its caveats and one of them was inability to debug coroutines (breakpoints/stepping). + +The good news is that we have lifted that limitation and coroutines can now be debugged just like any regular function. This can especially help people who use Promise libraries that rely on coroutines internally. + +![Debugging a coroutine]({{ site.url }}{{ site.baseurl }}/assets/images/luau-recap-february-2021-debugger.png) + +## Library changes + +`table` library now has a new method, `clear`, that removes all keys from the table but keeps the internal table capacity. When working with large arrays, this can be more efficient than assigning a table to `{}` - the performance gains are similar to that of using `table.create` instead of `{}` *when you expect the number of elements to stay more or less the same*. Note that large empty tables still take memory and are a bit slower for garbage collector to process, so use this with caution. + +In addition to that we found a small bug in `string.char` implementation that allowed creating strings from out-of-range character codes (e.g. `string.char(2000)`); the problem has been fixed and these calls now correctly generate an error. + +## Coming soon... + +* _Generic function types_ will soon be allowed! +``` +function id(x: a): a + return x +end +``` + +* _Typed variadics_ will soon allow types to be given to functions with varying numbers of arguments! +``` +function sum(...: number): number + local result = 0 + for i,v in ipairs({...}) do + result += v + end + return result +end +``` + +And there will be more! diff --git a/docs/assets/images/luau-recap-august-2020-arrow.png b/docs/assets/images/luau-recap-august-2020-arrow.png new file mode 100644 index 00000000..282bc5a0 Binary files /dev/null and b/docs/assets/images/luau-recap-august-2020-arrow.png differ diff --git a/docs/assets/images/luau-recap-august-2020-format.png b/docs/assets/images/luau-recap-august-2020-format.png new file mode 100644 index 00000000..d180b442 Binary files /dev/null and b/docs/assets/images/luau-recap-august-2020-format.png differ diff --git a/docs/assets/images/luau-recap-august-2020-format2.png b/docs/assets/images/luau-recap-august-2020-format2.png new file mode 100644 index 00000000..c98fbf3d Binary files /dev/null and b/docs/assets/images/luau-recap-august-2020-format2.png differ diff --git a/docs/assets/images/luau-recap-august-2020-meta.png b/docs/assets/images/luau-recap-august-2020-meta.png new file mode 100644 index 00000000..c7396f92 Binary files /dev/null and b/docs/assets/images/luau-recap-august-2020-meta.png differ diff --git a/docs/assets/images/luau-recap-february-2021-benchmark.png b/docs/assets/images/luau-recap-february-2021-benchmark.png new file mode 100644 index 00000000..a6833252 Binary files /dev/null and b/docs/assets/images/luau-recap-february-2021-benchmark.png differ diff --git a/docs/assets/images/luau-recap-february-2021-debugger.png b/docs/assets/images/luau-recap-february-2021-debugger.png new file mode 100644 index 00000000..aca24645 Binary files /dev/null and b/docs/assets/images/luau-recap-february-2021-debugger.png differ diff --git a/docs/assets/images/luau-recap-june-2020-xkcd.png b/docs/assets/images/luau-recap-june-2020-xkcd.png new file mode 100644 index 00000000..b7e2ed8e Binary files /dev/null and b/docs/assets/images/luau-recap-june-2020-xkcd.png differ diff --git a/docs/assets/images/luau-recap-november-2019-option.png b/docs/assets/images/luau-recap-november-2019-option.png new file mode 100644 index 00000000..3b2b93c1 Binary files /dev/null and b/docs/assets/images/luau-recap-november-2019-option.png differ diff --git a/docs/assets/images/luau-type-checking-release-screenshot.png b/docs/assets/images/luau-type-checking-release-screenshot.png new file mode 100644 index 00000000..5afc78ef Binary files /dev/null and b/docs/assets/images/luau-type-checking-release-screenshot.png differ diff --git a/docs/assets/images/luau-type-checking-release-studio-option.png b/docs/assets/images/luau-type-checking-release-studio-option.png new file mode 100644 index 00000000..e79aa831 Binary files /dev/null and b/docs/assets/images/luau-type-checking-release-studio-option.png differ diff --git a/docs/assets/images/type-annotation-needed.png b/docs/assets/images/type-annotation-needed.png new file mode 100644 index 00000000..25307327 Binary files /dev/null and b/docs/assets/images/type-annotation-needed.png differ diff --git a/docs/assets/images/type-annotation-provided.png b/docs/assets/images/type-annotation-provided.png new file mode 100644 index 00000000..7bac0340 Binary files /dev/null and b/docs/assets/images/type-annotation-provided.png differ diff --git a/docs/assets/images/type-error-after-syntax-error.png b/docs/assets/images/type-error-after-syntax-error.png new file mode 100644 index 00000000..6d8b23a9 Binary files /dev/null and b/docs/assets/images/type-error-after-syntax-error.png differ diff --git a/docs/assets/images/type-refinement-in-action.png b/docs/assets/images/type-refinement-in-action.png new file mode 100644 index 00000000..e3acf8ed Binary files /dev/null and b/docs/assets/images/type-refinement-in-action.png differ