├── .bundle └── config ├── .gitignore ├── 404.html ├── CNAME ├── Gemfile ├── LICENSE.md ├── README.md ├── _config.yml ├── _data └── navigation.yml ├── _includes ├── masthead.html └── repl.html ├── _pages ├── compatibility.md ├── demo.md ├── getting-started.md ├── grammar.md ├── library.md ├── limited-terms-of-use.md ├── lint.md ├── news.md ├── performance.md ├── profile.md ├── sandbox.md ├── syntax.md ├── typecheck.md └── why.md ├── _posts ├── 2019-11-11-luau-recap-november-2019.md ├── 2020-01-16-luau-type-checking-beta.md ├── 2020-02-25-luau-recap-february-2020.md ├── 2020-05-18-luau-recap-may-2020.md ├── 2020-06-20-luau-recap-june-2020.md ├── 2020-08-11-luau-recap-august-2020.md ├── 2020-10-30-luau-recap-october-2020.md ├── 2020-11-19-luau-type-checking-release.md ├── 2021-03-01-luau-recap-february-2021.md ├── 2021-03-29-luau-recap-march-2021.md ├── 2021-04-30-luau-recap-april-2021.md ├── 2021-05-31-luau-recap-may-2021.md ├── 2021-06-30-luau-recap-june-2021.md ├── 2021-07-30-luau-recap-july-2021.md ├── 2021-08-31-luau-recap-august-2021.md ├── 2021-09-30-luau-recap-september-2021.md ├── 2021-10-31-luau-recap-october-2021.md ├── 2021-11-03-luau-goes-open-source.md ├── 2021-11-29-luau-recap-november-2021.md ├── 2022-01-27-luau-recap-january-2022.md ├── 2022-02-28-luau-recap-february-2022.md ├── 2022-03-31-luau-recap-march-2022.md ├── 2022-05-02-luau-recap-april-2022.md ├── 2022-06-01-luau-recap-may-2022.md ├── 2022-07-07-luau-recap-june-2022.md ├── 2022-08-29-luau-recap-august-2022.md ├── 2022-10-31-luau-semantic-subtyping.md ├── 2022-11-01-luau-recap-september-october-2022.md ├── 2022-11-04-luau-origins-and-evolution.md ├── 2022-11-30-luau-recap-november-2022.md ├── 2023-02-02-luau-string-interpolation.md ├── 2023-03-31-luau-recap-march-2023.md ├── 2023-07-28-luau-recap-july-2023.md ├── 2023-11-01-luau-recap-october-2023.md └── 2024-07-23-luau-recap-july-2024.md ├── assets ├── css │ └── theme2.scss ├── images │ ├── chess-profile.svg │ ├── create-new-place.png │ ├── create-script.png │ ├── error-isfoo.png │ ├── error-ispositive-boolean.png │ ├── error-ispositive-string.png │ ├── error-ispositive.png │ ├── example.png │ ├── luau-88.png │ ├── luau-header.png │ ├── luau-recap-august-2020-arrow.png │ ├── luau-recap-august-2020-format.png │ ├── luau-recap-august-2020-format2.png │ ├── luau-recap-august-2020-meta.png │ ├── luau-recap-february-2021-benchmark.png │ ├── luau-recap-february-2021-debugger.png │ ├── luau-recap-june-2020-xkcd.png │ ├── luau-recap-march-2021-debug-after.png │ ├── luau-recap-march-2021-debug-before.png │ ├── luau-recap-march-2021-debug-dialog.png │ ├── luau-recap-november-2019-option.png │ ├── luau-type-checking-release-screenshot.png │ ├── luau-type-checking-release-studio-option.png │ ├── luau.png │ ├── mascot.png │ ├── type-annotation-needed.png │ ├── type-annotation-provided.png │ ├── type-error-after-syntax-error.png │ └── type-refinement-in-action.png └── js │ └── luau_mode.js ├── favicon.ico ├── index.md └── logo.svg /.bundle/config: -------------------------------------------------------------------------------- 1 | --- 2 | BUNDLE_PATH: "vendor/bundle" 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _site 2 | .sass-cache 3 | .jekyll-cache 4 | .jekyll-metadata 5 | vendor 6 | Gemfile.lock -------------------------------------------------------------------------------- /404.html: -------------------------------------------------------------------------------- 1 | --- 2 | permalink: /404.html 3 | layout: default 4 | --- 5 | 6 | 19 | 20 |
21 |

404

22 | 23 |

Page not found :(

24 |

The requested page could not be found.

25 |
26 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | luau.org -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | gem "github-pages", group: :jekyll_plugins 3 | gem "jekyll-include-cache", group: :jekyll_plugins 4 | gem "jekyll-feed", group: :jekyll_plugins 5 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2023 Roblox Corporation 4 | Copyright (c) 1994–2019 Lua.org, PUC-Rio. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 10 | of the Software, and to permit persons to whom the Software is furnished to do 11 | so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repository hosts the official Luau website, accessible on https://luau.org, including all the user-facing language documentation therein. 2 | Changes to this documentation that improve clarity, fix grammatical issues, explain aspects that haven't been explained before, and the like are warmly welcomed. 3 | 4 | Please feel free to [create a pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests) to improve our documentation. Note that at this point the documentation is English-only. 5 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | remote_theme: "mmistakes/minimal-mistakes@4.24.0" 2 | minimal_mistakes_skin: "default" #"air", "aqua", "contrast", "dark", "dirt", "neon", "mint", "plum" "sunrise" 3 | minimal_mistakes_skin2: "dark" 4 | url: 5 | name: Roblox 6 | title: Luau 7 | description: > 8 | A fast, small, safe, gradually typed embeddable scripting language derived from Lua 9 | logo: /assets/images/luau-88.png 10 | 11 | plugins: ["jekyll-include-cache", "jekyll-feed"] 12 | include: ["_pages"] 13 | atom_feed: 14 | path: "/feed.xml" 15 | 16 | defaults: 17 | # _docs 18 | - scope: 19 | path: "" 20 | type: "pages" 21 | values: 22 | layout: "single" 23 | sidebar: 24 | nav: "pages" 25 | # _posts 26 | - scope: 27 | path: "" 28 | type: "posts" 29 | values: 30 | layout: single 31 | related: true 32 | show_date: true 33 | -------------------------------------------------------------------------------- /_data/navigation.yml: -------------------------------------------------------------------------------- 1 | main: 2 | - title: News 3 | url: /news 4 | - title: Getting Started 5 | url: /getting-started 6 | - title: Demo 7 | url: /demo 8 | - title: GitHub 9 | url: https://github.com/luau-lang/luau 10 | 11 | pages: 12 | - title: Getting Started 13 | url: /getting-started 14 | - title: Why Luau? 15 | url: /why 16 | - title: Syntax 17 | url: /syntax 18 | - title: Linting 19 | url: /lint 20 | - title: Performance 21 | url: /performance 22 | - title: Sandboxing 23 | url: /sandbox 24 | - title: Compatibility 25 | url: /compatibility 26 | - title: Typechecking 27 | url: /typecheck 28 | - title: Profiling 29 | url: /profile 30 | - title: Library 31 | url: /library 32 | - title: Grammar 33 | url: /grammar 34 | -------------------------------------------------------------------------------- /_includes/masthead.html: -------------------------------------------------------------------------------- 1 | {% capture logo_path %}{{ site.logo }}{% endcapture %} 2 | 3 |
4 |
5 |
6 | 38 |
39 |
40 |
41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /_pages/demo.md: -------------------------------------------------------------------------------- 1 | --- 2 | permalink: /demo 3 | title: Demo 4 | classes: wide 5 | --- 6 | 7 | {% include repl.html %} 8 | -------------------------------------------------------------------------------- /_pages/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | permalink: /getting-started 3 | title: Getting Started 4 | toc: true 5 | --- 6 | 7 | Luau is a fast, small, safe, gradually typed embeddable scripting language derived from Lua 5.1. Luau ships as a command line tool for running, analyzing, and linting your Luau scripts. Luau is integrated with RobloxStudio and is automatically enabled with the `--!strict` flag at the top of any scripts. Roblox developers should also visit our [Creator Docs Luau Section](https://create.roblox.com/docs/luau). 8 | 9 | To get started with Luau you need to use `luau` command line binary to run your code and `luau-analyze` to run static analysis (including type checking and linting). You can download these from [a recent release](https://github.com/luau-lang/luau/releases). 10 | 11 | ## Creating a script 12 | 13 | To create your own testing script, create a new file with `.luau` as the extension: 14 | 15 | ```lua 16 | function ispositive(x) 17 | return x > 0 18 | end 19 | 20 | print(ispositive(1)) 21 | print(ispositive("2")) 22 | 23 | function isfoo(a) 24 | return a == "foo" 25 | end 26 | 27 | print(isfoo("bar")) 28 | print(isfoo(1)) 29 | ``` 30 | 31 | You can now run the file using `luau test.luau` and analyze it using `luau-analyze test.luau`. 32 | 33 | Note that there are no warnings about calling ``ispositive()`` with a string, or calling ``isfoo()`` a number. This is because the type checking uses non-strict mode by default, which is lenient in how it infers types used by the program. 34 | 35 | ## Type inference 36 | 37 | Now modify the script to include ``--!strict`` at the top: 38 | 39 | ```lua 40 | --!strict 41 | 42 | function ispositive(x) 43 | return x > 0 44 | end 45 | 46 | print(ispositive(1)) 47 | print(ispositive("2")) 48 | ``` 49 | 50 | In ``strict`` mode, Luau will infer types based on analysis of the code flow. There is also ``nonstrict`` mode, where analysis is more conservative and types are more frequently inferred as ``any`` to reduce cases where legitimate code is flagged with warnings. 51 | 52 | In this case, Luau will use the ``return x > 0`` statement to infer that ``ispositive()`` is a function taking a number and returning a boolean. Note that in this case, it was not necessary to add any explicit type annotations. 53 | 54 | Based on Luau's type inference, the analysis tool will now flag the incorrect call to ``ispositive()``: 55 | 56 | ``` 57 | $ luau-analyze test.luau 58 | test.luau(7,18): TypeError: Type 'string' could not be converted into 'number' 59 | ``` 60 | 61 | ## Annotations 62 | 63 | You can add annotations to locals, arguments, and function return types. Among other things, annotations can help enforce that you don't accidentally do something silly. Here's how we would add annotations to ``ispositive()``: 64 | 65 | ```lua 66 | --!strict 67 | 68 | function ispositive(x : number) : boolean 69 | return x > 0 70 | end 71 | 72 | local result : boolean 73 | result = ispositive(1) 74 | 75 | ``` 76 | 77 | Now we've explicitly told Luau that ``ispositive()`` accepts a number and returns a boolean. This wasn't strictly (pun intended) necessary in this case, because Luau's inference was able to deduce this already. But even in this case, there are advantages to explicit annotations. Imagine that later we decide to change ``ispositive()`` to return a string value: 78 | 79 | ```lua 80 | --!strict 81 | 82 | function ispositive(x : number) : boolean 83 | if x > 0 then 84 | return "yes" 85 | else 86 | return "no" 87 | end 88 | end 89 | 90 | local result : boolean 91 | result = ispositive(1) 92 | ``` 93 | 94 | Oops -- we're returning string values, but we forgot to update the function return type. Since we've told Luau that ``ispositive()`` returns a boolean (and that's how we're using it), the call site isn't flagged as an error. But because the annotation doesn't match our code, we get a warning in the function body itself: 95 | 96 | ``` 97 | $ luau-analyze test.luau 98 | test.luau(5,9): TypeError: Type 'string' could not be converted into 'boolean' 99 | test.luau(7,9): TypeError: Type 'string' could not be converted into 'boolean' 100 | ``` 101 | 102 | The fix is simple; just change the annotation to declare the return type as a string: 103 | 104 | ```lua 105 | --!strict 106 | 107 | function ispositive(x : number) : string 108 | if x > 0 then 109 | return "yes" 110 | else 111 | return "no" 112 | end 113 | end 114 | 115 | local result : boolean 116 | result = ispositive(1) 117 | ``` 118 | 119 | Well, almost - since we declared ``result`` as a boolean, the call site is now flagged: 120 | 121 | ``` 122 | $ luau-analyze test.luau 123 | test.luau(12,10): TypeError: Type 'string' could not be converted into 'boolean' 124 | ``` 125 | 126 | If we update the type of the local variable, everything is good. Note that we could also just let Luau infer the type of ``result`` by changing it to the single line version ``local result = ispositive(1)``. 127 | 128 | ```lua 129 | --!strict 130 | 131 | function ispositive(x : number) : string 132 | if x > 0 then 133 | return "yes" 134 | else 135 | return "no" 136 | end 137 | end 138 | 139 | local result : string 140 | result = ispositive(1) 141 | ``` 142 | 143 | ## Conclusions 144 | 145 | This has been a brief tour of the basic functionality of Luau, but there's lots more to explore. If you're interested in reading more, check out our main reference pages for [syntax](syntax) and [typechecking](typecheck). 146 | -------------------------------------------------------------------------------- /_pages/grammar.md: -------------------------------------------------------------------------------- 1 | --- 2 | permalink: /grammar 3 | title: Grammar 4 | classes: wide 5 | --- 6 | 7 | This is the complete syntax grammar for Luau in EBNF. More information about the terminal nodes STRING and NUMBER 8 | is available in the [syntax section](syntax). 9 | 10 | ```ebnf 11 | chunk ::= block 12 | block ::= {stat [';']} [laststat [';']] 13 | stat ::= varlist '=' explist | 14 | var compoundop exp | 15 | functioncall | 16 | 'do' block 'end' | 17 | 'while' exp 'do' block 'end' | 18 | 'repeat' block 'until' exp | 19 | 'if' exp 'then' block {'elseif' exp 'then' block} ['else' block] 'end' | 20 | 'for' binding '=' exp ',' exp [',' exp] 'do' block 'end' | 21 | 'for' bindinglist 'in' explist 'do' block 'end' | 22 | attributes 'function' funcname funcbody | 23 | attributes 'local' 'function' NAME funcbody | 24 | 'local' bindinglist ['=' explist] | 25 | ['export'] 'type' NAME ['<' GenericTypeListWithDefaults '>'] '=' Type | 26 | ['export'] 'type' 'function' NAME funcbody 27 | 28 | laststat ::= 'return' [explist] | 'break' | 'continue' 29 | 30 | funcname ::= NAME {'.' NAME} [':' NAME] 31 | funcbody ::= ['<' GenericTypeList '>'] '(' [parlist] ')' [':' ReturnType] block 'end' 32 | parlist ::= bindinglist [',' '...' [':' (GenericTypePack | Type)]] | '...' [':' (GenericTypePack | Type)] 33 | 34 | explist ::= {exp ','} exp 35 | 36 | binding ::= NAME [':' Type] 37 | bindinglist ::= binding [',' bindinglist] (* equivalent of Lua 5.1 'namelist', except with optional type annotations *) 38 | 39 | var ::= NAME | prefixexp '[' exp ']' | prefixexp '.' NAME 40 | varlist ::= var {',' var} 41 | prefixexp ::= var | functioncall | '(' exp ')' 42 | functioncall ::= prefixexp funcargs | prefixexp ':' NAME funcargs 43 | 44 | exp ::= asexp { binop exp } | unop exp { binop exp } 45 | ifelseexp ::= 'if' exp 'then' exp {'elseif' exp 'then' exp} 'else' exp 46 | asexp ::= simpleexp ['::' Type] 47 | stringinterp ::= INTERP_BEGIN exp { INTERP_MID exp } INTERP_END 48 | simpleexp ::= NUMBER | STRING | 'nil' | 'true' | 'false' | '...' | tableconstructor | attributes 'function' funcbody | prefixexp | ifelseexp | stringinterp 49 | funcargs ::= '(' [explist] ')' | tableconstructor | STRING 50 | 51 | tableconstructor ::= '{' [fieldlist] '}' 52 | fieldlist ::= field {fieldsep field} [fieldsep] 53 | field ::= '[' exp ']' '=' exp | NAME '=' exp | exp 54 | fieldsep ::= ',' | ';' 55 | 56 | compoundop ::= '+=' | '-=' | '*=' | '/=' | '//=' | '%=' | '^=' | '..=' 57 | binop ::= '+' | '-' | '*' | '/' | '//' | '^' | '%' | '..' | '<' | '<=' | '>' | '>=' | '==' | '~=' | 'and' | 'or' 58 | unop ::= '-' | 'not' | '#' 59 | 60 | littable ::= '{' [litfieldlist] '}' 61 | litfieldlist ::= litfield {fieldsep litfield} [fieldsep] 62 | litfield ::= [NAME '='] literal 63 | 64 | literal ::= 'nil' | 'false' | 'true' | NUMBER | STRING | littable 65 | litlist ::= literal {',' literal} 66 | 67 | pars ::= '(' [litlist] ')' | littable | STRING 68 | parattr ::= NAME [pars] 69 | attribute ::= '@' NAME | '@[' parattr {',' parattr} ']' 70 | attributes ::= {attribute} 71 | 72 | SimpleType ::= 73 | 'nil' | 74 | SingletonType | 75 | NAME ['.' NAME] [ '<' [TypeParams] '>' ] | 76 | 'typeof' '(' exp ')' | 77 | TableType | 78 | FunctionType | 79 | '(' Type ')' 80 | 81 | SingletonType ::= STRING | 'true' | 'false' 82 | 83 | Union ::= [SimpleType {'?'}] {'|' SimpleType {'?'}} 84 | Intersection ::= [SimpleType] {'&' SimpleType} 85 | Type ::= Union | Intersection 86 | 87 | GenericTypePackParameter ::= NAME '...' 88 | GenericTypeList ::= NAME [',' GenericTypeList] | GenericTypePackParameter {',' GenericTypePackParameter} 89 | 90 | GenericTypePackParameterWithDefault ::= NAME '...' '=' (TypePack | VariadicTypePack | GenericTypePack) 91 | GenericTypeListWithDefaults ::= 92 | NAME ['=' Type] [',' GenericTypeListWithDefaults] | 93 | GenericTypePackParameterWithDefault {',' GenericTypePackParameterWithDefault} 94 | 95 | TypeList ::= Type [',' TypeList] | '...' Type 96 | BoundTypeList ::= [NAME ':'] Type [',' BoundTypeList] | GenericTypePack | VariadicTypePack 97 | TypeParams ::= (Type | TypePack | VariadicTypePack | GenericTypePack) [',' TypeParams] 98 | TypePack ::= '(' [TypeList] ')' 99 | GenericTypePack ::= NAME '...' 100 | VariadicTypePack ::= '...' Type 101 | ReturnType ::= Type | TypePack | GenericTypePack | VariadicTypePack 102 | TableIndexer ::= ['read' | 'write'] '[' Type ']' ':' Type 103 | TableProp ::= ['read' | 'write'] NAME ':' Type 104 | PropList ::= TableProp [fieldsep PropList] | TableIndexer {fieldsep TableProp} 105 | 106 | TableType ::= '{' Type '}' | '{' [PropList] '}' 107 | FunctionType ::= ['<' GenericTypeList '>'] '(' [BoundTypeList] ')' '->' ReturnType 108 | ``` 109 | -------------------------------------------------------------------------------- /_pages/limited-terms-of-use.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: LIMITED USE LICENSE 4 | permalink: /limited-terms-of-use 5 | --- 6 |
7 |

ROBLOX, INC.

8 |

LIMITED USE LICENSE

9 |
10 | 11 | Roblox Corporation (“Roblox”) is making certain software for your access and use on a pre-release, trial, beta, evaluation, or similar limited purpose basis pursuant to this Limited Use License (the “License”). By clicking agree, downloading, accessing, or using the software and materials (which may include associated data and data sets, models, and documentation) accompanying this License (the “Licensed Software”), you agree to be bound by the terms and conditions of this License, including acknowledging that your rights are conditioned on compliance with these terms and revocable at any time, that the Licensed Software is made available on an “as-is” basis without warranty or guarantee of any kind, and that Roblox expressly reserves all rights not expressly granted in this License. 12 | Access and use of the Licensed Software may require applying for and maintaining an active registration with Roblox, the use of which account and associated services (the “Service”) is governed by and subject to separate terms (the “Terms of Use”). 13 | 14 | 1. **Evaluation Use of the Licensed Software.** Subject to your complete and ongoing compliance with this License (including any applicable Terms of Use), Roblox grants you a limited, nonexclusive, non-transferable, non-sublicensable, revocable permission to access and use the Licensed Software during the License Term in accordance with any applicable documentation, solely for the purpose of internal, non-production evaluation and testing of the Licensed Software, and solely in connection with your otherwise permitted use of the Roblox products and services for which the Licensed Software was designed. 15 | 16 | 2. **Source Code License.** Solely in the event that all or a portion of the Licensed Software is provided in source code form (the “Licensed Source”) the permission granted in Section 1 also includes the right to access and modify the Licensed Source, subject to the same limitations and restrictions as provided in Section 1 and elsewhere in this License and the Terms of Use. The modified version or other derivatives of such Licensed Source (in source or compiled form) will be deemed Licensed Software hereunder. 17 | 18 | 3. **Usage Guidelines.** You must not use the Licensed Software to transmit unlawful or otherwise objectionable material, transmit viruses or other harmful computer code, interfere with the performance of the Service or the data contained therein, attempt to gain unauthorized access to the Service or networks related to the Service, or interfere with another’s use of the Service. You must not distribute the Licensed Software or derivatives thereof in whole or in part, or modify, copy, or make derivative works based on, or otherwise reverse engineer, disassemble, or decompile any portion of the Licensed Software not made available to you by Roblox in source form. You may not access or use the Licensed Software or the Service in connection with any alternative or competitive service, or to otherwise reproduce, emulate, or replace features of the Service. You also may not use the Licensed Software to create malicious or abusive content (as determined by Roblox) or any content that violates a Roblox guideline or policy or use the Licensed Software in any manner that infringes, misappropriates, or otherwise violates any intellectual property right or other right of any person, or that violates any applicable laws. 19 | 20 | 4. **Feedback.** From time to time you may, but are not required to, provide Roblox with feedback regarding the Licensed Software, including by reporting any errors you encounter, providing suggestions for modifications and enhancements, submitting bug fixes, contributions, or other associated code, or making other similar submissions (collectively, “Feedback”). You retain ownership of your rights in Feedback. Notwithstanding, Feedback is provided to Roblox on a non-confidential basis (notwithstanding any indication to the contrary in any accompanying communication) and by submitting Feedback you hereby irrevocably grant Roblox and its successors an unrestricted, unlimited, worldwide, nonexclusive, transferable, sublicensable, fully paid up, royalty free, sublicensable (through multiple tiers) license to reproduce, distribute, publicly perform or display, and prepare derivatives of the Feedback, and to make, have made, use, sell, offer for sale, and import any product or service, and to practice any method disclosed in the Feedback, in any form or manner and without limitation, attribution, or compensation. 21 | 22 | 5. **Reservation of Rights.** Roblox retains all its right, title, and interest, including all intellectual property rights, in and to the Licensed Software and derivatives thereof, and nothing herein will be deemed to grant or confer on you any ownership of any such rights, whether expressly, by implication, estoppel, or otherwise. You acknowledge that Roblox may track usage of the Licensed Software in connection with the Services, and Roblox retains unrestricted rights to all related data, and any analytical results, models, methods or learnings derived therefrom. 23 | 24 | 6. **Additional Restrictions.** Your testing of the Licensed Software and the results thereof and your Feedback, are all Roblox confidential information (“Confidential Information”) and may not be used for any purpose other than your testing and evaluation of the Licensed Software, or shared with any other person or entity. You may not (i) disclose, permit disclosure of, publish, or disseminate Confidential Information to anyone (ii) perform any benchmarking test or similar comparative research, or (iii) disclose any results, opinions, or summaries of the Licensed Software or any Confidential Information. 25 | 26 | 7. **DISCLAIMER; LIMITATION OF LIABILITY.** THE LICENSED SOFTWARE IS PROVIDED “AS IS” AND WITH ALL FAULTS. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, ROBLOX HEREBY DISCLAIMS ALL WARRANTIES, WHETHER EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, NONINFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE, OR THAT THE LICENSED SOFTWARE SHALL BE ERROR-FREE OR UNINTERRUPTED. IN NO EVENT SHALL ROBLOX HAVE ANY DIRECT OR INDIRECT LIABILITY TO YOU FOR ANY REASON UNDER THIS LICENSE, INCLUDING ANY LOST PROFITS, LOSS OF DATA, LOSS OF USE, OR COSTS OF PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, OR FOR ANY SPECIAL, PUNITIVE, OR EXEMPLARY DAMAGES HOWEVER CAUSED AND, WHETHER IN CONTRACT, TORT OR UNDER ANY OTHER THEORY OF LIABILITY, WHETHER OR NOT EITHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. In addition, you agree and understand that the Licensed Software may be provided on an alpha, beta, or other pre-release basis, and Roblox is under no obligation to make any further releases, including any production release, and that any subsequent release may differ from the Licensed Software in any manner Roblox sees fit, including addition, modification, or removal of features and functionality present in the Licensed Software. You acknowledge that the limitations of liability and the disclaimers of warranties and damages set forth herein form an essential basis of the bargain between you and Roblox, and that they will survive and apply even if found to have failed of their essential purpose, to the greatest extent permissible pursuant to applicable law. 27 | 28 | 8. **Term and Termination.** This Agreement commences on the date you accept this License and, unless terminated earlier as described below or extended by mutual written agreement, shall terminate upon the later of the license term specified at the time you access the Licensed Software, or the public release of the production version of the Licensed Software (the “License Term”). Roblox may terminate the License (and your access to the Licensed Software and any associated materials) at any time for any reason or no reason. Any provisions that by their nature or express terms survive expiration or termination of this License will survive any expiration or termination of this License. Your obligations with respect to Confidential Information and Feedback will survive indefinitely, notwithstanding any eventual publication of a commercial or production version of the Licensed Software. Upon expiration or termination of this License for any reason, you must cease using the Licensed Software, and return or destroy copies of any Licensed Software or Licensed Source (and derivatives thereof) or other Confidential Information in your possession. 29 | -------------------------------------------------------------------------------- /_pages/news.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Luau News" 3 | permalink: /news/ 4 | layout: posts 5 | sidebar: "none" 6 | --- -------------------------------------------------------------------------------- /_pages/profile.md: -------------------------------------------------------------------------------- 1 | --- 2 | permalink: /profile 3 | title: Profiling 4 | toc: true 5 | --- 6 | 7 | One of main goals of Luau is to enable high performance code. To help with that goal, we are relentlessly optimizing the compiler and runtime - but ultimately, performance of their 8 | code is in developers' hands, and is a combination of good algorithm design and implementation that adheres to the strengths of the language. To help write efficient code, Luau 9 | provides a built-in profiler that samples the execution of the program and outputs a profiler dump that can be converted to an interactive flamegraph. 10 | 11 | To run the profiler, make sure you have an optimized build of the interpreter (otherwise profiling results are going to be very skewed) and run it with `--profile` argument: 12 | 13 | ``` 14 | $ luau --profile tests/chess.lua 15 | OK 8902 rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 16 | OK 2039 r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 0 17 | OK 2812 8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 0 18 | OK 9467 r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1 19 | OK 1486 rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ - 1 8 20 | OK 2079 r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10 21 | Profiler dump written to profile.out (total runtime 2.034 seconds, 20344 samples, 374 stacks) 22 | GC: 0.378 seconds (18.58%), mark 46.80%, remark 3.33%, atomic 1.93%, sweepstring 6.77%, sweep 41.16% 23 | ``` 24 | 25 | The resulting `profile.out` file can be converted to an SVG file by running `perfgraph.py` script that is part of Luau repository: 26 | 27 | ``` 28 | $ python tools/perfgraph.py profile.out >profile.svg 29 | ``` 30 | 31 | This produces an SVG file that can be opened in a browser (the image below is clickable): 32 | 33 | [![profile.svg](/assets/images/chess-profile.svg)](/assets/images/chess-profile.svg) 34 | 35 | In a flame graph visualization, the individual bars represent function calls, the width represents how much of the total program runtime they execute, and the nesting matches the call stack encountered during program execution. This is a fantastic visualization technique that allows you to hone in on the specific bottlenecks affecting 36 | your program performance, optimize those exact bottlenecks, and then re-generate the profile data and visualizer, and look for the next set of true bottlenecks (if any). 37 | 38 | Hovering your mouse cursor over individual sections will display detailed function information in the status bar and in a tooltip. If you want to Search for a specific named 39 | function, use the Search field in the upper right, or press Ctrl+F. 40 | 41 | Notice that some of the bars in the screenshot don't have any text. In some cases, there isn't enough room in the size of the bar to display the name. 42 | You can hover your mouse over those bars to see the name and source location of the function in the tool tip, or double-click to zoom in on that part of the flame graph. 43 | 44 | Some tooltips will have a source location for the function you're hovering over, but no name. Those are anonymous functions, or functions that were not declared in a way that 45 | allows Luau compiler to track the name. To fill in more names, you may want to make these changes to your code: 46 | 47 | `local myFunc = function() --[[ work ]] end` -> `local function myFunc() --[[ work ]] end` 48 | 49 | Even without these changes, you can hover over a given bar with no visible name and see it's source location. 50 | 51 | As any sampling profiler, this profiler relies on gathering enough information for the resulting output to be statistically meaningful. It may miss short functions if they 52 | aren't called often enough. By default the profiler runs at 10 kHz, this can be customized by passing a different parameter to `--profile=`. Note that higher 53 | frequencies result in higher profiling overhead and longer program execution, potentially skewing the results. 54 | 55 | This profiler doesn't track leaf C functions and instead attributes the time spent there to calling Luau functions. As a result, when thinking about why a given function is 56 | slow, consider not just the work it does immediately but also the library functions it calls. 57 | 58 | This profiler tracks time consumed by Luau thread stacks; when a thread calls another thread via `coroutine.resume`, the time spent is not attributed to the parent thread that's 59 | waiting for resume results. This limitation will be removed in the future. 60 | -------------------------------------------------------------------------------- /_pages/sandbox.md: -------------------------------------------------------------------------------- 1 | --- 2 | permalink: /sandbox 3 | title: Sandboxing 4 | toc: true 5 | --- 6 | 7 | Luau is safe to embed. Broadly speaking, this means that even in the face of untrusted (and in Roblox case, actively malicious) code, the language and the standard library don't allow unsafe access to the underlying system, and don't have known bugs that allow escaping out of the sandbox (e.g. to gain native code execution through ROP gadgets et al). Additionally, the VM provides extra features to implement isolation of privileged code from unprivileged code and protect one from the other; this is important if the embedding environment decides to expose some APIs that may not be safe to call from untrusted code, for example because they do provide controlled access to the underlying system or risk PII exposure through fingerprinting etc. 8 | 9 | This safety is achieved through a combination of removing features from the standard library that are unsafe, adding features to the VM that make it possible to implement sandboxing and isolation, and making sure the implementation is safe from memory safety issues using fuzzing. 10 | 11 | Of course, since the entire stack is implemented in C++, the sandboxing isn't formally proven - in theory, compiler or the standard library can have exploitable vulnerabilities. In practice these are very rare and usually found and fixed quickly. While implementing the stack in a safer language such as Rust would make it easier to provide these guarantees, to our knowledge (based on prior art) this would make it difficult to reach the level of performance required. 12 | 13 | ## Library 14 | 15 | Parts of the Lua 5.x standard library are unsafe. Some of the functions provide access to the host operating system, including process execution and file reads. Some functions lack sufficient memory safety checks. Some functions are safe if all code is untrusted, but can break the isolation barrier between trusted and untrusted code. 16 | 17 | The following libraries and global functions have been removed as a result: 18 | 19 | - `io.` library has been removed entirely, as it gives access to files and allows running processes 20 | - `package.` library has been removed entirely, as it gives access to files and allows loading native modules 21 | - `os.` library has been cleaned up from file and environment access functions (`execute`, `exit`, etc.). The only supported functions in the library are `clock`, `date`, `difftime` and `time`. 22 | - `debug.` library has been removed to a large extent, as it has functions that aren't memory safe and other functions break isolation; the only supported functions are `traceback` and `info` (which is similar to `debug.getinfo` but has a slightly different interface). 23 | - `dofile` and `loadfile` allowed access to file system and have been removed. 24 | 25 | To achieve memory safety, access to function bytecode has been removed. Bytecode is hard to validate and using untrusted bytecode may lead to exploits. Thus, `loadstring` doesn't work with bytecode inputs, and `string.dump`/`load` have been removed as they aren't necessary anymore. When embedding Luau, bytecode should be encrypted/signed to prevent MITM attacks as well, as the VM assumes that the bytecode was generated by the Luau compiler (which never produces invalid/unsafe bytecode). 26 | 27 | Finally, to make isolation possible within the same VM, the following global functions have reduced functionality: 28 | 29 | - `collectgarbage` only works with `"count"` argument, as modifying the state of GC can interfere with the expectations of other code running in the process. As such, `collectgarbage()` became an inferior version of `gcinfo()` and is deprecated. 30 | - `newproxy` only works with `true`/`false`/`nil` arguments. 31 | - `module` allowed overriding global packages and was removed as a result. 32 | 33 | > Note: `getfenv`/`setfenv` result in additional isolation challenges, as they allow injecting globals into scripts on the call stack. Ideally, these should be disabled as well, but unfortunately Roblox community relies on these for various reasons. This can be mitigated by limiting interaction between trusted and untrusted code, and/or using separate VMs. 34 | 35 | ## Environment 36 | 37 | The modification to the library functions are sufficient to make embedding safe, but aren't sufficient to provide isolation within the same VM. It should be noted that to achieve guaranteed isolation, it's advisable to load trusted and untrusted code into separate VMs; however, even within the same VM Luau provides additional safety features to make isolation cheaper. 38 | 39 | When initializing the default globals table, the tables are protected from modification: 40 | 41 | - All libraries (`string`, `math`, etc.) are marked as readonly 42 | - The string metatable is marked as readonly 43 | - The global table itself is marked as readonly 44 | 45 | This is using the VM feature that is not accessible from scripts, that prevents all writes to the table, including assignments, `rawset` and `setmetatable`. This makes sure that globals can't be monkey-patched in place, and can only be substituted through `setfenv`. 46 | 47 | By itself this would mean that code that runs in Luau can't use globals at all, since assigning globals would fail. While this is feasible, in Roblox we solve this by creating a new global table for each script, that uses `__index` to point to the builtin global table. This safely sandboxes the builtin globals while still allowing writing globals from each script. This also means that short of exposing special shared globals from the host, all scripts are isolated from each other. 48 | 49 | ## `__gc` 50 | 51 | Lua 5.1 exposes a `__gc` metamethod for userdata, which can be used on proxies (`newproxy`) to hook into garbage collector. Later versions of Lua extend this mechanism to work on tables. 52 | 53 | This mechanism is bad for performance, memory safety and isolation: 54 | 55 | - In Lua 5.1, `__gc` support requires traversing userdata lists redundantly during garbage collection to filter out finalizable objects 56 | - In later versions of Lua, userdata that implement `__gc` are split into separate lists; however, finalization prolongs the lifetime of the finalized objects which results in less prompt memory reclamation, and two-step destruction results in extra cache misses for userdata 57 | - `__gc` runs during garbage collection in context of an arbitrary thread which makes the thread identity mechanism used in Roblox to support trusted Luau code invalid 58 | - Objects can be removed from weak tables *after* being finalized, which means that accessing these objects can result in memory safety bugs, unless all exposed userdata methods guard against use-after-gc. 59 | - If `__gc` method ever leaks to scripts, they can call it directly on an object and use any method exposed by that object after that. This means that `__gc` and all other exposed methods must support memory safety when called on a destroyed object. 60 | 61 | Because of these issues, Luau does not support `__gc`. Instead it uses tag-based destructors that can perform additional memory cleanup during userdata destruction; crucially, these are only available to the host (so they can never be invoked manually), and they run right before freeing the userdata memory block which is both optimal for performance, and guaranteed to be memory safe. 62 | 63 | For monitoring garbage collector behavior the recommendation is to use weak tables instead. 64 | 65 | ## Interrupts 66 | 67 | In addition to preventing API access, it can be important for isolation to limit the memory and CPU usage of code that runs inside the VM. 68 | 69 | By default, no memory limits are imposed on the running code, so it's possible to exhaust the address space of the host; this is easy to configure from the host for Luau allocations, but of course with a rich API surface exposed by the host it's hard to eliminate this as a possibility. Memory exhaustion doesn't result in memory safety issues or any particular risk to the system that's running the host process, other than the host process getting terminated by the OS. 70 | 71 | Limiting CPU usage can be equally challenging with a rich API. However, Luau does provide a VM-level feature to try to contain runaway scripts which makes it possible to terminate any script externally. This works through a global interrupt mechanism, where the host can setup an interrupt handler at any point, and any Luau code is guaranteed to call this handler "eventually" (in practice this can happen at any function call or at any loop iteration). This still leaves the possibility of a very long running script open if the script manages to find a way to call a single C function that takes a lot of time, but short of that the interruption is very prompt. 72 | 73 | Roblox sets up the interrupt handler using a watchdog that: 74 | 75 | - Limits the runtime of any script in Studio to 10 seconds (configurable through Studio settings) 76 | - Upon client shutdown, interrupts execution of every running script 1 second after shutdown 77 | -------------------------------------------------------------------------------- /_pages/why.md: -------------------------------------------------------------------------------- 1 | --- 2 | permalink: /why 3 | title: Why Luau? 4 | --- 5 | 6 | Around 2006, [Roblox](https://www.roblox.com) started using Lua 5.1 as a scripting language for games. Over the years the runtime had to be tweaked to provide a safe, secure sandboxed environment; we gradually started accumulating small library changes and tweaks. 7 | 8 | Over the course of the last few years, instead of using Web-based stack for our player-facing application, Lua-based in-game UI and Qt-based editor UI, we've started consolidating a lot of the efforts and developing all of these using Roblox engine and Lua as a scripting language. 9 | 10 | Having grown a substantial internal codebase that needed to be correct and performant, and with the focus shifting a bit from novice game developers to professional studios building games on Roblox and our own teams of engineers building applications, there was a need to improve performance and quality of the code we were writing. 11 | 12 | Unlike mainline Lua, we also could not afford to do major breaking changes to the language (hence the 5.1 language baseline that remained unchanged for more than a decade). While faster implementations of Lua 5.1 like LuaJIT were available, they didn't meet our needs in terms of portability, ease of change and they didn't address the problem of developing robust code at scale. 13 | 14 | All of these motivated us to start reshaping Lua 5.1 that we started from into a new, derivative language that we call Luau. Our focus is on making the language more performant and feature-rich, and make it easier to write robust code through a combination of linting and type checking using a gradual type system. 15 | 16 | ## Complete rewrite? 17 | 18 | A very large part of Luau codebase is written from scratch. We needed a set of tools to be able to write language analysis tools; Lua has a parser that is integrated with the bytecode compiler, which makes it unsuitable for complex semantic analysis. For bytecode compilation, while a single pass compiler can deliver better compilation throughput and be simpler than a full frontend/backend, it significantly limits the optimizations that can be done at the bytecode level. 19 | 20 | Luau compiler and analysis tools are thus written from scratch, closely following the syntax and semantics of Lua. Our compiler is not single-pass, and instead relies on a set of analysis passes that run over the AST to produce efficient bytecode, followed by some post-process optimizations. 21 | 22 | As for the runtime, we had to rewrite the interpreter from scratch to get substantially faster performance; using a combination of techniques pioneered by LuaJIT and custom optimizations that are able to improve performance by taking control over the entire stack (language, compiler, interpreter, virtual machine), we're able to get close to LuaJIT interpreter performance while using C as an implementation language. 23 | 24 | The garbage collector and the core libraries represent more of an incremental change, where we used Lua 5.1 as a baseline but we're continuing to rewrite these as well with performance in mind. 25 | 26 | While Luau initially didn't include a JIT/AOT, native code generation has since been implemented for x64/arm64 platform as an optional component. 27 | -------------------------------------------------------------------------------- /_posts/2019-11-11-luau-recap-november-2019.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: single 3 | title: "Luau Recap: November 2019" 4 | --- 5 | 6 | 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! 7 | 8 | ## Debugger beta 9 | 10 | 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. 11 | 12 | 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. 13 | 14 | To use this, simply make sure that you’re enrolled in the new Lua VM beta: 15 | 16 | ![Enable New Lua VM]({{ site.url }}{{ site.baseurl }}/assets/images/luau-recap-november-2019-option.png) 17 | 18 | After this you can use the debugger as usual. If you see any bugs, please feel free to report them! 19 | 20 | ## Performance improvements 21 | 22 | * The for loop optimization that specializes `pairs/ipairs` now works for localized versions of these globals as well, as well as `next, table` expressions 23 | * a^k expressions are now faster for some trivial values of k such as 2 and 0.5 24 | * 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. 25 | * Accessing .X/.Y/.Z properties on Vector types is now ~30% faster 26 | * On Windows and Xbox, we’ve tuned our interpreter to be ~5-6% faster on Lua-intensive code 27 | * For a set of builtin functions, we now support very quickly calling them from VM via a new fastcall mechanism. 28 | 29 | 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`. 30 | 31 | ## Lua library extensions 32 | 33 | We’ve implemented most library features available in later versions of upstream Lua, including: 34 | 35 | * `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) 36 | * `table.move` from Lua 5.3 (useful for copying data between arrays) 37 | * `coroutine.isyieldable` from Lua 5.3 38 | * `math.log` now accepts a second optional argument (as seen in Lua 5.2) for the logarithm base 39 | 40 | We’ve also introduced two new functions in the table library: 41 | 42 | * `table.create(count, value)` can create an array-like table quickly 43 | * `table.find(table, value [, init])` can quickly find the numeric index of the element in the table 44 | 45 | Autocomplete support for `table.create`/`table.find` will ship next week 46 | 47 | ## Lua syntax extensions 48 | 49 | 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: 50 | 51 | * String literals now support `\z` (skip whitespace), `\x` (hexadecimal byte) and `\u` (Unicode codepoint) escape sequences 52 | 53 | and implemented a few extra changes: 54 | 55 | * Number literals now support binary literals, e.g. `0b010101` 56 | * Number literals now support underscores anywhere in the literal for easier digit grouping, e.g. `1_000_000` 57 | 58 | Note that the literal extensions aren’t currently supported in syntax highlighter in Studio but this will be amended soon. 59 | 60 | ## Error messages 61 | 62 | Error messages are slowly getting a bit of love. We’ve improved some runtime errors to be nicer, in particular: 63 | 64 | * When indexing operation fails, we now specify the key name or type, e.g. “attempt to index foo with ‘Health’” 65 | * When arithmetic operations fails, we now specify the type of arithmetic operation, e.g. “attempt to perform arithmetic (add) on table and number” 66 | 67 | 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'`. 68 | 69 | We are looking into some reports of misplaced line numbers on errors in multi-line expressions but this will only ship later. 70 | 71 | ## Correctness fixes 72 | 73 | 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: 74 | 75 | * Fixed some cases where we wouldn’t preserve negative zero (`-0`) 76 | * Fixed cases where `getfenv(0)` wouldn’t properly deoptimize access to builtin globals 77 | * Fixed cases where calling a function with >255 parameters would overflow the stack 78 | * 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) 79 | * Fixed cases where in Studio on Windows, constant-time comparisons with `NaNs` didn’t behave properly (`0/0==1`) 80 | 81 | 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. 82 | 83 | ## Script analysis 84 | 85 | 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: 86 | 87 | * 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. 88 | * 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 89 | * 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`) 90 | * We now detect and flag cases where in assignment expressions variables are implicitly initialized with nil or values are dropped during assignment 91 | * “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`) 92 | 93 | 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: 94 | 95 | * Local variables that shadow other local variables / global variables 96 | * Local variables that are assigned but never used 97 | * 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) 98 | 99 | ## Future plans 100 | 101 | 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. 102 | 103 | 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: 104 | 105 | As always don’t hesitate to reach out if you have any issues or have any suggestions for improvements. 106 | 107 | -------------------------------------------------------------------------------- /_posts/2020-01-16-luau-type-checking-beta.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: single 3 | title: "Luau Type Checking Beta" 4 | --- 5 | 6 | Hello! 7 | 8 | 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. 9 | 10 | I am very happy to offer a beta test into the second half of the Luau effort. 11 | 12 | ## Beta Test 13 | 14 | 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. 15 | 16 | Please try it out and tell us what you think, but be aware that this is not necessarily our final form. 🙂 17 | 18 | Beta testers can try it out by enabling the “Enable New Lua Script Analysis” beta feature in Roblox Studio. 19 | 20 | ## Overview 21 | 22 | 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. 23 | 24 | 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. 25 | 26 | ## Two Modes 27 | 28 | Luau runs in one of two modes: strict, and nonstrict. 29 | 30 | ### Nonstrict Mode 31 | 32 | 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. 33 | 34 | * If a local variable does not have a type annotation and it is not initially assigned a table, its type is any 35 | * Unannotated function parameters have type any 36 | * We do not check the number of values returned by a function 37 | * Passing too few or too many arguments to a function is ok 38 | 39 | ### Strict Mode 40 | 41 | 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. 42 | 43 | * The types of local variables, function parameters, and return types are deduced from how they are used 44 | * Errors are produced if a function returns an inconsistent number of parameters, or if it is passed the wrong number of arguments 45 | 46 | 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. 47 | ``` 48 | --!strict 49 | ``` 50 | 51 | ## New syntax 52 | 53 | You can write type annotations in 5 places: 54 | 55 | * After a local variable 56 | * After a function parameter 57 | * After a function declaration (to declare the function’s return type) 58 | * In a type alias, and 59 | * After an expression using the new as keyword. 60 | 61 | ``` 62 | local foo: number = 55 63 | 64 | function is_empty(param: string) => boolean 65 | return 0 == param:len() 66 | end 67 | 68 | type Point = {x: number, y: number} 69 | 70 | local baz = quux as number 71 | ``` 72 | 73 | ## Type syntax 74 | ### Primitive types 75 | 76 | `nil`, `number`, `string`, and `boolean` 77 | 78 | ### any 79 | The special type any signifies that Luau shouldn’t try to track the type at all. You can do anything with an any. 80 | 81 | ### Tables 82 | Table types are surrounded by curly braces. Within the braces, you write a list of name: type pairs: 83 | ``` 84 | type Point = {x: number, y: number} 85 | ``` 86 | Table types can also have indexers. This is how you describe a table that is used like a hash table or an array. 87 | ``` 88 | type StringArray = {[number]: string} 89 | 90 | type StringNumberMap = {[string]: number} 91 | ``` 92 | 93 | ### Functions 94 | 95 | Function types use a `=>` to separate the argument types from the return types. 96 | ``` 97 | type Callback = (string) => number 98 | ``` 99 | If a function returns more than one value, put parens around them all. 100 | ``` 101 | type MyFunction = (string) => (boolean, number) 102 | ``` 103 | 104 | ### Unions 105 | 106 | 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. 107 | ``` 108 | function ordinals(limit) 109 | local i = 0 110 | return function() => number | nil 111 | if i < limit then 112 | local t = i 113 | i = i + 1 114 | return t 115 | else 116 | return nil 117 | end 118 | end 119 | end 120 | ``` 121 | 122 | ### Options 123 | 124 | 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. 125 | ``` 126 | function foo(x: number, y: string?) end 127 | 128 | foo(5, 'five') -- ok 129 | foo(5) -- ok 130 | foo(5, 4) -- not ok 131 | ``` 132 | 133 | ### Type Inference 134 | 135 | If you don’t write a type annotation, Luau will try to figure out what it is. 136 | ``` 137 | --!strict 138 | local Counter = {count=0} 139 | 140 | function Counter:incr() 141 | self.count = 1 142 | return self.count 143 | end 144 | 145 | print(Counter:incr()) -- ok 146 | print(Counter.incr()) -- Error! 147 | print(Counter.amount) -- Error! 148 | ``` 149 | 150 | ## Future Plans 151 | 152 | This is just the first step! 153 | 154 | We’re excited about a whole bunch of stuff: 155 | 156 | * Nonstrict mode is way more permissive than we’d like 157 | * Generics! 158 | * Editor integration -------------------------------------------------------------------------------- /_posts/2020-02-25-luau-recap-february-2020.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: single 3 | title: "Luau Recap: February 2020" 4 | --- 5 | 6 | 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. 7 | 8 | A lot of people work on these improvements; thanks @Apakovtac, @EthicalRobot, @fun_enthusiast, @xyzzyismagic, @zeuxcg! 9 | 10 | 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: 11 | 12 | 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… 13 | 14 | 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 🤞 15 | 16 | We also fixed a few bugs in the type definitions for built-in functions/API and the type checker itself: 17 | 18 | * `table.concat` was accidentally treating the arguments as required 19 | * `string.byte` and `string.find` now have a correct precise type 20 | * `typeof` comparisons in if condition incorrectly propagated the inferred type into `elseif` branches 21 | 22 | We are also making the type checker more ergonomic and more correct. Two changes I want to call out are: 23 | 24 | * Type aliases declared with `type X = Y` are now co-recursive, meaning that they can refer to each other, e.g. 25 | 26 | ``` 27 | type array = { [number]: T } 28 | 29 | type Wheel = { radius: number, car: Car } 30 | type Car = { wheels: array } 31 | ``` 32 | 33 | * 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. 34 | 35 | ## Debugger (beta) 36 | 37 | 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. 38 | 39 | 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. 40 | 41 | 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. 42 | 43 | (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) 44 | 45 | ## Language 46 | 47 | This section is short and sweet this time around: 48 | 49 | * You can now use continue statement in for/while/repeat loops. :tada: 50 | 51 | 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. 52 | 53 | ## Performance 54 | 55 | 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. 56 | 57 | In particular, there are several memory and performance optimizations that shipped in the last few months: 58 | 59 | * Checking for truth (`if foo or foo and bar`) is now a bit faster, giving 2-3% performance improvements on some benchmarks 60 | * `table.create` (with value argument) and `table.pack` have been reimplemented and are ~1.5x faster than before 61 | * Internal mechanism for filling arrays has been made faster as well, which makes `Terrain:ReadVoxels` ~10% faster 62 | * Catching engine-generated errors with pcall/xpcall is now ~1.5x faster (this only affects performance of calls that generated errors) 63 | * String objects now take 8 bytes less memory per object (and in an upcoming change we’ll save a further 4 bytes) 64 | * 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. 65 | * 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. 66 | * Lua function calls have been optimized a bit more, gaining an extra 10% of performance in call-heavy benchmarks on Windows. 67 | * 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. 68 | 69 | ## Diagnostics 70 | 71 | We spent some time to improve error messages in various layers of the stack based on the reports from community. Specifically: 72 | 73 | * The static analysis warning about incorrect bounds for numeric for loops is now putting squigglies in the right place. 74 | * Fixed false positive static analysis warnings about unreachable code inside repeat…until loops in certain cases. 75 | * Multiline table construction expressions have a more precise line information now which helps in debugging since callstacks are now easier to understand 76 | * Incomplete statements (e.g. foo) now produce a more easily understandable parsing error 77 | * 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. 78 | * The legacy global `ypcall` is now flagged as deprecated by script analysis 79 | * 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: 80 | ``` 81 | local pi = 3․13 -- spoiler alert: this is not a dot! 82 | ``` 83 | produces `Unexpected Unicode character: U+2024. Did you mean '.'?` 84 | 85 | ## LoadLibrary removal 86 | 87 | 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). 88 | 89 | 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. 90 | 91 | 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. 92 | -------------------------------------------------------------------------------- /_posts/2020-08-11-luau-recap-august-2020.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: single 3 | title: "Luau Recap August 2020" 4 | --- 5 | 6 | Let’s talk about changes, big and small, that happened since June! 7 | 8 | Many people work on these improvements, with the team slowly growing - thanks @Apakovtac, @EthicalRobot, @fun_enthusiast, @mrow_pizza and @zeuxcg! 9 | 10 | ## Type annotations are safe to use in production! 11 | 12 | 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. 13 | 14 | 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: 15 | 16 | ![Fat arrow deprecated]({{ site.url }}{{ site.baseurl }}/assets/images/luau-recap-august-2020-arrow.png) 17 | 18 | … and must not be using the `__meta` property which no longer holds special meaning and we now warn you about that: 19 | 20 | ![meta deprecated]({{ site.url }}{{ site.baseurl }}/assets/images/luau-recap-august-2020-meta.png) 21 | 22 | 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)`. 23 | 24 | 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: 25 | 26 | * Better type checking for unary/binary operators 27 | * Improving error messages to make type errors more clear 28 | * Fixing a few remaining crashes for complex scripts 29 | * Fixing conflation of warnings/errors between different scripts with the same path in the tree 30 | * Improving type checking of globals in nonstrict mode (strict mode will continue to frown upon globals) 31 | 32 | 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! 33 | 34 | ## Format string analysis 35 | 36 | 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. 37 | 38 | 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. 39 | 40 | ![String format]({{ site.url }}{{ site.baseurl }}/assets/images/luau-recap-august-2020-format.png) 41 | 42 | 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. 43 | 44 | 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. 45 | 46 | ![String format]({{ site.url }}{{ site.baseurl }}/assets/images/luau-recap-august-2020-format2.png) 47 | 48 | ## Improvements to string. library 49 | 50 | We’ve upgraded the Luau string library to follow Lua 5.3 implementation; specifically: 51 | 52 | * `string.pack/string.packsize/string.unpack` are available for your byte packing needs 53 | * `string.gmatch` and other pattern matching functions now support `%g` and `\0` in patterns 54 | 55 | 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. 56 | 57 | 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: 58 | ``` 59 | local characterStateFormat = "fffbbbB" 60 | 61 | local characterState = string.pack(characterStateFormat, 62 | posx, posy, posz, dirx * 127, diry * 127, dirz * 127, health) 63 | ``` 64 | And unpack it like this after network transmission: 65 | ``` 66 | local posx, posy, posz, dirx, diry, dirz, health = 67 | string.unpack(characterStateFormat, characterState) 68 | dirx /= 127 69 | diry /= 127 70 | dirz /= 127 71 | ``` 72 | 73 | ## Assorted fixes 74 | 75 | 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. 76 | 77 | * 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 78 | * Fix debug information for constant upvalues; this fixes some bugs with watching local variables from the nested functions during debugging 79 | * Fix an off-by-one range check in string.find for init argument that could result in reading uninitialized memory 80 | * Fix type confusion for table.move target table argument that could result in reading or writing arbitrary memory 81 | * Fix type confusion for `debug.getinfo` in some circumstances (we don’t currently expose getinfo but have plans to do so in the future) 82 | * 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 83 | * 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. 84 | 85 | ## Performance improvements 86 | 87 | Only two changes in this category here this time around; some larger scale performance / memory improvements are still pending implementation. 88 | 89 | * Constant locals are now completely eliminated in cases when debugging is not available (so on server/client), making some scripts ~1-2% faster 90 | * Make script compilation ~5% faster by tuning the compiler analysis and code generation more carefully 91 | Oh, also `math.round` is now a thing which didn’t fit into any category above. -------------------------------------------------------------------------------- /_posts/2020-10-30-luau-recap-october-2020.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: single 3 | title: "Luau Recap: October 2020" 4 | --- 5 | 6 | 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! 7 | 8 | Many people work on these improvements, with the team slowly growing - thanks @Apakovtac, @EthicalRobot, @fun_enthusiast, @machinamentum, @mrow_pizza and @zeuxcg! 9 | 10 | ## Types are very close 11 | 12 | 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 🙂 13 | 14 | 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! 15 | 16 | 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. 17 | 18 | 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. 19 | 20 | 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: 21 | 22 | * Strict mode is now picky about passing extra arguments to functions, even though they are discarded silently at runtime, as this can hide bugs 23 | * The error message about using a : vs . during type checking is now much more precise 24 | * 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 25 | * Binary relational operators (`<` et al) are now stricter about the argument types and infer the argument types better 26 | * 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! 27 | * Fixed a few problems with indexing unions of tables with matching key types 28 | * Fixed issues with tracing types across modules (via require) in non-strict mode 29 | * Error messages for long table types are now trimmed to make the output look nicer 30 | * Improve the interaction between table types of unknown shape (`{ [string]: X }`) and table types of known shape. 31 | * Fix some issues with type checking table assignments 32 | * Fix some issues with variance of table fields 33 | * 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 34 | * Fix types for many builtins including `ipairs`, `table.create`, `Color3.fromHSV`, and a few others 35 | * 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!) 36 | * 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 37 | * 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 38 | * Improve the legibility of type errors when indexing a table without a given key 39 | * 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 40 | * Type checker now clearly reports cases where it finds a cyclic dependency between two modules 41 | * Type errors should now be correctly sorted in the Script Analysis widget 42 | * Error messages on mismatches between numbers of values in return statements should now be cleaner, as well as the associated type mismatch errors 43 | * Improve error messages for comparison operators 44 | * Flag attempts to require a non-module script during type checking 45 | * Fix some cases where a type/typeof guard could be misled into inferring a non-sensible type 46 | * 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 47 | * Improve the duplicate definition errors to specify the line of the first definition 48 | * 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 49 | * Improve the type errors for cyclic types to make them more readable 50 | * Make type checker more friendly by rewording a lot of error messages 51 | * Fix a few crashes in the type checker (although a couple more remain - working on them!) 52 | * … I think that’s it? 53 | * …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. 54 | 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. 55 | 56 | ## Type syntax changes 57 | 58 | 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: 59 | 60 | 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. 61 | 62 | 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. 63 | 64 | (if you’re a monster that uses mixed tables, you’ll have to continue using the longer syntax e.g. `{ [number]: string, n: number }`) 65 | 66 | ## Library changes 67 | 68 | There’s only a few small tweaks here this time around on the functionality front: 69 | 70 | * `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! 71 | * `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. 72 | 73 | ## Performance improvements 74 | 75 | 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! 76 | 77 | * 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! 78 | * Speaking of string methods, string.sub is now ~20% faster than before with the help of voodoo magic. 79 | 80 | ## Miscellaneous fixes 81 | 82 | There were a few small fixes that didn’t land into any specific category that I wanted to highlight: 83 | 84 | * 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! 85 | * Code following `assert(false)` is now treated as an unreachable destination from the linting and type checking point of view, similarly to error calls. 86 | * Linting support for various format strings has been greatly improved based on fantastic feedback from @Halalaluyafail3 (thanks!). 87 | 88 | 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. 89 | 90 | -------------------------------------------------------------------------------- /_posts/2020-11-19-luau-type-checking-release.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: single 3 | title: "Luau Type Checking Release" 4 | --- 5 | 6 | 10 months ago, we’ve started upon the journey of helping Roblox programmers 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. 7 | 8 | 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! 9 | 10 | ## What is type checking? 11 | 12 | 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! 13 | 14 | Consider this: 15 | ``` 16 | local p = Instance.new("Part") 17 | p.Positio = Vector3.new(1,2,3) 18 | ``` 19 | 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: 20 | 21 | !["Positio not found in class Part"]({{ site.url }}{{ site.baseurl }}/assets/images/luau-type-checking-release-screenshot.png) 22 | 23 | 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. 24 | 25 | ## How do I use type checking? 26 | 27 | A very important feature of Luau type checking you need to know about is that it has three modes: 28 | 29 | * `nocheck`, where we don’t type check the script in question. 30 | * `nonstrict`, where we type check the script but try to be lenient to allow commonly seen patterns even if they may violate type safety 31 | * `strict`, where we try to make sure that every single line of code you write is correct, and every value has a known type. 32 | 33 | The modes can be selected per script by writing a comment at the top of the script that starts with `--!`, e.g. `--!strict`. 34 | 35 | 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. 36 | 37 | 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: 38 | 39 | !["Studio option"]({{ site.url }}{{ site.baseurl }}/assets/images/luau-type-checking-release-studio-option.png) 40 | 41 | 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! 42 | 43 | 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. 44 | 45 | ## What are type annotations and how do I use them? 46 | 47 | 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: 48 | ``` 49 | function add(x, y) 50 | return x + y 51 | end 52 | ``` 53 | 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. 54 | 55 | 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 `:`: 56 | ``` 57 | function add(x: number, y: number) 58 | return x + y 59 | end 60 | ``` 61 | 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. 62 | 63 | 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). 64 | 65 | ## What happens when I get a type error? 66 | 67 | One concept that’s very important to understand is that right now type errors do not influence whether the code will run or not. 68 | 69 | 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! 70 | 71 | 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. 72 | 73 | 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. 74 | 75 | ## Do I have to re-learn Lua now?!? 76 | 77 | This is a question we get often! The answer is “no”. 78 | 79 | 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. 80 | 81 | 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. 82 | 83 | 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. 84 | 85 | ## Is there a performance difference? 86 | 87 | 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. 88 | 89 | 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. 90 | 91 | 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. 92 | 93 | ## What is next for types? 94 | 95 | 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: 96 | 97 | * Making nonstrict mode better to the point where we can enable it as a default for all Roblox scripts 98 | 99 | * Adding several features to make strict mode more powerful/friendly, such as typed variadics, type ascription and better generics support 100 | 101 | * Improving type refinements for type/typeof and nil checks 102 | 103 | * Making it possible to view the type of a variable in Studio 104 | 105 | * Reworking autocomplete to use type information instead of the current system 106 | 107 | 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. -------------------------------------------------------------------------------- /_posts/2021-03-01-luau-recap-february-2021.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: single 3 | title: "Luau Recap: February 2021" 4 | --- 5 | 6 | It's been a busy few months in Luau! 7 | 8 | ## Infallible parser 9 | 10 | 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. 11 | 12 | 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. 13 | 14 | The Luau parser now recovers from errors, which means, for example, we can give hints about programs in an IDE. 15 | 16 | ![A type error after a syntax error]({{ site.url }}{{ site.baseurl }}/assets/images/type-error-after-syntax-error.png) 17 | 18 | ## Type assertions 19 | 20 | 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". 21 | 22 | !["Consider adding a type annotation"]({{ site.url }}{{ site.baseurl }}/assets/images/type-annotation-needed.png) 23 | 24 | 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`. 25 | 26 | !["A type assertion y:any"]({{ site.url }}{{ site.baseurl }}/assets/images/type-annotation-provided.png) 27 | 28 | ## Typechecking improvements 29 | 30 | We've made various improvements to the Luau typechecker: 31 | 32 | * We allow duplicate function definitions in non-strict mode. 33 | * Better typechecking of `and`, `(f or g)()`, arrays with properties, and `string:format()`. 34 | * Improved typechecking of infinite loops. 35 | * Better error reporting for function type mismatch, type aliases and cyclic types. 36 | 37 | ## Performance improvements 38 | 39 | 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. 40 | 41 | * Table key assignments as well as global assignments have been optimized to play nicer with modern CPUs, yielding ~2% speedup in some benchmarks 42 | * Luau function calls are now ~3% faster in most cases; we also have more call optimizations coming up next month! 43 | * Modulo operation (%) is now a bit faster on Windows, resulting in ~2% performance improvement on some benchmarks 44 | 45 | !["Benchmark vs Lua 5.3"]({{ site.url }}{{ site.baseurl }}/assets/images/luau-recap-february-2021-benchmark.png) 46 | 47 | ## Debugger improvements 48 | 49 | 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). 50 | 51 | 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. 52 | 53 | ![Debugging a coroutine]({{ site.url }}{{ site.baseurl }}/assets/images/luau-recap-february-2021-debugger.png) 54 | 55 | ## Library changes 56 | 57 | `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. 58 | 59 | 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. 60 | 61 | ## Coming soon... 62 | 63 | * _Generic function types_ will soon be allowed! 64 | ``` 65 | function id(x: a): a 66 | return x 67 | end 68 | ``` 69 | 70 | * _Typed variadics_ will soon allow types to be given to functions with varying numbers of arguments! 71 | ``` 72 | function sum(...: number): number 73 | local result = 0 74 | for i,v in ipairs({...}) do 75 | result += v 76 | end 77 | return result 78 | end 79 | ``` 80 | 81 | And there will be more! 82 | -------------------------------------------------------------------------------- /_posts/2021-03-29-luau-recap-march-2021.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: single 3 | title: "Luau Recap: March 2021" 4 | --- 5 | 6 | It's been a busy month in Luau! 7 | 8 | ## Typed variadics 9 | 10 | Luau supports *variadic* functions, meaning ones which can take a variable number of arguments (varargs!) but previously there was no way to specify their type. Now you can! 11 | ``` 12 | function f(x: string, ...: number) 13 | print(x) 14 | print(...) 15 | end 16 | f("hi") 17 | f("lo", 5, 27) 18 | ``` 19 | This function takes a string, plus as many numbers as you like, but if you try calling it with anything else, you'll get a type error, for example `f("oh", true)` gives an error "Type `boolean` could not be converted into `number`" 20 | 21 | Variadics can be used in function declarations, and function types, for example 22 | ``` 23 | type T = { 24 | sum: (...number) -> number 25 | } 26 | function f(x: T) 27 | print(x.sum(1, 2, 3)) 28 | end 29 | ``` 30 | 31 | ## Generic functions 32 | 33 | **WARNING** Generic functions are currently disabled as we're fixing some critical bugs. 34 | 35 | ## Typechecking improvements 36 | 37 | We've made various improvements to the Luau typechecker: 38 | 39 | * Check bodies of methods whose `self` has type `any` 40 | * More precise types for `debug.*` methods 41 | * Mutually dependent type aliases are now handled correctly 42 | 43 | ## Performance improvements 44 | 45 | We are continuing to squeeze the performance out of all sorts of possible code; this is an ongoing process and we have many improvements in the pipeline, big and small. These are the changes that are already live: 46 | 47 | * Significantly optimized non-variadic function calls, improving performance by up to 10% on call-heavy benchmarks 48 | * Improve performance of `math.clamp`, `math.sign` and `math.round` by 2.3x, 2x and 1.6x respectively 49 | * Optimized `coroutine.resume` with ~10% gains on coroutine-heavy benchmarks 50 | * Equality comparisons are now a bit faster when comparing to constants, including `nil`; this makes some benchmarks 2-3% faster 51 | * Calls to builtin functions like `math.abs` or `bit32.rrotate` are now significantly faster in some cases, e.g. this makes SHA256 benchmark 25% faster 52 | * `rawset`, `rawget`, `rawequal` and 2-argument `table.insert` are now 40-50% faster; notably, `table.insert(t, v)` is now faster than `t[#t+1]=v` 53 | 54 | Note that we work off a set of benchmarks that we consider representative of the wide gamut of code that runs on Luau. If you have code that you think should be running faster, never hesitate to open a feature request / bug report on Roblox Developer Forum! 55 | 56 | ## Debugger improvements 57 | 58 | We continue to improve our Luau debugger and we have added a new feature to help with coroutine call debugging. 59 | The call stack that is being displayed while stopped inside a coroutine frame will display the chain of threads that have called it. 60 | 61 | Before: 62 | 63 | !["Old debugger"]({{ site.url }}{{ site.baseurl }}/assets/images/luau-recap-march-2021-debug-before.png) 64 | 65 | After: 66 | 67 | !["New debugger"]({{ site.url }}{{ site.baseurl }}/assets/images/luau-recap-march-2021-debug-after.png) 68 | 69 | We have restored the ability to break on all errors inside the scripts. 70 | This is useful in cases where you need to track the location and state of an error that is triggered inside 'pcall'. 71 | For example, when the error that's triggered is not the one you expected. 72 | 73 | !["Break on all exceptions"]({{ site.url }}{{ site.baseurl }}/assets/images/luau-recap-march-2021-debug-dialog.png) 74 | 75 | ## Library changes 76 | 77 | * Added the `debug.info` function which allows retrieving information about stack frames or functions; similarly to `debug.getinfo` from Lua, this accepts an options string that must consist of characters `slnfa`; unlike Lua that returns a table, the function returns all requested values one after another to improve performance. 78 | 79 | ## New logo 80 | 81 | Luau now has a shiny new logo! 82 | 83 | !["New logo!"]({{ site.url }}{{ site.baseurl }}/assets/images/luau.png) 84 | 85 | ## Coming soon... 86 | 87 | * Generic variadics! 88 | * Native Vector3 math with dramatic performance improvements! 89 | * Better tools for memory analysis! 90 | * Better treatment of cyclic requires during type checking! 91 | * Better type refinements including nil-ability checks, `and`/`or` and `IsA`! 92 | -------------------------------------------------------------------------------- /_posts/2021-04-30-luau-recap-april-2021.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: single 3 | title: "Luau Recap: April 2021" 4 | --- 5 | 6 | Another busy month in Luau with many performance improvements. 7 | 8 | ## Editor features 9 | 10 | Luau implementation now provides an internal API for type-aware autocomplete suggestions. 11 | 12 | Roblox Studio will be the first user of this API and we plan for a new beta feature to come soon in addition to existing Luau-powered beta features like Go To Declaration, Type Hovers and Script Function Filter (you should check those out!) 13 | 14 | ## Performance improvements 15 | 16 | Performance is a very important part of Luau implementation and we continue bringing in new performance optimizations: 17 | 18 | * We've finished the work on internal `vector` value type that will be used by `Vector3` type in Roblox. Improvements of up to 10x can be seen for primitive operations and some of our heavy `Vector3` benchmarks have seen 2-3x improvement. You can read more about this feature [on Roblox Developer forums](https://devforum.roblox.com/t/native-luau-vector3-beta/) 19 | * By optimizing the way string buffers are handled internally, we bring improvements to string operations including `string.lower`, `string.upper`, `string.reverse`, `string.rep`, `table.concat` and string concatenation operator `..`. Biggest improvements can be seen on large strings 20 | * Improved performance of `table.insert` and `table.remove`. Operations in the middle of large arrays can be multiple times faster with this change 21 | * Improved performance of internal table resize which brings additional 30% speedup for `table.insert` 22 | * Improved performance of checks for missing table fields 23 | 24 | ## Generic functions 25 | 26 | We had to temporarily disable generic function definitions last month after finding critical issues in the implementation. 27 | 28 | While they are still not available, we are making steady progress on fixing those issues and making additional typechecking improvements to bring them back in. 29 | 30 | ## Debugger improvements 31 | 32 | Debugging is now supported for parallel Luau Actors in Roblox Studio. 33 | 34 | Read more about the feature [on Roblox Developer forums](https://devforum.roblox.com/t/parallel-lua-beta/) and try it out yourself. 35 | 36 | ## Behavior changes 37 | 38 | Backwards compatibility is important for Luau, but sometimes a change is required to fix corner cases in the language / libraries or to improve performance. Even still, we try to keep impact of these changes to a minimum: 39 | 40 | * __eq tag method will always get called for table comparisons even when a table is compared to itself 41 | 42 | ## Coming soon... 43 | 44 | * Better type refinements for statements under a condition using a new constraint resolver. Luau will now understand complex conditions combining `and`/`not` and type guards with more improvements to come 45 | -------------------------------------------------------------------------------- /_posts/2021-05-31-luau-recap-may-2021.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: single 3 | title: "Luau Recap: May 2021" 4 | --- 5 | 6 | This month we have added a new small feature to the language and spent a lot of time improving our typechecker. 7 | 8 | ## Named function type arguments 9 | 10 | We've updated Luau syntax to support optional names of arguments inside function types. 11 | The syntax follows the same format as regular function argument declarations: `(a: number, b: string)` 12 | 13 | Names can be provided in any place where function type is used, for example: 14 | 15 | * in type aliases: 16 | ``` 17 | type MyCallbackType = (cost: number, name: string) -> string 18 | ``` 19 | 20 | * for variables: 21 | ``` 22 | local cb: (amount: number) -> number 23 | local function foo(cb: (name: string) -> ()) 24 | ``` 25 | 26 | Variadic arguments cannot have an extra name, they are already written as ...: number. 27 | 28 | These names are used for documentation purposes and we also plan to display them in Roblox Studio auto-complete and type hovers. 29 | They do not affect how the typechecking of Luau scripts is performed. 30 | 31 | ## Typechecking improvements 32 | 33 | Speaking of typechecking, we've implemented many improvements this month: 34 | * Typechecker will now visit bodies of all member functions, previously it didn't check methods if the self type was unknown 35 | * Made improvements to cyclic module import detection and error reporting 36 | * Fixed incorrect error on modification of table intersection type fields 37 | * When using an 'or' between a nillable type and a value, the resulting type is now inferred to be non-nil 38 | * We have improved error messages that suggest to use ':' for a method call 39 | * Fixed order of types in type mismatch error that was sometimes reversed 40 | * Fixed an issue with `table.insert` function signature 41 | * Fixed a bug which caused spurious unknown global errors 42 | 43 | We've also added new checks to our linter: 44 | * A new check will report uses of deprecated Roblox APIs 45 | * Linter will now suggest replacing globals with locals in more cases 46 | * New warning is generated if array loop starts or ends on index '0', but the array is indexed from '1' 47 | * FormatString lint will now check string patterns for `find`/`match` calls via `:` when object type is known to be a string 48 | 49 | We also fixed one of the sources for "Free types leaked into this module's public interface" error message and we are working to fix the remaining ones. 50 | 51 | As usual, typechecking improvements will not break execution of your games even if new errors get reported. 52 | 53 | ## Editor features 54 | 55 | We continue to improve our built-in support for auto-complete that will be used in future Roblox Studio updates and will make it easier to implement custom extensions for applications that support Language Server Protocol. 56 | 57 | As part of this work we will improve the type information provided by Roblox APIs to match actual arguments and results. 58 | 59 | ## Behavior changes 60 | 61 | When a relational comparison fails at runtime, the error message now specifies the comparison direction (e.g. `attempt to compare nil <= number`) 62 | 63 | ## Performance improvements 64 | 65 | * Improved performance of table lookup with an index operator and a literal string: `t["name"]` 66 | * Bytecode compilation is now ~5% faster which can improve server startup time for games with lots of scripts 67 | -------------------------------------------------------------------------------- /_posts/2021-06-30-luau-recap-june-2021.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: single 3 | title: "Luau Recap: June 2021" 4 | --- 5 | 6 | Most of our team was busy working on improving Luau interaction with Roblox Studio for an upcoming feature this month, but we were able to add typechecking and performance improvements as well! 7 | 8 | ## Constraint Resolver 9 | 10 | To improve type inference under conditional expressions and other dynamic type changes (like assignments) we have introduced a new constraint resolver framework into Luau type checker. 11 | 12 | This framework allows us to handle more complex expressions that combine `and`/`not` operators and type guards. 13 | 14 | Type guards support include expressions like: 15 | 16 | * `if instance:IsA("ClassName") then` 17 | * `if enum:IsA("EnumName") then` 18 | * `if type(v) == "string" then` 19 | 20 | This framework is extensible and we have plans for future improvements with `a == b`/`a ~= b` equality constraints and handling of table field assignments. 21 | 22 | It is now also possible to get better type information inside `else` blocks of an `if` statement. 23 | 24 | A few examples to see the constraint resolver in action: 25 | ```lua 26 | function say_hello(name: string?) 27 | -- extra parentheses were enough to trip the old typechecker 28 | if (name) then 29 | print("Hello " .. name .. "!") 30 | else 31 | print("Hello mysterious stranger!") 32 | end 33 | end 34 | ``` 35 | ```lua 36 | function say_hello(name: string?, surname: string?) 37 | -- but now we handle that and more complex expressions as well 38 | if not (name and surname) then 39 | print("Hello mysterious stranger!") 40 | else 41 | print("Hello " .. name .. " " .. surname .. "!") 42 | end 43 | end 44 | ``` 45 | 46 | Please note that constraints are currently placed only on local and global variables. 47 | One of our goals is to include support for table members in the future. 48 | 49 | ## Typechecking improvements 50 | 51 | We have improved the way we handled module `require` calls. Previously, we had a simple pattern match on the `local m = require(...)` statement, but now we have replaced it with a general handling of the function call in any context. 52 | 53 | Handling of union types in equality operators was fixed to remove incorrect error reports. 54 | 55 | A new `IsA` method was introduced to EnumItem to check the type of a Roblox Enum. 56 | This is intended to replace the `enumItem.EnumType == Enum.NormalId` pattern in the code for a construct that allows our constraint resolver to infer better types. 57 | 58 | Additional fixes include: 59 | * `table.pack` return type was fixed 60 | * A limit was added for deeply nested code blocks to avoid a crash 61 | * We have improved the type names that are presented in error messages and Roblox Studio 62 | * Error recovery was added to field access of a `table?` type. While you add a check for `nil`, typechecking can continue with better type information in other expressions. 63 | * We handled a few internal compiler errors and rare crashes 64 | 65 | ## Editor features 66 | 67 | If you have Luau-Powered Type Hover beta feature enabled in Roblox Studio, you will see more function argument names inside function type hovers. 68 | 69 | ## Behavior changes 70 | 71 | We no longer allow referencing a function by name inside argument list of that function: 72 | 73 | `local function f(a: number, b: typeof(f)) -- 'f' is no longer visible here` 74 | 75 | ## Performance improvements 76 | 77 | As always, we look for ways to improve performance of your scripts: 78 | * We have fixed memory use of Roblox Actor scripts in Parallel Luau beta feature 79 | * Performance of table clone through `table.move` has been greatly improved 80 | * Table length lookup has been optimized, which also brings improvement to table element insertion speed 81 | * Built-in Vector3 type support that we mentioned in [April](https://devforum.roblox.com/t/native-luau-vector3-beta/) is now enabled for everyone 82 | -------------------------------------------------------------------------------- /_posts/2021-07-30-luau-recap-july-2021.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: single 3 | title: "Luau Recap: July 2021" 4 | --- 5 | 6 | Our team was still busy working on upcoming Studio Beta feature for script editor, but we did fit in multiple typechecking improvements. 7 | 8 | ## Typechecking improvements 9 | 10 | A common complaint that we've received was a false-positive error when table with an optional or union element type is defined: 11 | ```lua 12 | --!strict 13 | type Foo = {x: number | string} 14 | local foos: {Foo} = { 15 | {x = 1234567}, 16 | {x = "hello"} -- Type 'string' could not be converted into 'number' 17 | } 18 | ``` 19 | This case is now handled and skipping optional fields is allowed as well: 20 | ```lua 21 | --!strict 22 | type Foo = { 23 | a: number, 24 | b: number? 25 | } 26 | local foos: {Foo} = { 27 | { a = 1 }, 28 | { a = 2, b = 3 } -- now ok 29 | } 30 | ``` 31 | Current fix only handles table element type in assignments, but we plan to extend that to function call arguments and individual table fields. 32 | 33 | Like we've mentioned last time, we will continue working on our new type constraint resolver and this month it learned to handle more complex expressions (including type guards) inside `assert` conditions: 34 | ```lua 35 | --!strict 36 | local part = script.Parent:WaitForChild("Part") 37 | assert(part:IsA("BasePart")) 38 | local basepart: BasePart = part -- no longer an error 39 | ``` 40 | 41 | And speaking of assertions, we applied a minor fix so that the type of the `assert` function correctly defines a second optional `string?` parameter. 42 | 43 | We have also fixed the type of `string.gmatch` function reported by one of the community members. 44 | We know about issues in a few additional library functions and we'll work to fix them as well. 45 | 46 | Hopefully, you didn't see 'free type leak' errors that underline your whole script, but some of you did and reported them to us. 47 | We read those reports and two additional cases have been fixed this month. 48 | We now track only a single one that should be fixed next month. 49 | 50 | Another false positive error that was fixed involves tables with __call metatable function. 51 | We no longer report a type error when this method is invoked and we'll also make sure that given arguments match the function definition: 52 | ```lua 53 | --!strict 54 | local t = { x = 2 } 55 | 56 | local x = setmetatable(t, { 57 | __call = function(self, a: number) 58 | return a * self.x 59 | end 60 | }) 61 | local a = x(2) -- no longer an error 62 | ``` 63 | Please note that while call operator on a table is now handled, function types in Luau are distinct from table types and you'll still get an error if you try to assign this table to a variable of a function type. 64 | 65 | ## Linter improvements 66 | 67 | A new 'TableOperations' lint check was added that will detect common correctness or performance issues with `table.insert` and `table.remove`: 68 | ```lua 69 | -- table.insert will insert the value before the last element, which is likely a bug; consider removing the second argument or wrap it in parentheses to silence 70 | table.insert(t, #t, 42) 71 | 72 | -- table.insert will append the value to the table; consider removing the second argument for efficiency 73 | table.insert(t, #t + 1, 42) 74 | 75 | -- table.insert uses index 0 but arrays are 1-based; did you mean 1 instead? 76 | table.insert(t, 0, 42) 77 | 78 | -- table.remove uses index 0 but arrays are 1-based; did you mean 1 instead? 79 | table.remove(t, 0) 80 | 81 | -- table.remove will remove the value before the last element, which is likely a bug; consider removing the second argument or wrap it in parentheses to silence 82 | table.remove(t, #t - 1) 83 | 84 | -- table.insert may change behavior if the call returns more than one result; consider adding parentheses around second argument 85 | table.insert(t, string.find("hello", "h")) 86 | ``` 87 | 88 | Another new check is 'DuplicateConditions'. The name speaks for itself, `if` statement chains with duplicate conditions and expressions containing `and`/`or` operations with redundant parts will now be detected: 89 | ```lua 90 | if x then 91 | -- ... 92 | elseif not x then 93 | -- ... 94 | elseif x̳ then -- Condition has already been checked on line 1 95 | -- ... 96 | end 97 | 98 | local success = a and a̳ -- Condition has already been checked on column 17 99 | 100 | local good = (a or b) or a̳ -- Condition has already been checked on column 15 101 | ``` 102 | 103 | We've also fixed an incorrect lint warning when `typeof` is used to check for `EnumItem`. 104 | 105 | ## Editor features 106 | 107 | An issue was fixed that prevented the debugger from displaying values inside Roblox callback functions when an error was reported inside of it. 108 | 109 | ## Behavior changes 110 | 111 | `table.insert` will no longer move elements forward 1 spot when index is negative or 0. 112 | 113 | This change also fixed a performance issue when `table.insert` was called with a large negative index. 114 | 115 | The 'TableOperations' lint mentioned earlier will flag cases where insertion at index 0 is performed. 116 | -------------------------------------------------------------------------------- /_posts/2021-08-31-luau-recap-august-2021.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: single 3 | title: "Luau Recap: August 2021" 4 | --- 5 | 6 | ## Editor features 7 | 8 | The Roblox Studio [Luau-Powered Autocomplete & Language Features Beta](https://devforum.roblox.com/t/script-editor-luau-powered-autocomplete-language-features-beta) that our team has been working on has finally been released! 9 | Be sure to check that out and leave your feedback for things we can improve. 10 | 11 | To support that feature, a lot of work went into: 12 | * Improving fault-tolerant parser recovery scenarios 13 | * Storing additional information in the AST, including comments, better location information and partial syntax data 14 | * Tracking additional information about types and their fields, including tracking definition locations, function argument names, deprecation state and custom Roblox-specific tags 15 | * Updating reflection information to provide more specific `Instance` types and correct previously missing or wrong type annotations 16 | * Hybrid typechecking mode which tries to infer types even in scripts with no typechecking enabled 17 | * Support for types that are attached to the `DataModel` tree elements to provide instance member information 18 | * Placing limits to finish typechecking in a finite space/time 19 | * Adding Autocomplete API for the Roblox Studio to get location-based entity information and appropriate suggestions 20 | * Additional type inference engine improvements and fixes 21 | 22 | While our work continues to respond to the feedback we receive, our team members are shifting focus to add generic functions, improve type refinements in conditionals, extend Parallel Luau, improve Lua VM performance and provide documentation. 23 | 24 | ## Typechecking improvements 25 | 26 | Type constraint resolver now remembers constraints placed on individual table fields. 27 | 28 | This should fix false-positive errors reported after making sure the optional table field is present: 29 | ```lua 30 | --!strict 31 | local t: {value: number?} = {value = 2} 32 | 33 | if t.value then 34 | local v: number = t.value -- ok 35 | end 36 | ``` 37 | 38 | And it can also refine field type to a more specific one: 39 | ```lua 40 | --!strict 41 | local t: {value: string|number} = {value = 2} 42 | 43 | if type(t.value) == "number" then 44 | return t.value * 2 -- ok 45 | end 46 | ``` 47 | 48 | Like before, combining multiple conditions using 'and' and 'not' is also supported. 49 | 50 | --- 51 | 52 | Constructing arrays with different values for optional/union types are now also supported for individual table fields and in functions call arguments: 53 | ```lua 54 | --!strict 55 | type Foo = {x: number | string, b: number?} 56 | 57 | local function foo(l: {Foo}) end 58 | 59 | foo({ 60 | {x = 1234567}, 61 | {x = "hello"}, -- now ok 62 | }) 63 | 64 | type Bar = {a: {Foo}} 65 | 66 | local foos: Bar = {a = { 67 | {x = 1234567}, 68 | {x = "hello", b = 2}, -- now ok 69 | }} 70 | ``` 71 | 72 | --- 73 | 74 | Finally, we have fixed an issue with Roblox class field access using indexing like `part["Anchored"] = true`. 75 | 76 | ## Linter improvements 77 | 78 | We have added a new linter check for duplicate local variable definitions. 79 | 80 | It is created to find duplicate names in cases like these: 81 | ```lua 82 | local function foo(a1, a2, a2) -- Function argument 'a2' already defined on column 24 83 | local a1, a2, a2 = f() -- Variable 'a2' already defined on column 11 84 | 85 | local bar = {} 86 | function bar:test(self) -- Function argument 'self' already defined implicitly 87 | ``` 88 | 89 | Our UnknownType linter warning was extended to check for correct class names passed into `FindFirstChildOfClass`, `FindFirstChildWhichIsA`, `FindFirstAncestorOfClass` and `FindFirstAncestorWhichIsA` functions. 90 | 91 | ## Performance improvements 92 | 93 | We have added an optimization to 'table.unpack' for 2x performance improvement. 94 | 95 | We've also implemented an extra optimization for tables to predict required table capacity based on fields that are assigned to it in the code after construction. This can reduce the need to reallocate tables. 96 | 97 | Variadic call performance was fine-tuned and is now ~10% faster. 98 | 99 | Construction of array literals was optimized for a ~7% improvement. 100 | 101 | Another optimization this month changes the location and rate of garbage collection invocations. 102 | We now try to avoid calling GC during the script execution and perform all the work in the GcJob part of the frame (it could be seen in the performance profiler). When possible, we can now skip that job in the frame completely, if we have some memory budget available. 103 | 104 | ## Other improvements 105 | 106 | For general stability improvements we fixed a crash when strange types like '`nil??`' are used and when users have their own global functions named '`require`'. 107 | 108 | Indexing a table with an incompatible type will now show squiggly error lines under the index instead of the whole expression, which was a bit misleading. 109 | 110 | An issue with debug information that caused `repeat ... until` condition line to be skipped when stepping was fixed. 111 | 112 | Type output was improved to replace display of types like '`{(g405) -> g405}`' with '`{(a) -> a}`'. 113 | -------------------------------------------------------------------------------- /_posts/2021-09-30-luau-recap-september-2021.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: single 3 | title: "Luau Recap: September 2021" 4 | --- 5 | 6 | ## Generic functions 7 | 8 | The big news this month is that generic functions are back! 9 | 10 | Luau has always supported type inference for generic functions, for example: 11 | ```lua 12 | type Point = { x: X, y: Y } 13 | function swap(p) 14 | return { x = p.y, y = p.x } 15 | end 16 | local p : Point = swap({ x = "hi", y = 37 }) 17 | local q : Point = swap({ x = "hi", y = true }) 18 | ``` 19 | but up until now, there's been no way to write the type of `swap`, since Luau didn't have type parameters to functions (just regular old data parameters). Well, now you can: 20 | ```lua 21 | function swap(p : Point): Point 22 | return { x = p.y, y = p.x } 23 | end 24 | ``` 25 | Generic functions can be used in function declarations, and function types too, for example 26 | ```lua 27 | type Swapper = { swap : (Point) -> Point } 28 | ``` 29 | 30 | People may remember that back in 31 | [April](https://devforum.roblox.com/t/luau-recap-april-2021/1205599) we 32 | announced generic functions, but then had to disable them. That was 33 | because [DataBrain](https://devforum.roblox.com/u/databrain) discovered a [nasty 34 | interaction](https://devforum.roblox.com/t/recent-type-system-regressions-for-generic-parametered-functions/) 35 | between `typeof` and generics, which meant that it was possible to 36 | write code that needed nested generic functions, which weren't 37 | supported back then. 38 | 39 | Well, now we do support nested generic functions, so you can write code like 40 | ```lua 41 | function mkPoint(x) 42 | return function(y) 43 | return { x = x, y = y } 44 | end 45 | end 46 | ``` 47 | and have Luau infer a type where a generic function returns a generic function 48 | ```lua 49 | function mkPoint(x : X) : (Y) -> Point 50 | return function(y : Y) : Point 51 | return { x = x, y = y } 52 | end 53 | end 54 | ``` 55 | For people who like jargon, Luau now supports *Rank N Types*, where 56 | previously it only supported Rank 1 Types. 57 | 58 | ## Bidirectional typechecking 59 | 60 | Up until now, Luau has used *bottom-up* typechecking. For example, for 61 | a function call `f(x)` we first find the type of `f` (say it's 62 | `(T)->U`) and the type for `x` (say it's `V`), make sure that `V` is 63 | a subtype of `T`, so the type of `f(x)` is `U`. 64 | 65 | This works in many cases, but has problems with examples like registering 66 | callback event handlers. In code like 67 | ```lua 68 | part.Touched:Connect(function (other) ... end) 69 | ``` 70 | if we try to typecheck this bottom-up, we have a problem because 71 | we don't know the type of `other` when we typecheck the body of the function. 72 | 73 | What we want in this case is a mix of bottom-up and *top-down* typechecking. 74 | In this case, from the type of `part.Touched:Connect` we know that `other` must 75 | have type `BasePart`. 76 | 77 | This mix of top-down and bottom-up typechecking is called 78 | *bidirectional typechecking*, and means that tools like type-directed 79 | autocomplete can provide better suggestions. 80 | 81 | ## Editor features 82 | 83 | We have made some improvements to the Luau-powered autocomplete beta feature in Roblox Studio: 84 | 85 | * We no longer give autocomplete suggestions for client-only APIs in server-side scripts, 86 | or vice versa. 87 | * For table literals with known shape, we provide autocomplete suggestions for properties. 88 | * We provide autocomplete suggestions for `Player.PlayerGui`. 89 | * Keywords such as `then` and `else` are autocompleted better. 90 | * Autocompletion is disabled inside a comment span (a comment starting `--[[`). 91 | 92 | ## Typechecking improvements 93 | 94 | In other typechecking news: 95 | 96 | * The Luau constraint resolver can now refine the operands of equality expressions. 97 | * Luau type guard refinements now support more arbitrary cases, for instance `typeof(foo) ~= "Instance"` eliminates anything not a subclass of `Instance`. 98 | * We fixed some crashes caused by use-after-free during type inference. 99 | * We do a better job of tracking updates when script is moved inside the data model. 100 | * We fixed one of the ways that [recursive types could cause free types to leak](https://devforum.roblox.com/t/free-types-leaked-into-this-modules-public-interface/1459070). 101 | * We improved the way that `return` statements interact with mutually recursive 102 | function declarations. 103 | * We improved parser recovery from code which looks like a function call (but isn't) such as 104 | ```lua 105 | local x = y 106 | (expr)[smth] = z 107 | ``` 108 | * We consistently report parse errors before type errors. 109 | * We display more types as `*unknown*` rather than as an internal type name like `error####`. 110 | * Luau now infers the result of `Instance:Clone()` much more accurately. 111 | 112 | ## Performance improvements 113 | 114 | * `Vector3.new` constructor has been optimized and is now ~2x faster 115 | * A previously implemented optimization for table size prediction has been enhanced to predict final table size when `setmetatable` is used, such as `local self = setmetatable({}, Klass)` 116 | * Method calls for user-specified objects have been optimized and are now 2-4% faster 117 | * `debug.traceback` is now 1.5x faster, although `debug.info` is likely still superior for performance-conscious code 118 | * Creating table literals with explicit numeric indices, such as `{ [1] = 42 }`, is now noticeably faster, although list-style construction is still recommended. 119 | 120 | ## Other improvements 121 | 122 | * The existing 'TableLiteral' lint now flags cases when table literals have duplicate numeric indices, such as `{ [1] = 1, [1] = 2 }` 123 | -------------------------------------------------------------------------------- /_posts/2021-10-31-luau-recap-october-2021.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: single 3 | title: "Luau Recap: October 2021" 4 | --- 5 | 6 | ## if-then-else expression 7 | 8 | In addition to supporting standard if *statements*, Luau adds support for if *expressions*. 9 | Syntactically, `if-then-else` expressions look very similar to if statements. 10 | However instead of conditionally executing blocks of code, if expressions conditionally evaluate expressions and return the value produced as a result. 11 | Also, unlike if statements, if expressions do not terminate with the `end` keyword. 12 | 13 | Here is a simple example of an `if-then-else` expression: 14 | ```lua 15 | local maxValue = if a > b then a else b 16 | ``` 17 | 18 | `if-then-else` expressions may occur in any place a regular expression is used. 19 | The `if-then-else` expression must match `if then else `; 20 | it can also contain an arbitrary number of `elseif` clauses, like `if then elseif then else `. 21 | Note that in either case, `else` is mandatory. 22 | 23 | Here's is an example demonstrating `elseif`: 24 | ```lua 25 | local sign = if x < 0 then -1 elseif x > 0 then 1 else 0 26 | ``` 27 | 28 | **Note:** In Luau, the `if-then-else` expression is preferred vs the standard Lua idiom of writing `a and b or c` (which roughly simulates a ternary operator). However, the Lua idiom may return an unexpected result if `b` evaluates to false. 29 | The `if-then-else` expression will behave as expected in all situations. 30 | 31 | ## Library improvements 32 | 33 | New additions to the `table` library have arrived: 34 | 35 | ```lua 36 | function table.freeze(t) 37 | ``` 38 | 39 | Given a non-frozen table, freezes it such that all subsequent attempts to modify the table or assign its metatable raise an error. 40 | If the input table is already frozen or has a protected metatable, the function raises an error; otherwise it returns the input table. 41 | Note that the table is frozen in-place and is not being copied. 42 | Additionally, only `t` is frozen, and keys/values/metatable of `t` don't change their state and need to be frozen separately if desired. 43 | 44 | ```lua 45 | function table.isfrozen(t): boolean 46 | ``` 47 | 48 | Returns `true` if and only if the input table is frozen. 49 | 50 | ## Typechecking improvements 51 | 52 | We continue work on our type constraint resolver and have multiple improvements this month. 53 | 54 | We now resolve constraints that are created by `or` expressions. 55 | In the following example, by checking against multiple type alternatives, we learn that value is a union of those types: 56 | ```lua 57 | --!strict 58 | local function f(x: any) 59 | if type(x) == "number" or type(x) == "string" then 60 | local foo = x -- 'foo' type is known to be 'number | string' here 61 | -- ... 62 | end 63 | end 64 | ``` 65 | 66 | Support for `or` constraints allowed us to handle additional scenarios with `and` and `not` expressions to reduce false positives after specific type guards. 67 | 68 | And speaking of type guards, we now correctly handle sub-class relationships in those checks: 69 | ```lua 70 | --!strict 71 | local function f(x: Part | Folder | string) 72 | if typeof(x) == "Instance" then 73 | local foo = x -- 'foo' type is known to be 'Part | Folder' here 74 | else 75 | local bar = x -- 'bar' type is known to be 'string' here 76 | end 77 | end 78 | ``` 79 | 80 | One more fix handles the `a and b or c` expression when 'b' depends on 'a': 81 | ```lua 82 | --!strict 83 | function f(t: {x: number}?) 84 | local a = t and t.x or 5 -- 'a' is a 'number', no false positive errors here 85 | end 86 | ``` 87 | 88 | Of course, our new if-then-else expressions handle this case as well. 89 | ```lua 90 | --!strict 91 | function f(t: {x: number}?) 92 | local a = if t then t.x else 5 -- 'a' is a 'number', no false positive errors here 93 | end 94 | ``` 95 | 96 | --- 97 | We have extended bidirectional typechecking that was announced last month to propagate types in additional statements and expressions. 98 | ```lua 99 | --!strict 100 | function getSortFunction(): (number, number) -> boolean 101 | return function(a, b) return a > b end -- a and b are now known to be 'number' here 102 | end 103 | 104 | local comp = getSortFunction() 105 | 106 | comp = function(a, b) return a < b end -- a and b are now known to be 'number' here as well 107 | ``` 108 | 109 | --- 110 | We've also improved some of our messages with union types and optional types (unions types with `nil`). 111 | 112 | When optional types are used incorrectly, you get better messages. For example: 113 | ```lua 114 | --!strict 115 | function f(a: {number}?) 116 | return a[1] -- "Value of type '{number}?' could be nil" instead of "'{number}?' is not a table' 117 | end 118 | ``` 119 | 120 | When a property of a union type is accessed, but is missing from some of the options, we will report which options are not valid: 121 | ```lua 122 | --!strict 123 | type A = { x: number, y: number } 124 | type B = { x: number } 125 | local a: A | B 126 | local b = a.y -- Key 'y' is missing from 'B' in the type 'A | B' 127 | ``` 128 | 129 | --- 130 | When we enabled generic functions last month, some users might have seen a strange error about generic functions not being compatible with regular ones. 131 | 132 | This was caused by undefined behaviour of recursive types. 133 | We have now added a restriction on how generic type parameters can be used in recursive types: [RFC: Recursive type restriction](https://github.com/Roblox/luau/blob/master/rfcs/recursive-type-restriction.md) 134 | 135 | ## Performance improvements 136 | 137 | An improvement to the Stop-The-World (atomic in Lua terms) stage of the garbage collector was made to reduce time taken by that step by 4x factor. 138 | While this step only happens once during a GC cycle, it cannot be split into small parts and long times were visible as frame time spikes. 139 | 140 | Table construction and resize was optimized further; as a result, many instances of table construction see 10-20% improvements 141 | for smaller tables on all platforms and 20%+ improvements on Windows. 142 | 143 | Bytecode compiler has been optimized for giant table literals, resulting in 3x higher compilation throughput for certain files on AMD Zen architecture. 144 | 145 | Coroutine resumption has been optimized and is now ~10% faster for coroutine-heavy code. 146 | 147 | Array reads and writes are also now a bit faster resulting in 1-3% lift in array-heavy benchmarks. 148 | -------------------------------------------------------------------------------- /_posts/2021-11-03-luau-goes-open-source.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: single 3 | title: "Luau Goes Open-Source" 4 | --- 5 | 6 | When Roblox was created 15 years ago, we chose Lua as the scripting language. Lua was small, fast, easy to embed and learn and opened up enormous possibilities for our developers. 7 | 8 | A lot in Roblox was built on Lua including hundreds of thousands of lines of internally-developed code that powers Roblox App and Roblox Studio to this day, and the millions of experiences that developers have created. For many of them, it was the first programming language they’ve learned. 9 | 10 | A few years ago, we started looking into how we can evolve Lua to be even faster, have better ergonomics, make it easier to write robust code and to unlock an ecosystem of rich tooling—from better static analysis to IDE integrations. 11 | 12 | This is how Luau was born. 13 | 14 | Luau is a new language that started from Lua 5.1 and kept evolving while keeping backwards compatibility and preserving the original design goals: simplicity, performance, embeddability. 15 | 16 | We’re incredibly grateful for the foundation that Lua has been—it’s been a joy to build on top of! So now we want to give back to the community at large. 17 | 18 | Starting today, [Luau](https://luau-lang.org) is no longer an inseparable part of Roblox platform; it’s a separate, open-source language. 19 | 20 | Luau is available at [https://github.com/Roblox/luau](https://github.com/Roblox/luau) and comes with the source code for the language runtime and all associated tooling: compiler, type checker, linter. The code is available to anyone, free of charge, under the terms of MIT License. We’re happy to accept contributions to the language, whether that’s documentation or source code. 21 | 22 | The language evolution is driven by an RFC process that is also open to the public. 23 | 24 | We are committed to improving Luau going forward—it remains a central piece of technology at Roblox. The team that works on the language keeps growing, and we have lots of ideas! The language will become even faster, even nicer to work with, even more powerful. 25 | 26 | We can’t wait to see what we can build, together. 27 | -------------------------------------------------------------------------------- /_posts/2021-11-29-luau-recap-november-2021.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: single 3 | title: "Luau Recap: November 2021" 4 | --- 5 | 6 | ## Type packs in type aliases 7 | 8 | Type packs are the construct Luau uses to represent a sequence of types. We've had syntax for generic type packs for a while now, and it sees use in generic functions, but it hasn't been available in type aliases. That has changed, and it is now syntactically legal to write the following type alias: 9 | ```lua 10 | type X = () -> A... 11 | type Y = X 12 | ``` 13 | 14 | We've also added support for explicit type packs. Previously, it was impossible to instantiate a generic with two or more type pack parameters, because it wasn't clear where the first pack ended and the second one began. We have introduced a new syntax for this use case: 15 | ``` 16 | type Fn = (P...) -> R... 17 | type X = Fn<(number, string), (string, number)> 18 | ``` 19 | 20 | For more information, check out [the documentation](https://luau-lang.org/typecheck#type-packs) or [the RFC](https://github.com/Roblox/luau/blob/f86d4c6995418e489a55be0100159009492778ff/rfcs/syntax-type-alias-type-packs.md) for this feature. 21 | 22 | ## Luau is open-source! 23 | 24 | We announced this in early November but it deserves repeating: Luau is now an open-source project! You can use Luau outside of Roblox, subject to MIT License, and - importantly - we accept contributions. 25 | 26 | Many changes contributed by community, both Roblox and external, have been merged since we've made Luau open source. Of note are two visible changes that shipped on Roblox platform: 27 | 28 | - The type error "Expected to return X values, but Y values are returned here" actually had X and Y swapped! This is now fixed. 29 | - Luau compiler dutifully computed the length of the string when using `#` operator on a string literal; this is now fixed and `#"foo"` compiles to 3. 30 | 31 | You might think that C++ is a scary language and you can't contribute to Luau. If so, you'd be happy to know that the contents of https://luau-lang.org, where we host our documentation, is also hosted on GitHub in the same repository (https://github.com/Roblox/luau/tree/master/docs) and that we'd love the community to contribute improvements to documentation among other changes! For example see [issues in this list](https://github.com/Roblox/luau/issues?q=is%3Aissue+is%3Aopen+label%3A%22pr+welcome%22) that start with "Documentation", but all other changes and additions to documentation are also welcome. 32 | 33 | ## Library improvements 34 | 35 | ```lua 36 | function bit32.countlz(n: number): number 37 | function bit32.countrz(n: number): number 38 | ``` 39 | Given a number, returns the number of preceding left or trailing right-hand bits that are `0`. 40 | 41 | See [the RFC for these functions](https://github.com/Roblox/luau/blob/f86d4c6995418e489a55be0100159009492778ff/rfcs/function-bit32-countlz-countrz.md) for more information. 42 | 43 | ## Type checking improvements 44 | 45 | We have enabled a rewrite of how Luau handles `require` tracing. This has two main effects: firstly, in strict mode, `require` statements that Luau can't resolve will trigger type errors; secondly, Luau now understands the `FindFirstAncestor` method in `require` expressions. 46 | 47 | Luau now warns when the index to `table.move` is 0, as this is non-idiomatic and performs poorly. If this behavior is intentional, wrap the index in parentheses to suppress the warning. 48 | 49 | Luau now provides additional context in table and class type mismatch errors. 50 | 51 | ## Performance improvements 52 | 53 | We have enabled several changes that aim to avoid allocating a new closure object in cases where it's not necessary to. This is helpful in cases where many closures are being allocated; in our benchmark suite, the two benchmarks that allocate a large number of closures improved by 15% and 5%, respectively. 54 | 55 | When checking union types, we now try possibilities whose synthetic names match. This will speed up type checking unions in cases where synthetic names are populated. 56 | 57 | We have also enabled an optimization that shares state in a hot path on the type checker. This will improve type checking performance. 58 | 59 | The Luau VM now attempts to cache the length of tables' array portion. This change showed a small performance improvement in benchmarks, and should speed up `#` expressions. 60 | 61 | The Luau type checker now caches a specific category of table unification results. This can improve type checking performance significantly when the same set of types is used frequently. 62 | 63 | When Luau is not retaining type graphs, the type checker now discards more of a module's type surface after type checking it. This improves memory usage significantly. 64 | 65 | ## Bug fixes 66 | 67 | We've fixed a bug where on ARM systems (mobile), packing negative numbers using unsigned formats in `string.pack` would produce the wrong result. 68 | 69 | We've fixed an issue with type aliases that reuse generic type names that caused them to be instantiated incorrectly. 70 | 71 | We've corrected a subtle bug that could cause free types to leak into a table type when a free table is bound to that table. 72 | 73 | We've fixed an issue that could cause Luau to report an infinitely recursive type error when the type was not infinitely recursive. 74 | -------------------------------------------------------------------------------- /_posts/2022-01-27-luau-recap-january-2022.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: single 3 | title: "Luau Recap: January 2022" 4 | --- 5 | 6 | ## Performance improvements 7 | 8 | The implementation of `tostring` has been rewritten. This change replaces the default number->string conversion with a 9 | new algorithm called Schubfach, which allows us to produce the shortest precise round-trippable representation of any 10 | input number very quickly. 11 | 12 | While performance is not the main driving factor, this also happens to be significantly faster than our old 13 | implementation (up to 10x depending on the number and the platform). 14 | 15 | --- 16 | 17 | Make `tonumber(x)` ~2x faster by avoiding reparsing string arguments. 18 | 19 | --- 20 | 21 | The Luau compiler now optimizes table literals where keys are constant variables the same way as if they were constants, eg 22 | 23 | ```lua 24 | local r, g, b = 1, 2, 3 25 | local col = { [r] = 255, [g] = 0, [b] = 255 } 26 | ``` 27 | 28 | ## Improvements to type assertions 29 | 30 | The `::` type assertion operator can now be used to coerce a value between any two related types. Previously, it could 31 | only be used for downcasts or casts to `any`. The following used to be invalid, but is now valid: 32 | 33 | ```lua 34 | local t = {x=0, y=0} 35 | local a = t :: {x: number} 36 | ``` 37 | 38 | ## Typechecking improvements 39 | 40 | An issue surrounding table literals and indexers has been fixed: 41 | 42 | ```lua 43 | type RecolorMap = {[string]: RecolorMap | Color3} 44 | 45 | local hatRecolorMap: RecolorMap = { 46 | Brim = Color3.fromRGB(255, 0, 0), -- We used to report an error here 47 | Top = Color3.fromRGB(255, 0, 0) 48 | } 49 | ``` 50 | 51 | --- 52 | Accessing a property whose base expression was previously refined will now return the correct result. 53 | 54 | ## Linter improvements 55 | 56 | `table.create(N, {})` will now produce a static analysis warning since the element is going to be shared for all table entries. 57 | 58 | ## Error reporting improvements 59 | 60 | When a type error involves a union (or an option), we now provide more context in the error message. 61 | 62 | For instance, given the following code: 63 | 64 | ```lua 65 | --!strict 66 | 67 | type T = {x: number} 68 | 69 | local x: T? = {w=4} 70 | ``` 71 | 72 | We now report the following: 73 | 74 | ``` 75 | Type 'x' could not be converted into 'T?' 76 | caused by: 77 | None of the union options are compatible. For example: Table type 'x' not compatible with type 'T' because the former is missing field 'x' 78 | ``` 79 | 80 | --- 81 | Luau now gives up and reports an `*unknown*` type in far fewer cases when typechecking programs that have type errors. 82 | 83 | ## New APIs 84 | 85 | We have brought in the [`coroutine.close`](https://luau-lang.org/library#coroutine-library) function from Lua 5.4. It accepts a suspended coroutine and marks it as non-runnable. In Roblox, this can be useful in combination with `task.defer` to implement cancellation. 86 | 87 | ## REPL improvements 88 | 89 | The `luau` REPL application can be compiled from source or downloaded from [releases page](https://github.com/Roblox/luau/releases). It has grown some new features: 90 | 91 | * Added `--interactive` option to run the REPL after running the last script file. 92 | * Allowed the compiler optimization level to be specified. 93 | * Allowed methods to be tab completed 94 | * Allowed methods on string instances to be completed 95 | * Improved Luau REPL argument parsing and error reporting 96 | * Input history is now saved/loaded 97 | 98 | ## Thanks 99 | 100 | A special thanks to all the fine folks who contributed PRs over the last few months! 101 | 102 | * [Halalaluyafail3](https://github.com/Halalaluyafail3) 103 | * [JohnnyMorganz](https://github.com/JohnnyMorganz) 104 | * [Kampfkarren](https://github.com/Kampfkarren) 105 | * [kunitoki](https://github.com/kunitoki) 106 | * [MathematicalDessert](https://github.com/MathematicalDessert) 107 | * [metatablecat](https://github.com/metatablecat) 108 | * [petrihakkinen](https://github.com/petrihakkinen) 109 | * [rafa_br34](https://github.com/rafa_br34) 110 | * [Rerumu](https://github.com/Rerumu) 111 | * [Slappy826](https://github.com/Slappy826) 112 | * [SnowyShiro](https://github.com/SnowyShiro) 113 | * [vladmarica](https://github.com/vladmarica) 114 | * [xgladius](https://github.com/xgladius) 115 | 116 | [Contribution guide](https://github.com/Roblox/luau/blob/2f989fc049772f36de1a4281834c375858507bda/CONTRIBUTING.md) 117 | -------------------------------------------------------------------------------- /_posts/2022-02-28-luau-recap-february-2022.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: single 3 | title: "Luau Recap: February 2022" 4 | --- 5 | 6 | ## Default type alias type parameters 7 | 8 | We have introduced a syntax to provide default type arguments inside the type alias type parameter list. 9 | 10 | It is now possible to have type functions where the instantiation can omit some type arguments. 11 | 12 | You can provide concrete types: 13 | 14 | ```lua 15 | --!strict 16 | type FieldResolver = (T, Data) -> number 17 | 18 | local a: FieldResolver = ... 19 | local b: FieldResolver = ... 20 | ``` 21 | 22 | Or reference parameters defined earlier in the list: 23 | 24 | ```lua 25 | --!strict 26 | type EqComp = (l: T, r: U) -> boolean 27 | 28 | local a: EqComp = ... -- (l: number, r: number) -> boolean 29 | local b: EqComp = ... -- (l: number, r: string) -> boolean 30 | ``` 31 | 32 | Type pack parameters can also have a default type pack: 33 | 34 | ```lua 35 | --!strict 36 | type Process = (T) -> U... 37 | 38 | local a: Process = ... -- (number) -> ...string 39 | local b: Process = ... -- (number) -> (boolean, string) 40 | ``` 41 | 42 | If all type parameters have a default type, it is now possible to reference that without providing any type arguments: 43 | 44 | ```lua 45 | --!strict 46 | type All = (T) -> U 47 | 48 | local a: All -- ok 49 | local b: All<> -- ok as well 50 | ``` 51 | 52 | For more details, you can read the original [RFC proposal](https://github.com/Roblox/luau/blob/master/rfcs/syntax-default-type-alias-type-parameters.md). 53 | 54 | ## Typechecking improvements 55 | 56 | This month we had many fixes to improve our type inference and reduce false positive errors. 57 | 58 | if-then-else expression can now have different types in each branch: 59 | 60 | ```lua 61 | --!strict 62 | local a = if x then 5 else nil -- 'a' will have type 'number?' 63 | local b = if x then 1 else '2' -- 'b' will have type 'number | string' 64 | ``` 65 | 66 | And if the expected result type is known, you will not get an error in cases like these: 67 | 68 | ```lua 69 | --!strict 70 | type T = {number | string} 71 | -- different array element types don't give an error if that is expected 72 | local c: T = if x then {1, "x", 2, "y"} else {0} 73 | ``` 74 | 75 | --- 76 | 77 | `assert` result is now known to not be 'falsy' (`false` or `nil`): 78 | 79 | ```lua 80 | --!strict 81 | local function f(x: number?): number 82 | return assert(x) -- no longer an error 83 | end 84 | ``` 85 | 86 | --- 87 | 88 | We fixed cases where length operator `#` reported an error when used on a compatible type: 89 | 90 | ```lua 91 | --!strict 92 | local union: {number} | {string} 93 | local a = #union -- no longer an error 94 | ``` 95 | 96 | --- 97 | 98 | Functions with different variadic argument/return types are no longer compatible: 99 | 100 | ```lua 101 | --!strict 102 | local function f(): (number, ...string) 103 | return 2, "a", "b" 104 | end 105 | 106 | local g: () -> (number, ...boolean) = f -- error 107 | ``` 108 | 109 | --- 110 | 111 | We have also fixed: 112 | 113 | * false positive errors caused by incorrect reuse of generic types across different function declarations 114 | * issues with forward-declared intersection types 115 | * wrong return type annotation for table.move 116 | * various crashes reported by developers 117 | 118 | ## Linter improvements 119 | 120 | A new static analysis warning was introduced to mark incorrect use of a '`a and b or c`' pattern. When 'b' is 'falsy' (`false` or `nil`), result will always be 'c', even if the expression 'a' was true: 121 | 122 | ```lua 123 | local function f(x: number) 124 | -- The and-or expression always evaluates to the second alternative because the first alternative is false; consider using if-then-else expression instead 125 | return x < 0.5 and false or 42 126 | end 127 | ``` 128 | 129 | Like we say in the warning, new if-then-else expression doesn't have this pitfall: 130 | 131 | ```lua 132 | local function g(x: number) 133 | return if x < 0.5 then false else 42 134 | end 135 | ``` 136 | 137 | --- 138 | 139 | We have also introduced a check for misspelled comment directives: 140 | 141 | ```lua 142 | --!non-strict 143 | -- ^ Unknown comment directive 'non-strict'; did you mean 'nonstrict'? 144 | ``` 145 | 146 | ## Performance improvements 147 | 148 | For performance, we have changed how our Garbage Collector collects unreachable memory. 149 | This rework makes it possible to free memory 2.5x faster and also comes with a small change to how we store Luau objects in memory. For example, each table now uses 16 fewer bytes on 64-bit platforms. 150 | 151 | Another optimization was made for `select(_, ...)` call. 152 | It is now using a special fast path that has constant-time complexity in number of arguments (~3x faster with 10 arguments). 153 | 154 | ## Thanks 155 | 156 | A special thanks to all the fine folks who contributed PRs this month! 157 | 158 | * [mikejsavage](https://github.com/mikejsavage) 159 | * [TheGreatSageEqualToHeaven](https://github.com/TheGreatSageEqualToHeaven) 160 | * [petrihakkinen](https://github.com/petrihakkinen) 161 | -------------------------------------------------------------------------------- /_posts/2022-03-31-luau-recap-march-2022.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: single 3 | title: "Luau Recap: March 2022" 4 | --- 5 | 6 | ## Singleton types 7 | 8 | We added support for singleton types! These allow you to use string or 9 | boolean literals in types. These types are only inhabited by the 10 | literal, for example if a variable `x` has type `"foo"`, then `x == 11 | "foo"` is guaranteed to be true. 12 | 13 | Singleton types are particularly useful when combined with union types, 14 | for example: 15 | 16 | ```lua 17 | type Animals = "Dog" | "Cat" | "Bird" 18 | ``` 19 | 20 | or: 21 | 22 | ```lua 23 | type Falsey = false | nil 24 | ``` 25 | 26 | In particular, singleton types play well with unions of tables, 27 | allowing tagged unions (also known as discriminated unions): 28 | 29 | ```lua 30 | type Ok = { type: "ok", value: T } 31 | type Err = { type: "error", error: E } 32 | type Result = Ok | Err 33 | 34 | local result: Result = ... 35 | if result.type == "ok" then 36 | -- result :: Ok 37 | print(result.value) 38 | elseif result.type == "error" then 39 | -- result :: Err 40 | error(result.error) 41 | end 42 | ``` 43 | 44 | The RFC for singleton types is https://github.com/Roblox/luau/blob/master/rfcs/syntax-singleton-types.md 45 | 46 | ## Width subtyping 47 | 48 | A common idiom for programming with tables is to provide a public interface type, but to keep some of the concrete implementation private, for example: 49 | 50 | ```lua 51 | type Interface = { 52 | name: string, 53 | } 54 | 55 | type Concrete = { 56 | name: string, 57 | id: number, 58 | } 59 | ``` 60 | 61 | Within a module, a developer might use the concrete type, but export functions using the interface type: 62 | 63 | ```lua 64 | local x: Concrete = { 65 | name = "foo", 66 | id = 123, 67 | } 68 | 69 | local function get(): Interface 70 | return x 71 | end 72 | ``` 73 | 74 | Previously examples like this did not typecheck but now they do! 75 | 76 | This language feature is called *width subtyping* (it allows tables to get *wider*, that is to have more properties). 77 | 78 | The RFC for width subtyping is https://github.com/Roblox/luau/blob/master/rfcs/sealed-table-subtyping.md 79 | 80 | ## Typechecking improvements 81 | 82 | * Generic function type inference now works the same for generic types and generic type packs. 83 | * We improved some error messages. 84 | * There are now fewer crashes (hopefully none!) due to mutating types inside the Luau typechecker. 85 | * We fixed a bug that could cause two incompatible copies of the same class to be created. 86 | * Luau now copes better with cyclic metatable types (it gives a type error rather than hanging). 87 | * Fixed a case where types are not properly bound to all of the subtype when the subtype is a union. 88 | * We fixed a bug that confused union and intersection types of table properties. 89 | * Functions declared as `function f(x : any)` can now be called as `f()` without a type error. 90 | 91 | ## API improvements 92 | 93 | * Implement `table.clone` which takes a table and returns a new table that has the same keys/values/metatable. The cloning is shallow - if some keys refer to tables that need to be cloned, that can be done manually by modifying the resulting table. 94 | 95 | ## Debugger improvements 96 | 97 | * Use the property name as the name of methods in the debugger. 98 | 99 | ## Performance improvements 100 | 101 | * Optimize table rehashing (~15% faster dictionary table resize on average) 102 | * Improve performance of freeing tables (~5% lift on some GC benchmarks) 103 | * Improve gathering performance metrics for GC. 104 | * Reduce stack memory reallocation. 105 | 106 | -------------------------------------------------------------------------------- /_posts/2022-05-02-luau-recap-april-2022.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: single 3 | title: "Luau Recap: April 2022" 4 | --- 5 | 6 | It's been a bit of a quiet month. We mostly have small optimizations and bugfixes for you. 7 | 8 | It is now allowed to define functions on sealed tables that have string indexers. These functions will be typechecked against the indexer type. For example, the following is now valid: 9 | 10 | ```lua 11 | local a : {[string]: () -> number} = {} 12 | 13 | function a.y() return 4 end -- OK 14 | ``` 15 | 16 | Autocomplete will now provide string literal suggestions for singleton types. eg 17 | 18 | ```lua 19 | local function f(x: "a" | "b") end 20 | f("_") -- suggest "a" and "b" 21 | ``` 22 | 23 | Improve error recovery in the case where we encounter a type pack variable in a place where one is not allowed. eg `type Foo = { value: A... }` 24 | 25 | When code does not pass enough arguments to a variadic function, the error feedback is now better. 26 | 27 | For example, the following script now produces a much nicer error message: 28 | ```lua 29 | type A = { [number]: number } 30 | type B = { [number]: string } 31 | 32 | local a: A = { 1, 2, 3 } 33 | 34 | -- ERROR: Type 'A' could not be converted into 'B' 35 | -- caused by: 36 | -- Property '[indexer value]' is not compatible. Type 'number' could not be converted into 'string' 37 | local b: B = a 38 | ``` 39 | 40 | If the following code were to error because `Hello` was undefined, we would erroneously include the comment in the span of the error. This is now fixed. 41 | ```lua 42 | type Foo = Hello -- some comment over here 43 | ``` 44 | 45 | Fix a crash that could occur when strict scripts have cyclic require() dependencies. 46 | 47 | Add an option to autocomplete to cause it to abort processing after a certain amount of time has elapsed. 48 | -------------------------------------------------------------------------------- /_posts/2022-06-01-luau-recap-may-2022.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: single 3 | title: "Luau Recap: May 2022" 4 | --- 5 | 6 | This month Luau team has worked to bring you a new language feature together with more typechecking improvements and bugfixes! 7 | 8 | ## Generalized iteration 9 | 10 | We have extended the semantics of standard Lua syntax for iterating through containers, `for vars in values` with support for generalized iteration. 11 | In Lua, to iterate over a table you need to use an iterator like `next` or a function that returns one like `pairs` or `ipairs`. In Luau, you can now simply iterate over a table: 12 | 13 | ```lua 14 | for k, v in {1, 4, 9} do 15 | assert(k * k == v) 16 | end 17 | ``` 18 | 19 | This works for tables but can also be customized for tables or userdata by implementing `__iter` metamethod. It is called before the iteration begins, and should return an iterator function like `next` (or a custom one): 20 | 21 | ```lua 22 | local obj = { items = {1, 4, 9} } 23 | setmetatable(obj, { __iter = function(o) return next, o.items end }) 24 | 25 | for k, v in obj do 26 | assert(k * k == v) 27 | end 28 | ``` 29 | 30 | The default iteration order for tables is specified to be consecutive for elements `1..#t` and unordered after that, visiting every element. 31 | Similar to iteration using `pairs`, modifying the table entries for keys other than the current one results in unspecified behavior. 32 | 33 | ## Typechecking improvements 34 | 35 | We have added a missing check to compare implicit table keys against the key type of the table indexer: 36 | 37 | ```lua 38 | -- error is correctly reported, implicit keys (1,2,3) are not compatible with [string] 39 | local t: { [string]: boolean } = { true, true, false } 40 | ``` 41 | 42 | Rules for `==` and `~=` have been relaxed for union types, if any of the union parts can be compared, operation succeeds: 43 | 44 | ```lua 45 | --!strict 46 | local function compare(v1: Vector3, v2: Vector3?) 47 | return v1 == v2 -- no longer an error 48 | end 49 | ``` 50 | 51 | Table value type propagation now correctly works with `[any]` key type: 52 | 53 | ```lua 54 | --!strict 55 | type X = {[any]: string | boolean} 56 | local x: X = { key = "str" } -- no longer gives an incorrect error 57 | ``` 58 | 59 | If a generic function doesn't provide type annotations for all arguments and the return value, additional generic type parameters might be added automatically: 60 | 61 | ```lua 62 | -- previously it was foo, now it's foo, because second argument is also generic 63 | function foo(x: T, y) end 64 | ``` 65 | 66 | We have also fixed various issues that have caused crashes, with many of them coming from your bug reports. 67 | 68 | ## Linter improvements 69 | 70 | `GlobalUsedAsLocal` lint warning has been extended to notice when global variable writes always happen before their use in a local scope, suggesting that they can be replaced with a local variable: 71 | 72 | ```lua 73 | function bar() 74 | foo = 6 -- Global 'foo' is never read before being written. Consider changing it to local 75 | return foo 76 | end 77 | function baz() 78 | foo = 10 79 | return foo 80 | end 81 | ``` 82 | 83 | ## Performance improvements 84 | 85 | Garbage collection CPU utilization has been tuned to further reduce frame time spikes of individual collection steps and to bring different GC stages to the same level of CPU utilization. 86 | 87 | Returning a type-cast local (`return a :: type`) as well as returning multiple local variables (`return a, b, c`) is now a little bit more efficient. 88 | 89 | ### Function inlining and loop unrolling 90 | 91 | In the open-source release of Luau, when optimization level 2 is enabled, the compiler will now perform function inlining and loop unrolling. 92 | 93 | Only loops with loop bounds known at compile time, such as `for i=1,4 do`, can be unrolled. The loop body must be simple enough for the optimization to be profitable; compiler uses heuristics to estimate the performance benefit and automatically decide if unrolling should be performed. 94 | 95 | Only local functions (defined either as `local function foo` or `local foo = function`) can be inlined. The function body must be simple enough for the optimization to be profitable; compiler uses heuristics to estimate the performance benefit and automatically decide if each call to the function should be inlined instead. Additionally recursive invocations of a function can't be inlined at this time, and inlining is completely disabled for modules that use `getfenv`/`setfenv` functions. 96 | -------------------------------------------------------------------------------- /_posts/2022-07-07-luau-recap-june-2022.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: single 3 | title: "Luau Recap: June 2022" 4 | --- 5 | 6 | # Lower bounds calculation 7 | 8 | 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. 9 | 10 | There are some important cases where this doesn't produce a helpful result. Take this function for instance: 11 | 12 | ```lua 13 | function find_first_if(vec, f) 14 | for i, e in ipairs(vec) do 15 | if f(e) then 16 | return i 17 | end 18 | end 19 | 20 | return nil 21 | end 22 | ``` 23 | 24 | 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`. 25 | 26 | 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. 27 | 28 | 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`." 29 | 30 | Lower bounds calculation does precisely this. Moving forward, Luau will instead infer the type `number?` for the above function. 31 | 32 | 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. 33 | 34 | 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." 35 | 36 | Please try it out and let us know what you think! 37 | 38 | ## Known bug 39 | 40 | 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. 41 | 42 | ```lua 43 | type T = {T?}? -- spuriously reduces to {nil}? 44 | ``` 45 | 46 | We hope to have this fixed soon. 47 | 48 | # All table literals now result in unsealed tables 49 | 50 | 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: 51 | 52 | ```lua 53 | local T = {} 54 | T.x = 5 -- OK 55 | 56 | local V = {x=5} 57 | V.y = 2 -- previously disallowed. Now OK. 58 | 59 | function mkTable() 60 | return {x = 5} 61 | end 62 | 63 | local U = mkTable() 64 | U.y = 2 -- Still disallowed: U is sealed 65 | ``` 66 | 67 | # Other fixes 68 | 69 | * Adjust indentation and whitespace when creating multiline string representations of types, resulting in types that are easier to read. 70 | * Some small bugfixes to autocomplete 71 | * Fix a case where accessing a nonexistent property of a table would not result in an error being reported. 72 | * Improve parser recovery for the incorrect code `function foo() -> ReturnType` (the correct syntax is `function foo(): ReturnType`) 73 | * Improve the parse error offered for code that improperly uses the `function` keyword to start a type eg `type T = function` 74 | * Some small crash fixes and performance improvements 75 | 76 | # Thanks! 77 | 78 | A very special thanks to all of our open source contributors: 79 | 80 | * [Allan N Jeremy](https://github.com/AllanJeremy) 81 | * [Daniel Nachun](https://github.com/danielnachun) 82 | * [JohnnyMorganz](https://github.com/JohnnyMorganz/) 83 | * [Petri Häkkinen](https://github.com/petrihakkinen) 84 | * [Qualadore](https://github.com/Qualadore) 85 | -------------------------------------------------------------------------------- /_posts/2022-08-29-luau-recap-august-2022.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: single 3 | title: "Luau Recap: July & August 2022" 4 | --- 5 | 6 | [Cross-posted to the [Roblox Developer Forum](https://devforum.roblox.com/t/luau-recap-july-august-2022/).] 7 | 8 | ## Tables now support `__len` metamethod 9 | 10 | See the RFC [Support `__len` metamethod for tables and `rawlen` function](https://github.com/Roblox/luau/blob/master/rfcs/len-metamethod-rawlen.md) for more details. 11 | 12 | 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`. 13 | 14 | Simply, tables now honor the `__len` metamethod, and `rawlen` is also added with similar semantics as `rawget` and `rawset`: 15 | 16 | ```lua 17 | local my_cool_container = setmetatable({ items = { 1, 2 } }, { 18 | __len = function(self) return #self.items end 19 | }) 20 | 21 | print(#my_cool_container) --> 2 22 | print(rawlen(my_cool_container)) --> 0 23 | ``` 24 | 25 | ## `never` and `unknown` types 26 | 27 | See the RFC [`never` and `unknown` types](https://github.com/Roblox/luau/blob/master/rfcs/never-and-unknown-types.md) for more details. 28 | 29 | 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`. 30 | 31 | 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. 32 | 33 | ```lua 34 | function f(x: string | number) 35 | if typeof(x) == "string" and typeof(x) == "number" then 36 | -- x: never 37 | end 38 | end 39 | ``` 40 | 41 | 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`. 42 | 43 | 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. 44 | 45 | ```lua 46 | function any(): any return 5 end 47 | function unknown(): unknown return 5 end 48 | 49 | -- no type error, but assigns a number to x which expects string 50 | local x: string = any() 51 | 52 | -- has type error, unknown cannot be converted into string 53 | local y: string = unknown() 54 | ``` 55 | 56 | To be able to do this soundly, you must apply type refinements on a variable of type `unknown`. 57 | 58 | ```lua 59 | local u = unknown() 60 | 61 | if typeof(u) == "string" then 62 | local y: string = u -- no type error 63 | end 64 | ``` 65 | 66 | 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. 67 | 68 | ## Argument names in type packs when instantiating a type 69 | 70 | 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 below syntax did not generate a parse error when it should have. 71 | 72 | ```lua 73 | Foo<(a: number, b: string)> 74 | ``` 75 | 76 | ## New IntegerParsing lint 77 | 78 | See [the announcement](https://devforum.roblox.com/t/improving-binary-and-hexadecimal-integer-literal-parsing-rules-in-luau/) for more details. We include this here for posterity. 79 | 80 | We've introduced a new lint called IntegerParsing. Right now, it lints three classes of errors: 81 | 82 | 1. Truncation of binary literals that resolves to a value over 64 bits, 83 | 2. Truncation of hexadecimal literals that resolves to a value over 64 bits, and 84 | 3. Double hexadecimal prefix. 85 | 86 | For 1.) and 2.), they are currently not planned to become a parse error, so action is not strictly required here. 87 | 88 | For 3.), this will be a breaking change! See [the rollout plan](https://devforum.roblox.com/t/improving-binary-and-hexadecimal-integer-literal-parsing-rules-in-luau/#rollout-5) for details. 89 | 90 | ## New ComparisonPrecedence lint 91 | 92 | We've also introduced a new lint called `ComparisonPrecedence`. It fires in two particular cases: 93 | 94 | 1. `not X op Y` where `op` is `==` or `~=`, or 95 | 2. `X op Y op Z` where `op` is any of the comparison or equality operators. 96 | 97 | 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`. 98 | 99 | ```lua 100 | -- not X == Y is equivalent to (not X) == Y; consider using X ~= Y, or wrap one of the expressions in parentheses to silence 101 | if not x == y then end 102 | 103 | -- not X ~= Y is equivalent to (not X) ~= Y; consider using X == Y, or wrap one of the expressions in parentheses to silence 104 | if not x ~= y then end 105 | 106 | -- not X <= Y is equivalent to (not X) <= Y; wrap one of the expressions in parentheses to silence 107 | if not x <= y then end 108 | 109 | -- X <= Y == Z is equivalent to (X <= Y) == Z; wrap one of the expressions in parentheses to silence 110 | if x <= y == 0 then end 111 | ``` 112 | 113 | 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. 114 | 115 | ## Function calls returning singleton types incorrectly widened 116 | 117 | 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. 118 | 119 | ```lua 120 | function f(): "abc" | "def" 121 | return if math.random() > 0.5 then "abc" else "def" 122 | end 123 | 124 | -- previously reported that 'string' could not be converted into '"abc" | "def"' 125 | local x: "abc" | "def" = f() 126 | ``` 127 | 128 | ## `string` can be a subtype of a table with a shape similar to `string` 129 | 130 | The function `my_cool_lower` is a function `(t: t1) -> a... where t1 = {+ lower: (t1) -> a... +}`. 131 | 132 | ```lua 133 | function my_cool_lower(t) 134 | return t:lower() 135 | end 136 | ``` 137 | 138 | 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. 139 | 140 | ```lua 141 | local s: string = my_cool_lower("HI") 142 | ``` 143 | 144 | ## Other analysis improvements 145 | 146 | * `string.gmatch`/`string.match`/`string.find` may now return more precise type depending on the patterns used 147 | * Fix a bug where type arena ownership invariant could be violated, causing stability issues 148 | * Fix a bug where internal type error could be presented to the user 149 | * Fix a false positive with optionals & nested tables 150 | * Fix a false positive in non-strict mode when using generalized iteration 151 | * Improve autocomplete behavior in certain cases for `:` calls 152 | * Fix minor inconsistencies in synthesized names for types with metatables 153 | * Fix autocomplete not suggesting globals defined after the cursor 154 | * Fix DeprecatedGlobal warning text in cases when the global is deprecated without a suggested alternative 155 | * Fix an off-by-one error in type error text for incorrect use of `string.format` 156 | 157 | ## Other runtime improvements 158 | 159 | * Comparisons with constants are now significantly faster when using clang as a compiler (10-50% gains on internal benchmarks) 160 | * When calling non-existent methods on tables or strings, `foo:bar` now produces a more precise error message 161 | * Improve performance for iteration of tables 162 | * Fix a bug with negative zero in vector components when using vectors as table keys 163 | * Compiler can now constant fold builtins under -O2, for example `string.byte("A")` is compiled to a constant 164 | * Compiler can model the cost of builtins for the purpose of inlining/unrolling 165 | * Local reassignment i.e. `local x = y :: T` is free iff neither `x` nor `y` is mutated/captured 166 | * Improve `debug.traceback` performance by 1.15-1.75x depending on the platform 167 | * Fix a corner case with table assignment semantics when key didn't exist in the table and `__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 tables 168 | * Reduce parser C stack consumption which fixes some stack overflow crashes on deeply nested sources 169 | * Improve performance of `bit32.extract`/`replace` when width is implied (~3% faster chess) 170 | * Improve performance of `bit32.extract` when field/width are constants (~10% faster base64) 171 | * `string.format` now supports a new format specifier, `%*`, that accepts any value type and formats it using `tostring` rules 172 | 173 | ## Thanks 174 | 175 | Thanks for all the contributions! 176 | 177 | * [natteko](https://github.com/natteko) 178 | * [JohnnyMorganz](https://github.com/JohnnyMorganz) 179 | * [khvzak](https://github.com/khvzak) 180 | * [Anaminus](https://github.com/Anaminus) 181 | * [memery-rbx](https://github.com/memery-rbx) 182 | * [jaykru](https://github.com/jaykru) 183 | * [Kampfkarren](https://github.com/Kampfkarren) 184 | * [XmiliaH](https://github.com/XmiliaH) 185 | * [Mactavsin](https://github.com/Mactavsin) 186 | -------------------------------------------------------------------------------- /_posts/2022-11-01-luau-recap-september-october-2022.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: single 3 | title: "Luau Recap: September & October 2022" 4 | --- 5 | 6 | ## Semantic subtyping 7 | 8 | One of the most important goals for Luau is to avoid *false 9 | positives*, that is cases where Script Analysis reports a type error, 10 | but in fact the code is correct. This is very frustrating, especially 11 | for beginners. Spending time chasing down a gnarly type error only to 12 | discover that it was the type system that's wrong is nobody's idea of fun! 13 | 14 | We are pleased to announce that a major component of minimizing false 15 | positives has landed, *semantic subtyping*, which removes a class of false positives caused 16 | by failures of subtyping. For example, in the program 17 | 18 | ```lua 19 | local x : CFrame = CFrame.new() 20 | local y : Vector3 | CFrame 21 | if (math.random()) then 22 | y = CFrame.new() 23 | else 24 | y = Vector3.new() 25 | end 26 | local z : Vector3 | CFrame = x * y -- Type Error! 27 | ``` 28 | 29 | an error is reported, even though there is no problem at runtime. This 30 | is because `CFrame`'s multiplication has two overloads: 31 | 32 | ```lua 33 | ((CFrame, CFrame) -> CFrame) 34 | & ((CFrame, Vector3) -> Vector3) 35 | ``` 36 | 37 | The current syntax-driven algorithm for subtyping is not sophisticated 38 | enough to realize that this is a subtype of the desired type: 39 | 40 | ```lua 41 | (CFrame, Vector3 | CFrame) -> (Vector3 | CFrame) 42 | ``` 43 | 44 | Our new algorithm is driven by the semantics of subtyping, not the syntax of types, 45 | and eliminates this class of false positives. 46 | 47 | If you want to know more about semantic subtyping in Luau, check out our 48 | [technical blog post](https://luau-lang.org/2022/10/31/luau-semantic-subtyping.html) 49 | on the subject. 50 | 51 | ## Other analysis improvements 52 | 53 | * Improve stringification of function types. 54 | * Improve parse error warnings in the case of missing tokens after a comma. 55 | * Improve typechecking of expressions involving variadics such as `{ ... }`. 56 | * Make sure modules don't return unbound generic types. 57 | * Improve cycle detection in stringifying types. 58 | * Improve type inference of combinations of intersections and generic functions. 59 | * Improve typechecking when calling a function which returns a variadic e.g. `() -> (number...)`. 60 | * Improve typechecking when passing a function expression as a parameter to a function. 61 | * Improve error reporting locations. 62 | * Remove some sources of memory corruption and crashes. 63 | 64 | ## Other runtime and debugger improvements 65 | 66 | * Improve performance of accessing debug info. 67 | * Improve performance of `getmetatable` and `setmetatable`. 68 | * Remove a source of freezes in the debugger. 69 | * Improve GC accuracy and performance. 70 | 71 | ## Thanks 72 | 73 | Thanks for all the contributions! 74 | 75 | * [AllanJeremy](https://github.com/AllanJeremy) 76 | * [JohnnyMorganz](https://github.com/JohnnyMorganz) 77 | * [jujhar16](https://github.com/jujhar16) 78 | * [petrihakkinen](https://github.com/petrihakkinen) 79 | -------------------------------------------------------------------------------- /_posts/2022-11-30-luau-recap-november-2022.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: single 3 | title: "Luau Recap: November 2022" 4 | --- 5 | 6 | While the team is busy to bring some bigger things in the future, we have made some small improvements this month. 7 | 8 | ## Analysis improvements 9 | 10 | We have improved tagged union type refinements to only include unhandled type cases in the `else` branch of the `if` statement: 11 | 12 | ```lua 13 | type Ok = { tag: "ok", value: T } 14 | type Err = { tag: "error", msg: string } 15 | type Result = Ok | Err 16 | 17 | function unwrap(r: Result): T? 18 | if r.tag == "ok" then 19 | return r.value 20 | else 21 | -- Luau now understands that 'r' here can only be the 'Err' part 22 | print(r.msg) 23 | return nil 24 | end 25 | end 26 | ``` 27 | 28 | For better inference, we updated the definition of `Enum.SomeType:GetEnumItems()` to return `{Enum.SomeType}` instead of common `{EnumItem}` and the return type of `next` function now includes the possibility of key being `nil`. 29 | 30 | Finally, if you use `and` operator on non-boolean values, `boolean` type will no longer be added by the type inference: 31 | 32 | ```lua 33 | local function f1(a: number?) 34 | -- 'x' is still a 'number?' and doesn't become 'boolean | number' 35 | local x = a and 5 36 | end 37 | ``` 38 | 39 | ## Error message improvements 40 | 41 | We now give an error when built-in types are being redefined: 42 | 43 | ```lua 44 | type string = number -- Now an error: Redefinition of type 'string' 45 | ``` 46 | 47 | We also had a parse error missing in case you forgot your default type pack parameter value. We accepted the following code silently without raising an issue: 48 | 49 | ```lua 50 | type Foo = nil -- Now an error: Expected type, got '>' 51 | ``` 52 | 53 | Error about function argument count mismatch no longer points at the last argument, but instead at the function in question. 54 | So, instead of: 55 | 56 | ```lua 57 | function myfunction(a: number, b:number) end 58 | myfunction(123) 59 | ~~~ 60 | ``` 61 | 62 | We now highlight this: 63 | 64 | ```lua 65 | function myfunction(a: number, b:number) end 66 | myfunction(123) 67 | ~~~~~~~~~~ 68 | ``` 69 | 70 | If you iterate over a table value that could also be `nil`, you get a better explanation in the error message: 71 | 72 | ```lua 73 | local function f(t: {number}?) 74 | for i,v in t do -- Value of type {number}? could be nil 75 | --... 76 | end 77 | end 78 | ``` 79 | Previously it was `Cannot call non-function {number}?` which was confusing. 80 | 81 | And speaking of confusing, some of you might have seen an error like `Type 'string' could not be converted into 'string'`. 82 | 83 | This was caused by Luau having both a primitive type `string` and a table type coming from `string` library. Since the way you can get the type of the `string` library table is by using `typeof(string)`, the updated error message will mirror that and report `Type 'string' could not be converted into 'typeof(string)'`. 84 | 85 | 86 | Parsing now recovers with a more precise error message if you forget a comma in table constructor spanning multiple lines: 87 | 88 | ```lua 89 | local t = { 90 | a = 1 91 | b = 2 -- Expected ',' after table constructor element 92 | c = 3 -- Expected ',' after table constructor element 93 | } 94 | ``` 95 | -------------------------------------------------------------------------------- /_posts/2023-02-02-luau-string-interpolation.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: single 3 | title: "String Interpolation" 4 | --- 5 | 6 | String interpolation is the new syntax introduced to Luau that allows you to create a string literal with expressions inside of that string literal. 7 | 8 | In short, it's a safer and more ergonomic alternative over `string.format`. 9 | 10 | Here's a quick example of a string interpolation: 11 | 12 | ```lua 13 | local combos = {2, 7, 1, 8, 5} 14 | print(`The lock combination is {table.concat(combos)}. Again, {table.concat(combos, ", ")}.`) 15 | --> The lock combination is 27185. Again, 2, 7, 1, 8, 5. 16 | ``` 17 | 18 | String interpolation also composes well with the `__tostring` metamethod. 19 | 20 | ```lua 21 | local balance = setmetatable({ value = 500 }, { 22 | __tostring = function(self) 23 | return "$" .. tostring(self.value) 24 | end 25 | }) 26 | 27 | print(`You have {balance}!`) 28 | --> You have $500! 29 | ``` 30 | 31 | To find out more details about this feature, check out [Luau Syntax page](/syntax#string-interpolation). 32 | 33 | This is also the first major language feature implemented in a [contribution](https://github.com/Roblox/luau/pull/614) from the open-source community. Thanks [Kampfkarren](https://github.com/Kampfkarren)! 34 | -------------------------------------------------------------------------------- /_posts/2023-03-31-luau-recap-march-2023.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: single 3 | title: "Luau Recap: March 2023" 4 | --- 5 | 6 | How the time flies! The team has been busy since the last November Luau Recap working on some large updates that are coming in the future, but before those arrive, we have some improvements that you can already use! 7 | 8 | ## Improved type refinements 9 | 10 | Type refinements handle constraints placed on variables inside conditional blocks. 11 | 12 | In the following example, while variable `a` is declared to have type `number?`, inside the `if` block we know that it cannot be `nil`: 13 | 14 | ```lua 15 | local function f(a: number?) 16 | if a ~= nil then 17 | a *= 2 -- no type errors 18 | end 19 | ... 20 | end 21 | ``` 22 | 23 | One limitation we had previously is that after a conditional block, refinements were discarded. 24 | 25 | But there are cases where `if` is used to exit the function early, making the following code essentially act as a hidden `else` block. 26 | 27 | We now correctly preserve such refinements and you should be able to remove `assert` function calls that were only used to get rid of false positive errors about types being `nil`. 28 | 29 | ```lua 30 | local function f(x: string?) 31 | if not x then return end 32 | 33 | -- x is a 'string' here 34 | end 35 | ``` 36 | 37 | Throwing calls like `error()` or `assert(false)` instead of a `return` statement are also recognized. 38 | 39 | ```lua 40 | local function f(x: string?) 41 | if not x then error('first argument is nil') end 42 | 43 | -- x is 'string' here 44 | end 45 | ``` 46 | 47 | Existing complex refinements like `type`/`typeof`, tagged union checks and other are expected to work as expected. 48 | 49 | ## Marking table.getn/foreach/foreachi as deprecated 50 | 51 | `table.getn`, `table.foreach` and `table.foreachi` were deprecated in Lua 5.1 that Luau is based on, and removed in Lua 5.2. 52 | 53 | `table.getn(x)` is equivalent to `rawlen(x)` when 'x' is a table; when 'x' is not a table, `table.getn` produces an error. 54 | 55 | It's difficult to imagine code where `table.getn(x)` is better than either `#x` (idiomatic) or `rawlen(x)` (fully compatible replacement). 56 | 57 | `table.getn` is also slower than both alternatives and was marked as deprecated. 58 | 59 | `table.foreach` is equivalent to a `for .. pairs` loop; `table.foreachi` is equivalent to a `for .. ipairs` loop; both may also be replaced by generalized iteration. 60 | 61 | Both functions are significantly slower than equivalent for loop replacements, are more restrictive because the function can't yield. 62 | 63 | Because both functions bring no value over other library or language alternatives, they were marked deprecated as well. 64 | 65 | You may have noticed linter warnings about places where these functions are used. For compatibility, these functions are not going to be removed. 66 | 67 | ## Autocomplete improvements 68 | 69 | When table key type is defined to be a union of string singletons, those keys can now autocomplete in locations marked as '^': 70 | 71 | ```lua 72 | type Direction = "north" | "south" | "east" | "west" 73 | 74 | local a: {[Direction]: boolean} = {[^] = true} 75 | local b: {[Direction]: boolean} = {["^"]} 76 | local b: {[Direction]: boolean} = {^} 77 | ``` 78 | 79 | We also fixed incorrect and incomplete suggestions inside the header of `if`, `for` and `while` statements. 80 | 81 | ## Runtime improvements 82 | 83 | On the runtime side, we added multiple optimizations. 84 | 85 | `table.sort` is now ~4.1x faster (when not using a predicate) and ~2.1x faster when using a simple predicate. 86 | 87 | We also have ideas on how improve the sorting performance in the future. 88 | 89 | `math.floor`, `math.ceil` and `math.round` now use specialized processor instructions. We have measured ~7-9% speedup in math benchmarks that heavily used those functions. 90 | 91 | A small improvement was made to builtin library function calls, getting a 1-2% improvement in code that contains a lot of fastcalls. 92 | 93 | Finally, a fix was made to table array part resizing that brings large improvement to performance of large tables filled as an array, but at an offset (for example, starting at 10000 instead of 1). 94 | 95 | Aside from performance, a correctness issue was fixed in multi-assignment expressions. 96 | 97 | ```lua 98 | arr[1], n = n, n - 1 99 | ``` 100 | 101 | In this example, `n - 1` was assigned to `n` before `n` was assigned to `arr[1]`. This issue has now been fixed. 102 | 103 | ## Analysis improvements 104 | 105 | Multiple changes were made to improve error messages and type presentation. 106 | 107 | * Table type strings are now shown with newlines, to make them easier to read 108 | * Fixed unions of `nil` types displaying as a single `?` character 109 | * "Type pack A cannot be converted to B" error is not reported instead of a cryptic "Failed to unify type packs" 110 | * Improved error message for value count mismatch in assignments like `local a, b = 2` 111 | 112 | You may have seen error messages like `Type 'string' cannot be converted to 'string?'` even though usually it is valid to assign `local s: string? = 'hello'` because `string` is a sub-type of `string?`. 113 | 114 | This is true in what is called Covariant use contexts, but doesn't hold in Invariant use contexts, like in the example below: 115 | 116 | ```lua 117 | local a: { x: Model } 118 | local b: { x: Instance } = a -- Type 'Model' could not be converted into 'Instance' in an invariant context 119 | ``` 120 | 121 | In this example, while `Model` is a sub-type of `Instance` and can be used where `Instance` is required. 122 | 123 | The same is not true for a table field because when using table `b`, `b.x` can be assigned an `Instance` that is not a `Model`. When `b` is an alias to `a`, this assignment is not compatible with `a`'s type annotation. 124 | 125 | --- 126 | 127 | Some other light changes to type inference include: 128 | 129 | * `string.match` and `string.gmatch` are now defined to return optional values as match is not guaranteed at runtime 130 | * Added an error when unrelated types are compared with `==`/`~=` 131 | * Fixed issues where variable after `typeof(x) == 'table'` could not have been used as a table 132 | 133 | ## Thanks 134 | 135 | A very special thanks to all of our open source contributors: 136 | 137 | * [niansa/tuxifan](https://github.com/niansa) 138 | * [B. Gibbons](https://github.com/bmg817) 139 | * [Epix](https://github.com/EpixScripts) 140 | * [Harold Cindy](https://github.com/HaroldCindy) 141 | * [Qualadore](https://github.com/Qualadore) 142 | -------------------------------------------------------------------------------- /_posts/2023-07-28-luau-recap-july-2023.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: single 3 | title: "Luau Recap: July 2023" 4 | --- 5 | 6 | Our team is still spending a lot of time working on upcoming replacement for our type inference engine as well as working on native code generation to improve runtime performance. 7 | 8 | However, we also worked on unrelated improvements during this time that are summarized here. 9 | 10 | ## Analysis improvements 11 | 12 | Indexing table intersections using `x["prop"]` syntax has been fixed and no longer reports a false positive error: 13 | 14 | ```lua 15 | type T = { foo: string } & { bar: number } 16 | local x: T = { foo = "1", bar = 2 } 17 | 18 | local y = x["bar"] -- This is no longer an error 19 | ``` 20 | 21 | Generic `T...` type is now convertible to `...any` variadic parameter. 22 | 23 | This solves issues people had with variadic functions and variadic argument: 24 | 25 | ```lua 26 | local function foo(...: any) 27 | print(...) 28 | end 29 | 30 | local function bar(...: T...) 31 | foo(...) -- This is no longer an error 32 | end 33 | ``` 34 | 35 | We have also improved our general typechecking performance by ~17% and by additional ~30% in modules with complex types. 36 | 37 | Other fixes include: 38 | 39 | * Fixed issue with type `T?` not being convertible to `T | T` or `T?` which could've generated confusing errors 40 | * Return type of `os.date` is now inferred as `DateTypeResult` when argument is "*t" or "!*t" 41 | 42 | ## Runtime improvements 43 | 44 | Out-of-memory exception handling has been improved. 45 | `xpcall` handlers will now actually be called with a "not enough memory" string and whatever string/object they return will be correctly propagated. 46 | 47 | Other runtime improvements we've made: 48 | 49 | * Performance of `table.sort` was improved further. It now guarantees N*log(N) time complexity in the worst case 50 | * Performance of `table.concat` was improved by ~5-7% 51 | * Performance of `math.noise` was improved by ~30% 52 | * Inlining of functions is now possible even when they used to compute their own arguments 53 | * Improved logic for determining whether inlining a function or unrolling a loop is profitable 54 | 55 | ## Autocomplete improvements 56 | 57 | An issue with exported types not being suggested is now fixed. 58 | 59 | ## Debugger improvements 60 | 61 | We have fixed the search for the closest executable breakpoint line. 62 | 63 | Previously, breakpoints might have been skipped in `else` blocks at the end of a function. 64 | This simplified example shows the issue: 65 | 66 | ```lua 67 | local function foo(isIt) 68 | if isIt then 69 | print("yes") 70 | else 71 | -- When 'true' block exits the function, breakpoint couldn't be placed here 72 | print("no") 73 | end 74 | end 75 | ``` 76 | 77 | ## Thanks 78 | 79 | A very special thanks to all of our open source contributors: 80 | 81 | * [Petri Häkkinen](https://github.com/petrihakkinen) 82 | * [JohnnyMorganz](https://github.com/JohnnyMorganz) 83 | * [Gael](https://github.com/TheGreatSageEqualToHeaven) 84 | * [Jan](https://github.com/Jan200101) 85 | * [Alex Orlenko](https://github.com/khvzak) 86 | * [mundusnine](https://github.com/mundusnine) 87 | * [Ben Mactavsin](https://github.com/BenMactavsin) 88 | * [RadiatedExodus](https://github.com/RealEthanPlayzDev) 89 | * [Lodinu Kalugalage](https://github.com/imlodinu) 90 | * [MagelessMayhem](https://github.com/MagelessMayhem) 91 | * [Someon1e](https://github.com/Someon1e) 92 | -------------------------------------------------------------------------------- /_posts/2023-11-01-luau-recap-october-2023.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: single 3 | title: "Luau Recap: October 2023" 4 | --- 5 | 6 | We're still quite busy working on some big type checking updates that we hope to talk about soon, but we have a few equally exciting updates to share in the meantime! 7 | 8 | Let's dive in! 9 | 10 | ## Floor Division 11 | 12 | Luau now has a floor division operator. It is spelled `//`: 13 | 14 | ```lua 15 | local a = 10 // 3 -- a == 3 16 | a //= 2 -- a == 1 17 | ``` 18 | 19 | For numbers, `a // b` is equivalent to `math.floor(a / b)`, and you can also overload this operator by implementing the `__idiv` metamethod. The syntax and semantics are borrowed from Lua 5.3 (although Lua 5.3 has an integer type while we don't, we tried to match the behavior to be as close as possible). 20 | 21 | ## Native Codegen Preview 22 | 23 | We are actively working on our new native code generation module that can significantly improve the performance of compute-dense scripts by compiling them to X64 (Intel/AMD) or A64 (ARM) machine code and executing that natively. We aim to support all AArch64 hardware with the current focus being Apple Silicon (M1-M3) chips, and all Intel/AMD hardware that supports AVX1 (with no planned support for earlier systems). When the hardware does not support native code generation, any code that would be compiled as native just falls back to the interpreted execution. 24 | 25 | When working with [open-source releases](https://github.com/luau-lang/luau/releases), binaries now have native code generation support compiled in by default; you need to pass `--codegen` command line flag to enable it. If you use Luau as a library in a third-party application, you would need to manually link `Luau.CodeGen` library and call the necessary functions to compile specific modules as needed - or keep using the interpreter if you want to! If you work in Roblox Studio, we have integrated native code generation preview [as a beta feature](https://devforum.roblox.com/t/luau-native-code-generation-preview-studio-beta/2572587), which currently requires manual annotation of select scripts with `--!native` comment. 26 | 27 | Our goal for the native code generation is to help reach ultimate performance for code that needs to process data very efficiently, but not necessarily to accelerate every line of code, and not to replace the interpreter. We remain committed to maximizing interpreted execution performance, as not all platforms will support native code generation, and it's not always practical to use native code generation for large code bases because it has a larger memory impact than bytecode. We intend for this to unlock new performance opportunities for complex features and algorithms, e.g. code that spends a lot of time working with numbers and arrays, but not to dramatically change performance on UI code or code that spends a lot of its time calling Lua functions like `table.sort`, or external C functions (like Roblox engine APIs). 28 | 29 | Importantly, native code generation does not change our behavior or correctness expectations. Code compiled natively should give the same results when it executes as non-native code (just take a little less time), and it should not result in any memory safety or sandboxing issues. If you ever notice native code giving a different result from non-native code, please submit a bug report. 30 | 31 | We continue to work on many code size and performance improvements; here's a short summary of what we've done in the last couple of months, and there's more to come! 32 | 33 | - Repeated access to table fields with the same object and name are now optimized (e.g. `t.x = t.x + 5` is faster) 34 | - Numerical `for` loops are now compiled more efficiently, yielding significant speedups on hot loops 35 | - Bit operations with constants are now compiled more efficiently on X64 (for example, `bit32.lshift(x, 1)` is faster); this optimization was already in place for A64 36 | - Repeated access to array elements with the same object and index is now faster in certain cases 37 | - Performance of function calls has been marginally improved on X64 and A64 38 | - Fix code generation for some `bit32.extract` variants where we could produce incorrect results 39 | - `table.insert` is now faster when called with two arguments as it's compiled directly to native code 40 | - To reduce code size, module code outside of functions is not compiled natively unless it has loops 41 | 42 | ## Analysis Improvements 43 | 44 | The `break` and `continue` keywords can now be used in loop bodies to refine variables. This was contributed by a community member - thank you, [AmberGraceSoftware](https://github.com/AmberGraceSoftware)! 45 | 46 | ```lua 47 | function f(objects: { { value: string? } }) 48 | for _, object in objects do 49 | if not object.value then 50 | continue 51 | end 52 | 53 | local x: string = object.value -- ok! 54 | end 55 | end 56 | ``` 57 | 58 | When type information is present, we will now emit a warning when `#` or `ipairs` is used on a table that has no numeric keys or indexers. This helps avoid common bugs like using `#t == 0` to check if a dictionary is empty. 59 | 60 | ```lua 61 | local message = { data = { 1, 2, 3 } } 62 | 63 | if #message == 0 then -- Using '#' on a table without an array part is likely a bug 64 | end 65 | ``` 66 | 67 | Finally, some uses of `getfenv`/`setfenv` are now flagged as deprecated. We do not plan to remove support for `getfenv`/`setfenv` but we actively discourage its use as it disables many optimizations throughout the compiler, runtime, and native code generation, and interferes with type checking and linting. 68 | 69 | ## Autocomplete Improvements 70 | 71 | We used to have a bug that would arise in the following situation: 72 | 73 | ```lua 74 | --!strict 75 | type Direction = "Left" | "Right" 76 | local dir: Direction = "Left" 77 | 78 | if dir == ""| then 79 | end 80 | ``` 81 | 82 | (imagine the cursor is at the position of the `|` character in the `if` statement) 83 | 84 | We used to suggest `Left` and `Right` even though they are not valid completions at that position. This is now fixed. 85 | 86 | We've also added a complete suggestion for anonymous functions if one would be valid at the requested position. For example: 87 | 88 | ```lua 89 | local p = Instance.new('Part') 90 | p.Touched:Connect( 91 | ``` 92 | 93 | You will see a completion suggestion `function (anonymous autofilled)`. Selecting that will cause the following to be inserted into your code: 94 | 95 | ```lua 96 | local p = Instance.new('Part') 97 | p.Touched:Connect(function(otherPart: BasePart) end 98 | ``` 99 | 100 | We also fixed some confusing editor feedback in the following case: 101 | 102 | ```lua 103 | game:FindFirstChild( 104 | ``` 105 | 106 | Previously, the signature help tooltip would erroneously tell you that you needed to pass a `self` argument. We now correctly offer the signature `FindFirstChild(name: string, recursive: boolean?): Instance` 107 | 108 | ## Runtime Improvements 109 | 110 | * `string.format`'s handling of `%*` and `%s` is now 1.5-2x faster 111 | * `tonumber` and `tostring` are now 1.5x and 2.5x faster respectively when working on primitive types 112 | * Compiler now recognizes `math.pi` and `math.huge` and performs constant folding on the expressions that involve these at `-O2`; for example, `math.pi*2` is now free. 113 | * Compiler now optimizes `if...then...else` expressions into AND/OR form when possible (for example, `if x then x else y` now compiles as `x or y`) 114 | * We had a few bugs around `repeat..until` statements when the `until` condition referred to local variables defined in the loop body. These bugs have been fixed. 115 | * Fix an oversight that could lead to `string.char` and `string.sub` generating potentially unlimited amounts of garbage and exhausting all available memory. 116 | * We had a bug that could cause the compiler to unroll loops that it really shouldn't. This could result in massive bytecode bloat. It is now fixed. 117 | 118 | ## luau-lang on GitHub 119 | 120 | If you've been paying attention to our GitHub projects, you may have noticed that we've moved `luau` repository to a new [luau-lang GitHub organization](https://github.com/luau-lang)! This is purely an organizational change but it's helping us split a few repositories for working with documentation and RFCs and be more organized with pull requests in different areas. Make sure to update your bookmarks and [star our main repository](https://github.com/luau-lang/luau) if you haven't already! 121 | 122 | Lastly, a big thanks to our [open source community](https://github.com/luau-lang/luau) for their generous contributions: 123 | 124 | * [MagelessMayhem](https://github.com/MagelessMayhem) 125 | * [cassanof](https://github.com/cassanof) 126 | * [LoganDark](https://github.com/LoganDark) 127 | * [j-hui](https://github.com/j-hui) 128 | * [xgqt](https://github.com/xgqt) 129 | * [jdpatdiscord](https://github.com/jdpatdiscord) 130 | * [Someon1e](https://github.com/Someon1e) 131 | * [AmberGraceSoftware](https://github.com/AmberGraceSoftware) 132 | * [RadiantUwU](https://github.com/RadiantUwU) 133 | * [SamuraiCrow](https://github.com/SamuraiCrow) 134 | -------------------------------------------------------------------------------- /assets/css/theme2.scss: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | --- 4 | 5 | @charset "utf-8"; 6 | 7 | @import "minimal-mistakes/skins/{{ site.minimal_mistakes_skin2 | default: 'default' }}"; // skin 8 | @import "minimal-mistakes"; // main partials -------------------------------------------------------------------------------- /assets/images/create-new-place.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luau-lang/site/96af82d9c8a22f337908676facf999310f4d87ba/assets/images/create-new-place.png -------------------------------------------------------------------------------- /assets/images/create-script.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luau-lang/site/96af82d9c8a22f337908676facf999310f4d87ba/assets/images/create-script.png -------------------------------------------------------------------------------- /assets/images/error-isfoo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luau-lang/site/96af82d9c8a22f337908676facf999310f4d87ba/assets/images/error-isfoo.png -------------------------------------------------------------------------------- /assets/images/error-ispositive-boolean.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luau-lang/site/96af82d9c8a22f337908676facf999310f4d87ba/assets/images/error-ispositive-boolean.png -------------------------------------------------------------------------------- /assets/images/error-ispositive-string.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luau-lang/site/96af82d9c8a22f337908676facf999310f4d87ba/assets/images/error-ispositive-string.png -------------------------------------------------------------------------------- /assets/images/error-ispositive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luau-lang/site/96af82d9c8a22f337908676facf999310f4d87ba/assets/images/error-ispositive.png -------------------------------------------------------------------------------- /assets/images/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luau-lang/site/96af82d9c8a22f337908676facf999310f4d87ba/assets/images/example.png -------------------------------------------------------------------------------- /assets/images/luau-88.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luau-lang/site/96af82d9c8a22f337908676facf999310f4d87ba/assets/images/luau-88.png -------------------------------------------------------------------------------- /assets/images/luau-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luau-lang/site/96af82d9c8a22f337908676facf999310f4d87ba/assets/images/luau-header.png -------------------------------------------------------------------------------- /assets/images/luau-recap-august-2020-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luau-lang/site/96af82d9c8a22f337908676facf999310f4d87ba/assets/images/luau-recap-august-2020-arrow.png -------------------------------------------------------------------------------- /assets/images/luau-recap-august-2020-format.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luau-lang/site/96af82d9c8a22f337908676facf999310f4d87ba/assets/images/luau-recap-august-2020-format.png -------------------------------------------------------------------------------- /assets/images/luau-recap-august-2020-format2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luau-lang/site/96af82d9c8a22f337908676facf999310f4d87ba/assets/images/luau-recap-august-2020-format2.png -------------------------------------------------------------------------------- /assets/images/luau-recap-august-2020-meta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luau-lang/site/96af82d9c8a22f337908676facf999310f4d87ba/assets/images/luau-recap-august-2020-meta.png -------------------------------------------------------------------------------- /assets/images/luau-recap-february-2021-benchmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luau-lang/site/96af82d9c8a22f337908676facf999310f4d87ba/assets/images/luau-recap-february-2021-benchmark.png -------------------------------------------------------------------------------- /assets/images/luau-recap-february-2021-debugger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luau-lang/site/96af82d9c8a22f337908676facf999310f4d87ba/assets/images/luau-recap-february-2021-debugger.png -------------------------------------------------------------------------------- /assets/images/luau-recap-june-2020-xkcd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luau-lang/site/96af82d9c8a22f337908676facf999310f4d87ba/assets/images/luau-recap-june-2020-xkcd.png -------------------------------------------------------------------------------- /assets/images/luau-recap-march-2021-debug-after.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luau-lang/site/96af82d9c8a22f337908676facf999310f4d87ba/assets/images/luau-recap-march-2021-debug-after.png -------------------------------------------------------------------------------- /assets/images/luau-recap-march-2021-debug-before.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luau-lang/site/96af82d9c8a22f337908676facf999310f4d87ba/assets/images/luau-recap-march-2021-debug-before.png -------------------------------------------------------------------------------- /assets/images/luau-recap-march-2021-debug-dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luau-lang/site/96af82d9c8a22f337908676facf999310f4d87ba/assets/images/luau-recap-march-2021-debug-dialog.png -------------------------------------------------------------------------------- /assets/images/luau-recap-november-2019-option.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luau-lang/site/96af82d9c8a22f337908676facf999310f4d87ba/assets/images/luau-recap-november-2019-option.png -------------------------------------------------------------------------------- /assets/images/luau-type-checking-release-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luau-lang/site/96af82d9c8a22f337908676facf999310f4d87ba/assets/images/luau-type-checking-release-screenshot.png -------------------------------------------------------------------------------- /assets/images/luau-type-checking-release-studio-option.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luau-lang/site/96af82d9c8a22f337908676facf999310f4d87ba/assets/images/luau-type-checking-release-studio-option.png -------------------------------------------------------------------------------- /assets/images/luau.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luau-lang/site/96af82d9c8a22f337908676facf999310f4d87ba/assets/images/luau.png -------------------------------------------------------------------------------- /assets/images/mascot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luau-lang/site/96af82d9c8a22f337908676facf999310f4d87ba/assets/images/mascot.png -------------------------------------------------------------------------------- /assets/images/type-annotation-needed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luau-lang/site/96af82d9c8a22f337908676facf999310f4d87ba/assets/images/type-annotation-needed.png -------------------------------------------------------------------------------- /assets/images/type-annotation-provided.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luau-lang/site/96af82d9c8a22f337908676facf999310f4d87ba/assets/images/type-annotation-provided.png -------------------------------------------------------------------------------- /assets/images/type-error-after-syntax-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luau-lang/site/96af82d9c8a22f337908676facf999310f4d87ba/assets/images/type-error-after-syntax-error.png -------------------------------------------------------------------------------- /assets/images/type-refinement-in-action.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luau-lang/site/96af82d9c8a22f337908676facf999310f4d87ba/assets/images/type-refinement-in-action.png -------------------------------------------------------------------------------- /assets/js/luau_mode.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | // Luau mode. Based on Lua mode from CodeMirror and Franciszek Wawrzak (https://codemirror.net/mode/lua/lua.js) 5 | 6 | (function(mod) { 7 | if (typeof exports == "object" && typeof module == "object") // CommonJS 8 | mod(require("../../lib/codemirror")); 9 | else if (typeof define == "function" && define.amd) // AMD 10 | define(["../../lib/codemirror"], mod); 11 | else // Plain browser env 12 | mod(CodeMirror); 13 | })(function(CodeMirror) { 14 | "use strict"; 15 | 16 | CodeMirror.defineMode("luau", function(_, parserConfig) { 17 | var indentUnit = 4; 18 | 19 | function prefixRE(words) { 20 | return new RegExp("^(?:" + words.join("|") + ")"); 21 | } 22 | function wordRE(words) { 23 | return new RegExp("^(?:" + words.join("|") + ")$"); 24 | } 25 | var specials = wordRE(parserConfig.specials || ["type"]); 26 | 27 | // long list of standard functions from lua manual 28 | var builtins = wordRE([ 29 | "_VERSION","assert","error","gcinfo","getfenv","getmetatable","ipairs","newproxy","next", 30 | "pairs","pcall","print","rawequal","rawget","rawlen","rawset","select","setfenv", 31 | "setmetatable","tonumber","tostring","type","typeof","unpack","xpcall", 32 | 33 | "bit32.arshift","bit32.band","bit32.bnot","bit32.bor","bit32.btest","bit32.bxor","bit32.byteswap", 34 | "bit32.countlz","bit32.countrz","bit32.extract","bit32.lrotate","bit32.lshift","bit32.replace","bit32.rrotate","bit32.rshift", 35 | 36 | "buffer.copy","buffer.create","buffer.fill","buffer.fromstring","buffer.len", 37 | "buffer.readf32","buffer.readf64","buffer.readi16","buffer.readi32","buffer.readi8","buffer.readstring","buffer.readu16","buffer.readu32","buffer.readu8", 38 | "buffer.tostring","buffer.writef32","buffer.writef64","buffer.writei16","buffer.writei32","buffer.writei8","buffer.writestring","buffer.writeu16","buffer.writeu32","buffer.writeu8", 39 | 40 | "coroutine.close","coroutine.create","coroutine.isyieldable","coroutine.resume","coroutine.running","coroutine.status","coroutine.wrap","coroutine.yield", 41 | 42 | "debug.info","debug.traceback", 43 | 44 | "math.abs","math.acos","math.asin","math.atan","math.atan2","math.ceil","math.clamp","math.cos","math.cosh", 45 | "math.deg","math.exp","math.floor","math.fmod","math.frexp","math.huge","math.ldexp","math.log","math.log10", 46 | "math.max","math.min","math.modf","math.noise","math.pi","math.pow","math.rad","math.random","math.randomseed", 47 | "math.round","math.sign","math.sin","math.sinh","math.sqrt","math.tan","math.tanh", 48 | 49 | "os.clock","os.date","os.difftime","os.time", 50 | 51 | "string.byte","string.char","string.find","string.format","string.gmatch","string.gsub","string.len","string.lower", 52 | "string.match","string.pack","string.packsize","string.rep","string.reverse","string.split","string.sub","string.unpack","string.upper", 53 | 54 | "table.clear","table.clone","table.concat","table.create","table.find","table.freeze","table.insert","table.isfrozen","table.maxn","table.move","table.pack","table.remove","table.sort","table.unpack", 55 | 56 | "utf8.char","utf8.charpattern","utf8.codepoint","utf8.codes","utf8.len","utf8.offset", 57 | 58 | "vector.create", "vector.magnitude", "vector.normalize", "vector.cross", "vector.dot", "vector.angle", 59 | "vector.floor", "vector.ceil", "vector.abs", "vector.sign", "vector.clamp", "vector.max", "vector.min" 60 | ]); 61 | var keywords = wordRE(["and","break","elseif","false","nil","not","or","return", 62 | "true","function", "end", "if", "then", "else", "do", 63 | "while", "repeat", "until", "for", "in", "local", "continue" ]); 64 | 65 | var indentTokens = wordRE(["function", "if","repeat","do", "\\(", "{"]); 66 | var dedentTokens = wordRE(["end", "until", "\\)", "}"]); 67 | var dedentPartial = prefixRE(["end", "until", "\\)", "}", "else", "elseif"]); 68 | 69 | function readBracket(stream) { 70 | var level = 0; 71 | while (stream.eat("=")) ++level; 72 | stream.eat("["); 73 | return level; 74 | } 75 | 76 | function normal(stream, state) { 77 | var ch = stream.next(); 78 | if (ch == "-" && stream.eat("-")) { 79 | if (stream.eat("[") && stream.eat("[")) 80 | return (state.cur = bracketed(readBracket(stream), "comment"))(stream, state); 81 | stream.skipToEnd(); 82 | return "comment"; 83 | } 84 | if (ch == "\"" || ch == "'" || ch == "`") 85 | return (state.cur = string(ch))(stream, state); 86 | if (ch == "[" && /[\[=]/.test(stream.peek())) 87 | return (state.cur = bracketed(readBracket(stream), "string"))(stream, state); 88 | if (/\d/.test(ch)) { 89 | stream.eatWhile(/[\w.%]/); 90 | return "number"; 91 | } 92 | if (/[\w_]/.test(ch)) { 93 | stream.eatWhile(/[\w\\\-_.]/); 94 | return "variable"; 95 | } 96 | return null; 97 | } 98 | 99 | function bracketed(level, style) { 100 | return function(stream, state) { 101 | var curlev = null, ch; 102 | while ((ch = stream.next()) != null) { 103 | if (curlev == null) { 104 | if (ch == "]") curlev = 0; 105 | } else if (ch == "=") { 106 | ++curlev; 107 | } else if (ch == "]" && curlev == level) { 108 | state.cur = normal; 109 | break; 110 | } else { 111 | curlev = null; 112 | } 113 | } 114 | return style; 115 | }; 116 | } 117 | 118 | function string(quote) { 119 | return function(stream, state) { 120 | var escaped = false, ignoreWhitespace = false, ch; 121 | while ((ch = stream.next()) != null) { 122 | if (ch == quote && !escaped) { 123 | break; 124 | } 125 | if (ch == "z" && escaped) { 126 | stream.eatSpace(); 127 | ignoreWhitespace = stream.eol(); 128 | } 129 | escaped = !escaped && ch == "\\"; 130 | } 131 | 132 | if (!ignoreWhitespace) { 133 | state.cur = normal; 134 | } 135 | return "string"; 136 | }; 137 | } 138 | 139 | return { 140 | startState: function(basecol) { 141 | return {basecol: basecol || 0, indentDepth: 0, cur: normal}; 142 | }, 143 | 144 | token: function(stream, state) { 145 | if (stream.eatSpace()) { 146 | return null; 147 | } 148 | var style = state.cur(stream, state); 149 | var word = stream.current(); 150 | if (style == "variable") { 151 | if (keywords.test(word)) { 152 | style = "keyword"; 153 | } else if (builtins.test(word)) { 154 | style = "builtin"; 155 | } else if (specials.test(word)) { 156 | style = "variable-2"; 157 | } 158 | } 159 | if ((style != "comment") && (style != "string")) { 160 | if (indentTokens.test(word)) { 161 | ++state.indentDepth; 162 | } else if (dedentTokens.test(word)) { 163 | --state.indentDepth; 164 | } 165 | } 166 | return style; 167 | }, 168 | 169 | indent: function(state, textAfter) { 170 | var closing = dedentPartial.test(textAfter); 171 | return state.basecol + indentUnit * (state.indentDepth - (closing ? 1 : 0)); 172 | }, 173 | 174 | electricInput: /^\s*(?:end|until|else|\)|\})$/, 175 | lineComment: "--", 176 | blockCommentStart: "--[[", 177 | blockCommentEnd: "]]" 178 | }}); 179 | CodeMirror.defineMIME("text/x-luau", "luau"); 180 | }); 181 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luau-lang/site/96af82d9c8a22f337908676facf999310f4d87ba/favicon.ico -------------------------------------------------------------------------------- /index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Lua*u* 3 | layout: splash 4 | permalink: / 5 | 6 | header: 7 | overlay_color: #000 8 | overlay_filter: 0.8 9 | overlay_image: /assets/images/luau-header.png 10 | 11 | excerpt: > 12 | Lua*u* (lowercase *u*, /ˈlu.aʊ/) is a fast, small, safe, gradually typed embeddable scripting language derived from Lua. 13 | 14 | feature_row1: 15 | - 16 | title: Motivation 17 | excerpt: > 18 | Around 2006, [Roblox](https://www.roblox.com) started using Lua 5.1 as a scripting language for games. Over the years we ended up substantially evolving the implementation and the language; to support growing sophistication of games on the Roblox platform, growing team sizes and large internal teams writing a lot of code for application/editor (1+MLOC as of 2020), we had to invest in performance, ease of use and language tooling, and introduce a gradual type system to the language. [More...](/why) 19 | 20 | - 21 | title: Sandboxing 22 | excerpt: > 23 | Luau limits the set of standard libraries exposed to the users and implements extra sandboxing features to be able to run unprivileged code (written by our game developers) side by side with privileged code (written by us). This results in an execution environment that is different from what is commonplace in Lua. [More...](/sandbox) 24 | 25 | - 26 | title: Compatibility 27 | excerpt: > 28 | Whenever possible, Luau aims to be backwards-compatible with Lua 5.1 and at the same time to incorporate features from later revisions of Lua. However, Luau is not a full superset of later versions of Lua - we do not always agree with Lua design decisions, and have different use cases and constraints. All post-5.1 Lua features, along with their support status in Luau, [are documented here](compatibility). 29 | 30 | feature_row2: 31 | - 32 | title: Syntax 33 | image_path: /assets/images/example.png 34 | excerpt: > 35 | Luau is syntactically backwards-compatible with Lua 5.1 (code that is valid Lua 5.1 is also valid Luau); however, we have extended the language with a set of syntactical features that make the language more familiar and ergonomic. The syntax [is described here](syntax). 36 | 37 | feature_row3: 38 | - 39 | title: Analysis 40 | excerpt: > 41 | To make it easier to write correct code, Luau comes with a set of analysis tools that can surface common mistakes. These consist of a linter and a type checker, colloquially known as script analysis, and are integrated into `luau-analyze` command line executable. The linting passes are [described here](lint), and the type checking user guide can [be found here](typecheck). 42 | 43 | - 44 | title: Performance 45 | excerpt: > 46 | In addition to a completely custom front end that implements parsing, linting and type checking, Luau runtime features new bytecode, interpreter and compiler that are heavily tuned for performance. Luau interpreter can be competitive with LuaJIT interpreter depending on the program. An optional component for manual Just-In-Time compilation is also available for x64 and arm64 platforms, which can considerably speed up certain programs. We continue to optimize the runtime and rewrite portions of it to be even more efficient. While our overall goal is to minimize the amount of time programmers spend tuning performance, some details about the performance characteristics are [provided for inquisitive minds](performance). 47 | 48 | - 49 | title: Libraries 50 | excerpt: > 51 | As a language, Luau is a full superset of Lua 5.1. As far as standard library is concerned, some functions had to be removed from the builtin libraries, and some functions had to be added; refer to [full documentation](/library) for details. When Luau is embedded into an application, the scripts normally get access to extra library features that are application-specific. 52 | 53 | --- 54 | 55 | {% include feature_row id="feature_row1" %} 56 | 57 | {% include feature_row id="feature_row2" type="left" %} 58 | 59 | {% include feature_row id="feature_row3" %} 60 | -------------------------------------------------------------------------------- /logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | --------------------------------------------------------------------------------