Compare commits

...

1154 commits

Author SHA1 Message Date
ariel
72f6c8b679
Sync to upstream/release/672 (#1800)
# What's Changed?

Hi there, folks! It's been another busy week in the type mines, trying
to bring you all the very best type inference system we can. We've got a
bunch of updates to large pain points across the new type solver, and
our next big update (currently under a debug flag) improving type
generalization is finally nearing completion (and should hopefully
eliminate quite a lot of "type solver failed to complete" errors). We've
also continued polishing both the CST Parser and the `Luau.Require`
library we introduced a few releases ago based on user feedback and bug
reports, and we're really happy with how they're turning out.

# Parser
- Fixes a bug in the CST tooling where the spacing on return type
annotations for functions was not being printed correctly.
- Resolves some issues with the JSON encoding of `AstGenericType` and
`AstGenericTypePack`

# Runtime
- Implements support for yielding requires in `Luau.Require` library.
- Improves the error messages for require-by-string to include the chunk
name that was problematic where possible and the overall require path
that failed to be required.
- Fixes a bug that prevented the use of `require` within C functions and
`pcall`.
- Adds an API to support selectively removing chunks from the require
cache in `Luau.Require`
- Adds an API to support clearing the entire require cache in
`Luau.Require`

# New Type Solver

- Fixes a crash in the new non-strict mode when visiting function return
types in incomplete ASTs (e.g. during editing).
- Improves type simplification to support intersections of tables with
extern types, resolving _one_ of the causes of frequent refinements
unexpectedly leading to `never`.
- Improves type inference to better understand diverging branches in
functions, reducing false negatives where the type system fails to learn
that a binding must now always be initialized.
- Fixes a typo in the type definitions for user-defined function types
where the `intersection` tag was misspelled.
- Improves the overall accuracy of free type tracking during constraint
solving, leading to better inference results overall.
- Implements `types.optional` as a new library function for user-defined
type functions to make it easier to union a type with `nil`.
- Resolves a number of bugs caused by local type inference expanding the
domain of upvalues

# Internal Contributors

Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Ariel Weiss <aaronweiss@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Talha Pathan <tpathan@roblox.com>
Co-authored-by: Varun Saini <vsaini@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>

---------

Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Varun Saini <61795485+vrn-sn@users.noreply.github.com>
Co-authored-by: Alexander Youngblood <ayoungblood@roblox.com>
Co-authored-by: Menarul Alam <malam@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
2025-05-02 14:00:23 -07:00
Varun Saini
d9aa88e772
Fix crash when require is called from root VM stack (#1788)
Copied from #1785:
> If require is called from the root interpreter stack (e.g. using C
API) then lua_getinfo call will not succeed, leaving garbage in
lua_Debug ar struct.
> Accessing later ar.source as null-terminated string is unsafe and can
cause a crash.
> 
> This PR adds a check to ensure that lua_getinfo call is successful.

Co-authored-by: Alex Orlenko <zxteam@protonmail.com>
2025-04-28 11:15:43 -07:00
Andy Friesen
c51743268b
Sync to upstream/release/671 (#1787)
# General

* Internally rename `ClassType` to `ExternType`. In definition files,
the syntax to define these types has changed to `declare extern type Foo
with prop: type end`
* Add `luarequire_registermodule` to Luau.Require
* Support yieldable Luau C functions calling other functions
* Store return types as `AstTypePack*` on Ast nodes

## New Solver

* Improve the logic that determines constraint dispatch ordering
* Fix a crash in the type solver that arose when using multi-return
functions with `string.format`
* Fix https://github.com/luau-lang/luau/issues/1736
* Initial steps toward rethinking function generalization:
* Instead of generalizing every type in a function all at once, we will
instead generalize individual type variables once their bounds have been
fully resolved. This will make it possible to properly interleave type
function reduction and generalization.
* Magic functions are no longer considered magical in cases where they
are not explicitly called by the code.
* The most prominent example of this is in `for..in` loops where the
function call is part of the desugaring process.
* Almost all magic functions work by directly inspecting the AST, so
they can't work without an AST fragment anyway.
* Further, none of the magic functions we have are usefully used in this
way.

Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Ariel Weiss <aaronweiss@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Sora Kanosue <skanosue@roblox.com>
Co-authored-by: Talha Pathan <tpathan@roblox.com>
Co-authored-by: Varun Saini <vsaini@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2025-04-25 14:19:27 -07:00
Vighnesh-V
a2303a6ae6
Sync to upstream/release/670 (#1779)
# General 
This week has been focused primarily on bugfixes, with a ton of
usability improvements to the new solver, fragment autocomplete, and the
concrete syntax tree project.

## Runtime
- Fix an assertion caused by failing to allocate native code pages.
- Expose a `lua_pushrequire` function, which performs the same
initialization steps as `luaopen_require` but does not register require
globally. This lets users create specialized, custom `requires`.

# New Solver
- Fix a bug in simplification of types caused by combinatorial explosion
of intersection and union types.
- Fix a memory leak in fragment autocomplete
- Improve the isolation of modules in fragment autocomplete
- Throw errors when users define a type function with the name `typeof`
- Continue to narrow intersection types which might be `never`.
- Major rework of generalization continues - we are blazing a new path
with eager + non-reentrant generalization and actively working to make
these more performant and less error prone.
- Improve the ability of `and/or` type functions to reduce, even when
their arguments are generic.
- Report arity mismatches for undersaturated calls with unknown
parameters

# New Non-Strict
- Extends the new non-strict mode to report unknown symbols in types 

# Old Solver
- Fix a crash caused by excessive stack usage during typechecking

# Misc
- Improvements to Concrete Syntax Tree location tracking for string
table props.

---
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Ariel Weiss <aaronweiss@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Sora Kanosue <skanosue@roblox.com>
Co-authored-by: Talha Pathan <tpathan@roblox.com>
Co-authored-by: Varun Saini <vsaini@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2025-04-18 13:44:39 -07:00
Hunter Goldstein
d110c812bb
Disable LuauNonReentrantGeneralization for some tests (#1775)
For now, this flag causes a stack overflow for some tests on
Windows: we end up minting a massive recursive intersection during
generalization. Let's flip it off until a fix arrives.
2025-04-14 16:55:31 -07:00
Maidenless
b6457801c7
Update Arch Linux installation instructions (#1774) 2025-04-14 08:47:49 -07:00
Varun Saini
50f32a1400
Do not store file extensions in module chunknames [Luau CLI] (#1772) 2025-04-13 10:31:45 -07:00
menarulalam
a8d14596e7
Sync to upstream/release/669 (#1770)
We have lots of new changes for you! 

# What's Changed
## General

- We updated Luau's license year to 2025! 
- We fixed a bug where large amounts of errors were being printed when
deep intersections of unions error.


## Require-by-String
This release introduces the `Luau.Require` library, which exposes the
runtime semantics of require-by-string, including support for the new
`@self` alias described in [this
RFC](https://github.com/luau-lang/rfcs/pull/109).

The library operates on a virtualized filesystem, allowing consumers to
specify navigation rules without assuming a filesystem context.
Documentation in `Require.h` explains how to enable the library, and the
`setupState` function in Repl.cpp demonstrates how we've integrated it
into the luau CLI tool. Note that the interface in `Require.h` is
written in C, which enables any application written in a language with a
C foreign-function interface to link against this library and enable
require-by-string. This makes it straightforward for any application
embedding Luau to support require-by-string, provided that it defines or
operates within an environment resembling a virtual filesystem.

The core navigation semantics of require-by-string have additionally
been pulled into the `Luau.RequireNavigator` library. While
`Luau.Require` internally depends on `Luau.RequireNavigator`, the latter
does not depend on the Luau VM. This library provides an interface for
inspecting require-by-string's navigation behavior and therefore serves
as a useful dependency for static tooling. Documentation for
`Luau.RequireNavigator` is available in `RequireNavigator.h`.
## Autocomplete
- We fixed a memory leak in fragment autocomplete!
## New Solver And Old Solver
- We've found a infinite iteration error over a type pack. We added a
way to detect this error and throw an `InternalCompileError` instead.
- We fix `table.freeze` not accounting for the first argument not
getting type stated. We fall back to regular inference instead.
- We fix a crash in the old solver with `length_error`.
- We fix a crash in the new solver stemming from generalization
reentrancy. Now we correctly generalize interior free types that do not
appear in a function signature.
- We fix a nil refinement. (Fixes
https://github.com/luau-lang/luau/issues/1687 and
https://github.com/luau-lang/luau/issues/1451)



### Internal Contributors
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Talha Pathan <tpathan@roblox.com>
Co-authored-by: Varun Saini <vsaini@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

Full Changelog: https://github.com/luau-lang/luau/compare/0.668...0.669

---------

Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Varun Saini <61795485+vrn-sn@users.noreply.github.com>
Co-authored-by: Alexander Youngblood <ayoungblood@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Ariel Weiss <aaronweiss@roblox.com>
2025-04-11 17:44:21 -07:00
vegorov-rbx
cbe078b3b4
Enable the A64 unwinding test (#1769) 2025-04-11 11:33:06 -07:00
Aviral Goel
ee1c6bf0db
Sync to upstream/release/668 (#1760)
## New Type Solver

1. Update resolved types for singleton unions and intersections to avoid
crashing when type checking type assertions.
2. Generalize free return type pack of a function type inferred at call
site to ensure that the free type does not leak to another module.
3. Fix crash from cyclic indexers by reducing if possible or producing
an error otherwise.
4. Fix handling of irreducible type functions to prevent type inference
from failing.
5. Fix handling of recursive metatables to avoid infinite recursion.

## New and Old Type Solver

Fix accidental capture of all exceptions in multi-threaded typechecking
by converting all typechecking exceptions to `InternalCompilerError` and
only capturing those.

## Fragment Autocomplete

1. Add a block based diff algorithm based on class index and span for
re-typechecking. This reduces the granularity of fragment autocomplete
to avoid flakiness when the fragment does not have enough type
information.
2. Fix bugs arising from incorrect scope selection for autocompletion.

## Roundtrippable AST

Store type alias location in `TypeFun` class to ensure it is accessible
for exported types as part of the public interface.

## Build System

1. Bump minimum supported CMake version to 3.10 since GitHub is phasing
out the currently supported minimum version 3.0, released 11 years ago.
2. Fix compilation when `HARDSTACKTESTS` is enabled.

## Miscellaneous

Flag removals and cleanup of unused code.

## Internal Contributors

Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Ariel Weiss <aaronweiss@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Talha Pathan <tpathan@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

## External Contributors

Thanks to [@grh-official](https://github.com/grh-official) for PR #1759 

**Full Changelog**:
https://github.com/luau-lang/luau/compare/0.667...0.668

---------

Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Varun Saini <61795485+vrn-sn@users.noreply.github.com>
Co-authored-by: Alexander Youngblood <ayoungblood@roblox.com>
Co-authored-by: Menarul Alam <malam@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Ariel Weiss <aaronweiss@roblox.com>
2025-04-04 14:11:51 -07:00
ayoungbloodrbx
6b33251b89
Sync to upstream/release/667 (#1754)
After a very auspicious release last week, we have a new bevy of changes
for you!

## What's Changed

### Deprecated Attribute

This release includes an implementation of the `@deprecated` attribute
proposed in [this
RFC](https://rfcs.luau.org/syntax-attribute-functions-deprecated.html).
It relies on the new type solver to propagate deprecation information
from function and method AST nodes to the corresponding type objects.
These objects are queried by a linter pass when it encounters local,
global, or indexed variables, to issue deprecation warnings. Uses of
deprecated functions and methods in recursion are ignored. To support
deprecation of class methods, the parser has been extended to allow
attribute declarations on class methods. The implementation does not
support parameters, so it is not currently possible for users to
customize deprecation messages.

### General

- Add a limit for normalization of function types.

### New Type Solver

- Fix type checker to accept numbers as concat operands (Fixes #1671).
- Fix user-defined type functions failing when used inside type
aliases/nested calls (Fixes #1738, Fixes #1679).
- Improve constraint generation for overloaded functions (in part thanks
to @vvatheus in #1694).
- Improve type inference for indexers on table literals, especially when
passing table literals directly as a function call argument.
- Equate regular error type and intersection with a negation of an error
type.
- Avoid swapping types in 2-part union when RHS is optional.
- Use simplification when doing `~nil` refinements.
- `len<>` now works on metatables without `__len` function.

### AST

- Retain source information for `AstTypeUnion` and
`AstTypeIntersection`.

### Transpiler

- Print attributes on functions.

### Parser

- Allow types in indexers to begin with string literals by @jackdotink
in #1750.

### Autocomplete

- Evaluate user-defined type functions in ill-formed source code to
provide autocomplete.
- Fix the start location of functions that have attributes.
- Implement better fragment selection.

### Internal Contributors

Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Ariel Weiss <aaronweiss@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Sora Kanosue <skanosue@roblox.com>
Co-authored-by: Talha Pathan <tpathan@roblox.com>
Co-authored-by: Varun Saini <vsaini@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

**Full Changelog**:
https://github.com/luau-lang/luau/compare/0.666...0.667

---------

Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Varun Saini <61795485+vrn-sn@users.noreply.github.com>
Co-authored-by: Menarul Alam <malam@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Ariel Weiss <aaronweiss@roblox.com>
2025-03-28 16:15:46 -07:00
Jack
12dac2f1f4
fix parsing string union indexers (#1750)
Today this code results in a syntax error: `type foo = { ["bar" |
"baz"]: number }`. This is odd and I believe it is a bug. I have fixed
this so that it is now parsed as an indexer field with a union type.
This change should not affect the way any code is parsed today, and
allow types in indexers to begin with string literals.

---------

Co-authored-by: ariel <aweiss@hey.com>
2025-03-25 16:18:22 -07:00
Matheus
2621488abe
Fix singleton parameters in overloaded functions (#1694)
- Fixes #1691 
- Fixes #1589

---------

Co-authored-by: Math <175355178+maffeus@users.noreply.github.com>
Co-authored-by: ariel <aaronweiss@roblox.com>
Co-authored-by: Matheus <175355178+m4fh@users.noreply.github.com>
Co-authored-by: ariel <aweiss@hey.com>
2025-03-24 09:27:13 -07:00
Varun Saini
5f42e63a73
Sync to upstream/release/666 (#1747)
Another week, another release. Happy spring! 🌷 

## New Type Solver

- Add typechecking and autocomplete support for user-defined type
functions!
- Improve the display of type paths, making type mismatch errors far
more human-readable.
- Enhance various aspects of the `index` type function: support function
type metamethods, fix crashes involving cyclic metatables, and forward
`any` types through the type function.
- Fix incorrect subtyping results involving the `buffer` type.
- Fix crashes related to typechecking anonymous functions in nonstrict
mode.

## AST

- Retain source information for type packs, functions, and type
functions.
- Introduce `AstTypeOptional` to differentiate `T?` from `T | nil` in
the AST.
- Prevent the transpiler from advancing before tokens when the AST has
errors.

## Autocomplete

- Introduce demand-based cloning and better module isolation for
fragment autocomplete, leading to a substantial speedup in performance.
- Guard against recursive unions in `autocompleteProps`.

## Miscellaneous

- #1720 (thank you!)

## Internal Contributors

Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Ariel Weiss <aaronweiss@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Talha Pathan <tpathan@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2025-03-21 14:43:00 -07:00
Hunter Goldstein
e0b55a9cb1
Sync to upstream/release/665 (#1732)
Hello all! Another week, another Luau release!

# Change to `lua_setuserdatametatable`

This release fixes #1710: `lua_setuserdatametatable` is being changed so
that it _only_ operates on the top of the stack: the `idx` parameter is
being removed. Prior to this, `lua_setuserdatametable` would set the
metatable of the value in the stack at `idx`, but _always_ pop the top
of the stack. The old behavior is available in this release as
`lua_setuserdatametatable_DEPRECATED`.

# General

This release exposes a generalized implementation of require-by-string's
autocomplete logic. `FileResolver` can now be optionally constructed
with a `RequireSuggester`, which provides an interface for converting a
given module to a `RequireNode`. Consumers of this new API implement a
`RequireNode` to define how modules are represented in their embedded
context, and the new API manages the logic specific to
require-by-string, including providing suggestions for require aliases.
This enhancement moves toward integrating require-by-string's semantics
into the language itself, rather than merely providing a specification
for community members to implement themselves.

# New Type Solver
* Fixed a source of potential `Luau::follow detected a Type cycle`
internal compiler exceptions when assigning a global to itself.
* Fixed an issue whereby `*no-refine*` (a type which should not be
visible at the end of type checking) was not being properly elided,
causing inference of class-like tables to become unreadable / induce
crashes in autocomplete.
* Fixed a case of incomplete constraint solving when performing basic
math in a loop

# Fragment Autocomplete
* Fixed several crashes related to not properly filling in scope
information for the fragments
* Fixed a source of memory corruption by isolating the return type of a
fragment when it is type checked.
* Improved performance by opting not to clone persistent types for the
fragment (e.g.: built in types)
 
# Internal Contributors
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Talha Pathan <tpathan@roblox.com>
Co-authored-by: Varun Saini <vsaini@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Varun Saini <61795485+vrn-sn@users.noreply.github.com>
Co-authored-by: Alexander Youngblood <ayoungblood@roblox.com>
Co-authored-by: Menarul Alam <malam@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Ariel Weiss <aaronweiss@roblox.com>
2025-03-14 13:11:24 -07:00
Kostadin
b0c3f40b0c
Add #include <stdint.h> to fix building with gcc 15 (#1720)
With gcc 15, the C++ Standard Library no longer includes other headers
that were internally used by the library. In Luau's case the missing
header is `<stdint.h>`

Downstream Gentoo bug: https://bugs.gentoo.org/938122
Signed-off-by: Kostadin Shishmanov <kostadinshishmanov@protonmail.com>

---------

Co-authored-by: vegorov-rbx <75688451+vegorov-rbx@users.noreply.github.com>
2025-03-10 06:02:09 -07:00
vegorov-rbx
de9f5d6eb6
Sync to upstream/release/664 (#1715)
As always, a weekly Luau update!
This week we have further improvements to new type solver, fixing a few
of the popular issues reported. The fragment autocomplete is even more
stable and we believe it's ready for broader use.

Aside from that we have a few general fixes/improvements:
* Fixed data race when multi-threaded typechecking is used, appearing as
a random crash at the end of typechecking
* AST data is now available from `Luau::Module`

## New Type Solver

* Fixed type refinements made by function calls which could attach `nil`
as an option of a type before (Fixes #1528)
* Improved bidirectional typechecking in tables (Fixes #1596)
* Fixed normalization of negated types
* `getmetatable()` on `any` type should no longer report an error

## Fragment Autocomplete

* Fixed auto-complete suggestions being provided inside multiline
comments
* Fixed an assertion failure that could happen when old type solver was
used
* Fixed issues with missing suggestions when multiple statements are on
the same line
* Fixed memory safety issues

## Internal Contributors

Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2025-03-07 10:07:27 -08:00
ariel
640ebbc0a5
Sync to upstream/release/663 (#1699)
Hey folks, another week means another Luau release! This one features a
number of bug fixes in the New Type Solver including improvements to
user-defined type functions and a bunch of work to untangle some of the
outstanding issues we've been seeing with constraint solving not
completing in real world use. We're also continuing to make progress on
crashes and other problems that affect the stability of fragment
autocomplete, as we work towards delivering consistent, low-latency
autocomplete for any editor environment.

## New Type Solver

- Fix a bug in user-defined type functions where `print` would
incorrectly insert `\1` a number of times.
- Fix a bug where attempting to refine an optional generic with a type
test will cause a false positive type error (fixes #1666)
- Fix a bug where the `refine` type family would not skip over
`*no-refine*` discriminants (partial resolution for #1424)
- Fix a constraint solving bug where recursive function calls would
consistently produce cyclic constraints leading to incomplete or
inaccurate type inference.
- Implement `readparent` and `writeparent` for class types in
user-defined type functions, replacing the incorrectly included `parent`
method.
- Add initial groundwork (under a debug flag) for eager free type
generalization, moving us towards further improvements to constraint
solving incomplete errors.

## Fragment Autocomplete

- Ease up some assertions to improve stability of mixed-mode use of the
two type solvers (i.e. using Fragment Autocomplete on a type graph
originally produced by the old type solver)
- Resolve a bug with type compatibility checks causing internal compiler
errors in autocomplete.

## Lexer and Parser

- Improve the accuracy of the roundtrippable AST parsing mode by
correctly placing closing parentheses on type groupings.
- Add a getter for `offset` in the Lexer by @aduermael in #1688
- Add a second entry point to the parser to parse an expression,
`parseExpr`

## Internal Contributors

Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Ariel Weiss <aaronweiss@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: James McNellis <jmcnellis@roblox.com>
Co-authored-by: Talha Pathan <tpathan@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Varun Saini <61795485+vrn-sn@users.noreply.github.com>
Co-authored-by: Alexander Youngblood <ayoungblood@roblox.com>
Co-authored-by: Menarul Alam <malam@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2025-02-28 14:42:30 -08:00
Adrien Duermael
6a21dba682
Lexer: add offset getter (#1688)
Added a getter for the Lexer's private `offset` to track its cursor
position in the buffer.
This helps me index tokens by buffer address in a project where I'm
rendering Luau code with [Dear ImGui](https://github.com/ocornut/imgui).
Would love this merged so I can use official Luau releases again!

Co-authored-by: Adrian Duermael <adrien@cu.bzh>
2025-02-24 16:25:57 -08:00
ramdoys
c1e2f650db
chore: update applicable .lua files to .luau (#1560)
Updates all of the APPLICABLE .lua files to the .luau file ending.

---------

Co-authored-by: ramdoys <ramdoysdirect@gmail.com>
2025-02-21 14:29:20 -08:00
vegorov-rbx
c2e72666d9
Sync to upstream/release/662 (#1681)
## What's new

This update brings improvements to the new type solver, roundtrippable
AST parsing mode and closes multiple issues reported in this repository.

* `require` dependency tracing for non-string requires now supports `()`
groups in expressions and types as well as an ability to type annotate a
value with a `typeof` of a different module path
* Fixed rare misaligned memory access in Compiler/Typechecker on 32 bit
platforms (Closes #1572)

## New Solver

* Fixed crash/UB in subtyping of type packs (Closes #1449)
* Fixed incorrect type errors when calling `debug.info` (Closes #1534
and Resolves #966)
* Fixed incorrect boolean and string equality comparison result in
user-defined type functions (Closes #1623)
* Fixed incorrect class types being produced in user-defined type
functions when multiple classes share the same name (Closes #1639)
* Improved bidirectional typechecking for table literals containing
elements that have not been solved yet (Closes #1641)

## Roundtrippable AST

* Added source information for `AstStatTypeAlias`
* Fixed an issue with `AstTypeGroup` node (added in #1643) producing
invalid AST json. Contained type is now named 'inner' instead of 'type'
* Fixed end location of the `do ... end` statement

---

Internal Contributors:

Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Talha Pathan <tpathan@roblox.com>
Co-authored-by: Varun Saini <vsaini@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2025-02-21 10:24:12 -08:00
ariel
86bf4ae42d
Revise some of the copytext in markdown files. (#1677)
We have a bunch of small grammatical nits and slightly awkward phrasings
present in our existing markdown files. This is a small pass over all of
them to fix those, and to provide some additional updated information
that has become more clear over time (like additional users of Luau, or
our leveraging something akin to the Minus 100 Points philosophy for
evaluating RFCs).

---------

Co-authored-by: Varun Saini <61795485+vrn-sn@users.noreply.github.com>
2025-02-20 13:56:00 -08:00
karl-police
29a5198055
Fix the order of the arguments being inputted into result.append in print() from the Type Function Runtime (#1676)
---

Co-authored-by: vegorov-rbx <75688451+vegorov-rbx@users.noreply.github.com>
2025-02-20 12:32:47 -08:00
vegorov-rbx
14ccae9b44
Update minimal Ubuntu version in workflows from 20.04 to 22.04 (#1670)
Ubuntu 20.04 LTS is reaching the end of its standard support period.
GitHub runners are going to stop providing images for it soon.

This PR updates all workflows to use 22.04 instead of 20.04.
While this will impact the compatibility of prebuilt release binaries on
20.04, users of those systems can always build Luau from source.

`coverage` workflow is also updating from clang++10 to clang++ (defaults
to clang++14 today).
Line coverage with this switch drops from 89.65% to 87.45%, function
coverage remains 91.26%.
As a bonus, we now get branch coverage information.
2025-02-20 10:32:33 -08:00
nothing
9c198413ec
Analysis: Make typeof on a type userdata return "type" (#1568)
This change introduces a flag (`LuauUserTypeFunTypeofReturnsType`) that,
when enabled, sets `__type` on the type userdata's metatable to "type".
This behaviour was described in the user-defined type function RFC
(https://rfcs.luau.org/user-defined-type-functions.html), but seems to
have been missed; this change implements that behaviour.

Currently this does not change `typeof(t) == 'type'` emitting an unknown
type warning as I don't trust myself to implement it due to my general
lack of C++ knowledge; this can be worked on later.
2025-02-17 09:36:52 -08:00
dependabot[bot]
bd4fe54f4b
Bump jinja2 from 3.1.4 to 3.1.5 in /tools/fuzz (#1607) 2025-02-17 08:58:48 -08:00
Vighnesh-V
77642988c2
Sync to upstream/release/661 (#1664)
# General
- Additional logging enabled for fragment autocomplete.

## Roundtrippable AST
- Add a new `AstNode`, `AstGenericType`
- Retain source information for `AstExprTypeAssertion`
## New Type Solver
- New non-strict mode will report unknown symbol errors, e.g
```
foo = 5
local wrong1 = foob <- issue warning
```
- Fixed a bug where new non-strict mode failed to visit large parts of
the program.
- We now infer the types of unnanotated local variables in statements
with multiple assignments, e.g. `local x: "a", y, z = "a", f()`
- Fixed bugs in constraint dispatch ordering.
- Fixed a bug that caused an infinite loop between `Subtyping`,
`OverloadResolution`, and `Type Function Reduction`, by preventing calls
to `Type Function Reduction` being re-entrant.
- Fixed a crash in bidirectional type inference caused by asserting read
and write properties on a type that was readonly.

## Runtime
- Fix a stack overflow caused by `luaL_checkstack` consuming stack space
even if the function fails to reserve memory.
- Using '%c' with a 0 value in Luau string.format will append a '\0'.
Resolves https://github.com/luau-lang/luau/issues/1650

## Miscellaneous
- Miscellaneous small bugfixes for the new solver.

**Full Changelog**:
https://github.com/luau-lang/luau/compare/0.660...0.661
----
Co-authored-by: Ariel Weiss <aaronweiss@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Talha Pathan <tpathan@roblox.com>
Co-authored-by: Varun Saini <vsaini@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Varun Saini <61795485+vrn-sn@users.noreply.github.com>
Co-authored-by: Alexander Youngblood <ayoungblood@roblox.com>
Co-authored-by: Menarul Alam <malam@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
2025-02-14 13:57:46 -08:00
Aviral Goel
2e61028cba
Sync to upstream/release/660 (#1643)
# General

This release introduces initial work on a Roundtrippable AST for Luau,
and numerous fixes to the new type solver, runtime, and fragment
autocomplete.

## Roundtrippable AST

To support tooling around source code transformations, we are extending
the parser to retain source information so that we can re-emit the
initial source code exactly as the author wrote it. We have made
numerous changes to the Transpiler, added new AST types such as
`AstTypeGroup`, and added source information to AST nodes such as
`AstExprInterpString`, `AstExprIfElse`, `AstTypeTable`,
`AstTypeReference`, `AstTypeSingletonString`, and `AstTypeTypeof`.

## New Type Solver

* Implement `setmetatable` and `getmetatable` type functions.
* Fix handling of nested and recursive union type functions to prevent
the solver from getting stuck.
* Free types in both old and new solver now have an upper and lower
bound to resolve mixed mode usage of the solvers in fragment
autocomplete.
* Fix infinite recursion during normalization of cyclic tables.
* Add normalization support for intersections of subclasses with negated
superclasses.

## Runtime
* Fix compilation error in Luau buffer bit operations for big-endian
machines.

## Miscellaneous
* Add test and bugfixes to fragment autocomplete.
* Fixed `clang-tidy` warnings in `Simplify.cpp`.

**Full Changelog**:
https://github.com/luau-lang/luau/compare/0.659...0.660

---

Co-authored-by: Ariel Weiss <aaronweiss@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Talha Pathan <tpathan@roblox.com>
Co-authored-by: Varun Saini <vsaini@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Varun Saini <61795485+vrn-sn@users.noreply.github.com>
Co-authored-by: Alexander Youngblood <ayoungblood@roblox.com>
Co-authored-by: Menarul Alam <malam@roblox.com>
2025-02-07 16:17:11 -08:00
menarulalam
f8a1e0129d
Sync to upstream/release/659 (#1637)
## What's Changed

General performance improvements and bug fixes. `lua_clonetable` was
added too.

### General

## Runtime
- Improvements were made to Luau's performance, including a
`lua_clonetable` function and optimizations to string caching. Buffer
read/write operations were optimized for big-endian machines.
## New Solver
- Crashes related to duplicate keys in table literals, fragment AC
crashes, and potential hash collisions in the StringCache.
- We now handle user-defined type functions as opaque and track interior
free table types.
## Require By String
- Require-by-string path resolution was simplified.

**Full Changelog**:
https://github.com/luau-lang/luau/compare/0.658...0.659

---

Co-authored-by: Ariel Weiss <aaronweiss@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Varun Saini <vsaini@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Yohoo Lin <yohoo@roblox.com>

---------

Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Varun Saini <61795485+vrn-sn@users.noreply.github.com>
Co-authored-by: Alexander Youngblood <ayoungblood@roblox.com>
2025-01-31 18:58:36 -08:00
ayoungbloodrbx
c13b5b7440
Sync to upstream/release/658 (#1625)
Some checks failed
benchmark / callgrind (map[branch:main name:luau-lang/benchmark-data], ubuntu-22.04) (push) Has been cancelled
build / macos (push) Has been cancelled
build / macos-arm (push) Has been cancelled
build / ubuntu (push) Has been cancelled
build / windows (Win32) (push) Has been cancelled
build / windows (x64) (push) Has been cancelled
build / coverage (push) Has been cancelled
build / web (push) Has been cancelled
release / macos (push) Has been cancelled
release / ubuntu (push) Has been cancelled
release / windows (push) Has been cancelled
release / web (push) Has been cancelled
## What's Changed

### General
- Allow types of tables to diverge after using `table.clone` (fixes
#1617).
- Allow 2-argument vector.create in Luau.
- Fix a crash when suggesting autocomplete after encountering parsing
errors.
- Add lua_tolstringatom C API which returns the string length (whether
or not the atom exists) and which extends the existing lua_tostringatom
function the same way lua_tolstring/lua_tostring do.
- Luau now retains the DFGs of typechecked modules.

### Magic Functions Migration Note
We've made a change to the API used to define magic functions.

Previously, we had a set of function pointers on each `FunctionType`
that would be invoked by the type inference engine at the correct point.

The problem we'd run into is that they were all `std::function`s, we'd
grown quite a few of them, and Luau allocates tens of thousands of types
as it performs type inference. This adds up to a large amount of memory
for data that isn't used by 99% of types.

To slim things down a bit, we've replaced all of those `std::function`s
with a single `shared_ptr` to a new interface called `MagicFunction`.
This slims down the memory footprint of each type by about 50 bytes.

The virtual methods of `MagicFunction` have roughly 1:1 correspondence
with the old interface, so updating things should not be too difficult:

* `FunctionType::magicFunction` is now `MagicFunction::handleOldSolver`
* `FunctionType::dcrMagicFunction` is now `MagicFunction::infer`
* `FunctionType::dcrMagicRefinement` is now `MagicFunction::refine`
* `FunctionType::dcrMagicTypeCheck` is now `MagicFunction::typeCheck`

**Full Changelog**:
https://github.com/luau-lang/luau/compare/0.657...0.658

---

Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Ariel Weiss <aaronweiss@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Talha Pathan <tpathan@roblox.com>
Co-authored-by: Varun Saini <vsaini@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2025-01-24 12:15:19 -08:00
vegorov-rbx
6061a14e9f
Fixup desync between internal and external codebases (#1622)
Some checks failed
benchmark / callgrind (map[branch:main name:luau-lang/benchmark-data], ubuntu-22.04) (push) Has been cancelled
build / macos (push) Has been cancelled
build / macos-arm (push) Has been cancelled
build / ubuntu (push) Has been cancelled
build / windows (Win32) (push) Has been cancelled
build / windows (x64) (push) Has been cancelled
build / coverage (push) Has been cancelled
build / web (push) Has been cancelled
release / macos (push) Has been cancelled
release / ubuntu (push) Has been cancelled
release / windows (push) Has been cancelled
release / web (push) Has been cancelled
During conflict resolution while transferring patches back and forth,
some conflict resolutions may cause differences that are harder to spot.

This process would have also cleaned up differences noted in
https://github.com/luau-lang/luau/pull/1621
2025-01-20 08:05:58 -08:00
Arseny Kapoulkine
82c9383a39
Fix math.map/math.lerp merge fallout (#1621)
Some checks are pending
benchmark / callgrind (map[branch:main name:luau-lang/benchmark-data], ubuntu-22.04) (push) Waiting to run
build / macos (push) Waiting to run
build / macos-arm (push) Waiting to run
build / ubuntu (push) Waiting to run
build / windows (Win32) (push) Waiting to run
build / windows (x64) (push) Waiting to run
build / coverage (push) Waiting to run
build / web (push) Waiting to run
release / macos (push) Waiting to run
release / ubuntu (push) Waiting to run
release / windows (push) Waiting to run
release / web (push) Waiting to run
- LuauMathMap flag was not removed but the uses of it were
- LuauMathLerp addition to math table was duplicated
2025-01-19 14:14:12 -08:00
Varun Saini
29047504da
Sync to upstream/release/657 (#1619)
Some checks failed
benchmark / callgrind (map[branch:main name:luau-lang/benchmark-data], ubuntu-22.04) (push) Has been cancelled
build / macos (push) Has been cancelled
build / macos-arm (push) Has been cancelled
build / ubuntu (push) Has been cancelled
build / windows (Win32) (push) Has been cancelled
build / windows (x64) (push) Has been cancelled
build / coverage (push) Has been cancelled
build / web (push) Has been cancelled
release / macos (push) Has been cancelled
release / ubuntu (push) Has been cancelled
release / windows (push) Has been cancelled
release / web (push) Has been cancelled
## General
- Fix a parsing bug related to the starting position of function names.
- Rename Luau's `Table` struct to `LuaTable`.

## New Solver
- Add support for generics in user-defined type functions
([RFC](https://rfcs.luau.org/support-for-generic-function-types-in-user-defined-type-functions.html)).
- Provide a definition of `math.lerp` to the typechecker.
- Implement error suppression in `string.format`.
  - Fixes #1587.
- Ensure function call discriminant types are always filled when
resolving `FunctionCallConstraint`.

---

Co-authored-by: Ariel Weiss <aaronweiss@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Talha Pathan <tpathan@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2025-01-17 14:55:39 -08:00
Petri Häkkinen
67e9d85124
Add 2-component vector constructor (#1569)
Some checks are pending
benchmark / callgrind (map[branch:main name:luau-lang/benchmark-data], ubuntu-22.04) (push) Waiting to run
build / macos (push) Waiting to run
build / macos-arm (push) Waiting to run
build / ubuntu (push) Waiting to run
build / windows (Win32) (push) Waiting to run
build / windows (x64) (push) Waiting to run
build / coverage (push) Waiting to run
build / web (push) Waiting to run
release / macos (push) Waiting to run
release / ubuntu (push) Waiting to run
release / windows (push) Waiting to run
release / web (push) Waiting to run
Implement RFC: 2-component vector constructor. This includes 2-component
overload for `vector.create` and associated fastcall function, and its
type definition. These features are controlled by a new feature flag
`LuauVector2Constructor`. Additionally constant folding now supports two
components when `LuauVector2Constants` feature flag is set.

Note: this work does not include changes to CodeGen. Thus calls to
`vector.create` with only two arguments are not natively compiled
currently. This is left for future work.
2025-01-17 08:45:03 -08:00
Arseny Kapoulkine
24cacc94ed
CodeGen: Implement support for math.lerp lowering (#1609)
Some checks are pending
benchmark / callgrind (map[branch:main name:luau-lang/benchmark-data], ubuntu-22.04) (push) Waiting to run
build / macos (push) Waiting to run
build / macos-arm (push) Waiting to run
build / ubuntu (push) Waiting to run
build / windows (Win32) (push) Waiting to run
build / windows (x64) (push) Waiting to run
build / coverage (push) Waiting to run
build / web (push) Waiting to run
release / macos (push) Waiting to run
release / ubuntu (push) Waiting to run
release / windows (push) Waiting to run
release / web (push) Waiting to run
To implement math.lerp without branches, we add SELECT_NUM which
selects one of the two inputs based on the comparison condition.

For simplicity, we only support C == D for now; this can be extended to
a more generic version with a IrCondition operand E, but that requires
more work on the SSE side (to flip the comparison for some conditions
like Greater, and expose more generic vcmpsd).

Note: On AArch64 this will effectively result in a change in floating
point
behavior between native code and non-native code: clang synthesizes
fmadd (because floating point contraction is allowed by default, and the
arch always has the instruction), whereas this change will use
fmul+fadd.

I am not sure if this is good or bad, and if this is a problem in C or
not.
Specifically, clang's behavior results in different results between X64
and AArch64 when *not* using codegen, and with this change the behavior
when using codegen is... the same? :)

Fixing this will require either using LERP_NUM instead and hand-coding
lowering, or exposing some sort of "quasi" MADD_NUM (which would
lower to fma on AArch64 and mul+add on X64).

A small benefit to the current approach is `lerp(1, 5, t)`
constant-folds the
subtraction. With LERP_NUM this optimization will need to be implemented
manually as a partial constant-folding for LERP_NUM.

A similar problem exists today for vector.cross & vector.dot. So maybe
this
is not something we need to fix, unsure.
2025-01-16 10:48:27 -08:00
Hunter Goldstein
c759cd5581
Sync to upstream/release/656 (#1612)
Some checks failed
benchmark / callgrind (map[branch:main name:luau-lang/benchmark-data], ubuntu-22.04) (push) Has been cancelled
build / macos (push) Has been cancelled
build / macos-arm (push) Has been cancelled
build / ubuntu (push) Has been cancelled
build / windows (Win32) (push) Has been cancelled
build / windows (x64) (push) Has been cancelled
build / coverage (push) Has been cancelled
build / web (push) Has been cancelled
release / macos (push) Has been cancelled
release / ubuntu (push) Has been cancelled
release / windows (push) Has been cancelled
release / web (push) Has been cancelled
# General

All code has been re-formatted by `clang-format`; this is not
mechanically enforced, so Luau may go out-of-sync over the course of the
year.

# New Solver

* Track free types interior to a block of code on `Scope`, which should
reduce the number of free types that remain un-generalized after type
checking is complete (e.g.: less errors like `'a <: number is
incompatible with number`).

# Autocomplete

* Fragment autocomplete now does *not* provide suggestions within
comments (matching non-fragment autocomplete behavior).
* Autocomplete now respects iteration and recursion limits (some hangs
will now early exit with a "unification too complex error," some crashes
will now become internal complier exceptions).

# Runtime

* Add a limit to how many Luau codegen slot nodes addresses can be in
use at the same time (fixes #1605, fixes #1558).
* Added constant folding for vector arithmetic (fixes #1553).
* Added support for `buffer.readbits` and `buffer.writebits` (see:
https://github.com/luau-lang/rfcs/pull/18).

---

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2025-01-10 11:34:39 -08:00
Vyacheslav Egorov
945c510b3c Merge branch 'merge' 2025-01-10 17:59:33 +02:00
Arseny Kapoulkine
8a4ef26f89
Implement support for math.lerp (#1608)
Some checks are pending
benchmark / callgrind (map[branch:main name:luau-lang/benchmark-data], ubuntu-22.04) (push) Waiting to run
build / macos (push) Waiting to run
build / macos-arm (push) Waiting to run
build / ubuntu (push) Waiting to run
build / windows (Win32) (push) Waiting to run
build / windows (x64) (push) Waiting to run
build / coverage (push) Waiting to run
build / web (push) Waiting to run
release / macos (push) Waiting to run
release / ubuntu (push) Waiting to run
release / windows (push) Waiting to run
release / web (push) Waiting to run
This change implements math.lerp RFC with C function definition, builtin
function, builtin constant folding and tests.

The tests validate a few lerp properties by providing counter-examples
for popular lerp implementations; the testing is of course not
exhaustive, as exhaustive testing was done offline using fuzzing.

Type definitions will be updated separately.

Codegen support will be implemented separately: it requires new IR for
conditional
selects to represent the desired logic without using a branch.
2025-01-09 09:42:07 -08:00
vegorov-rbx
9a102e2aff
Fix negation type 'inner' method in user-defined type functions (#1582)
Some checks failed
benchmark / callgrind (map[branch:main name:luau-lang/benchmark-data], ubuntu-22.04) (push) Has been cancelled
build / macos (push) Has been cancelled
build / macos-arm (push) Has been cancelled
build / ubuntu (push) Has been cancelled
build / windows (Win32) (push) Has been cancelled
build / windows (x64) (push) Has been cancelled
build / coverage (push) Has been cancelled
build / web (push) Has been cancelled
release / macos (push) Has been cancelled
release / ubuntu (push) Has been cancelled
release / windows (push) Has been cancelled
release / web (push) Has been cancelled
Fixes #1580
2024-12-20 11:30:43 -08:00
aaron
8f94786ceb
Refactor CLI structure to match the include/src split that our other projects have. (#1573)
Some checks failed
benchmark / callgrind (map[branch:main name:luau-lang/benchmark-data], ubuntu-22.04) (push) Has been cancelled
build / macos (push) Has been cancelled
build / macos-arm (push) Has been cancelled
build / ubuntu (push) Has been cancelled
build / windows (Win32) (push) Has been cancelled
build / windows (x64) (push) Has been cancelled
build / coverage (push) Has been cancelled
build / web (push) Has been cancelled
release / macos (push) Has been cancelled
release / ubuntu (push) Has been cancelled
release / windows (push) Has been cancelled
release / web (push) Has been cancelled
This PR refactors the CLI folder to use the same project split between
include and src directories that we have for all the other artifacts in
luau. It also includes the require-by-string implementation we already
have as a feature of `Luau.CLI.lib`. Both of these changes are targeted
at making it easier for embedding projects to setup an effective
equivalent to the standalone `luau` executable with whatever runtime
libraries they need attached and without having to unnecessarily
duplicate code from luau itself.
2024-12-17 13:50:27 -08:00
vegorov-rbx
7ab3482003
Remove Ast dependency on CLI.Lib (#1571)
Some checks are pending
benchmark / callgrind (map[branch:main name:luau-lang/benchmark-data], ubuntu-22.04) (push) Waiting to run
build / macos (push) Waiting to run
build / macos-arm (push) Waiting to run
build / ubuntu (push) Waiting to run
build / windows (Win32) (push) Waiting to run
build / windows (x64) (push) Waiting to run
build / coverage (push) Waiting to run
build / web (push) Waiting to run
release / macos (push) Waiting to run
release / ubuntu (push) Waiting to run
release / windows (push) Waiting to run
release / web (push) Waiting to run
2024-12-16 16:40:46 -08:00
Vighnesh-V
2e6fdd90a0
Sync to upstream/release/655 (#1563)
Some checks failed
benchmark / callgrind (map[branch:main name:luau-lang/benchmark-data], ubuntu-22.04) (push) Has been cancelled
build / macos (push) Has been cancelled
build / macos-arm (push) Has been cancelled
build / ubuntu (push) Has been cancelled
build / windows (Win32) (push) Has been cancelled
build / windows (x64) (push) Has been cancelled
build / coverage (push) Has been cancelled
build / web (push) Has been cancelled
release / macos (push) Has been cancelled
release / ubuntu (push) Has been cancelled
release / windows (push) Has been cancelled
release / web (push) Has been cancelled
## New Solver
* Type functions should be able to signal whether or not irreducibility
is due to an error
* Do not generate extra expansion constraint for uninvoked user-defined
type functions
* Print in a user-defined type function reports as an error instead of
logging to stdout
* Many e-graphs bugfixes and performance improvements
* Many general bugfixes and improvements to the new solver as a whole
* Fixed issue with used-defined type functions not being able to call
each other
* Infer types of globals under new type solver

## Fragment Autocomplete
* Miscellaneous fixes to make interop with the old solver better

## Runtime
* Support disabling specific built-in functions from being fast-called
or constant-evaluated (Closes #1538)
* New compiler option `disabledBuiltins` accepts a list of library
function names like "tonumber" or "math.cos"
* Added constant folding for vector arithmetic
* Added constant propagation and type inference for vector globals
(Fixes #1511)
* New compiler option `librariesWithKnownMembers` accepts a list of
libraries for members of which a request for constant value and/or type
will be made
* `libraryMemberTypeCb` callback is called to get the type of a global,
return one of the `LuauBytecodeType` values. 'boolean', 'number',
'string' and 'vector' type are supported.
* `libraryMemberConstantCb` callback is called to setup the constant
value of a global. To set a value, C API `luau_set_compile_constant_*`
or C++ API `setCompileConstant*` functions should be used.

---
Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Daniel Angel <danielangel@roblox.com>
Co-authored-by: Jonathan Kelaty <jkelaty@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Varun Saini <vsaini@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Junseo Yoo <jyoo@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Varun Saini <61795485+vrn-sn@users.noreply.github.com>
Co-authored-by: Alexander Youngblood <ayoungblood@roblox.com>
Co-authored-by: Varun Saini <vsaini@roblox.com>
Co-authored-by: Andrew Miranti <amiranti@roblox.com>
Co-authored-by: Shiqi Ai <sai@roblox.com>
Co-authored-by: Yohoo Lin <yohoo@roblox.com>
Co-authored-by: Daniel Angel <danielangel@roblox.com>
Co-authored-by: Jonathan Kelaty <jkelaty@roblox.com>
2024-12-13 13:02:30 -08:00
Vighnesh
87eac7befa Merge branch 'upstream' into merge 2024-12-13 11:21:49 -08:00
Vighnesh
79cdfe1094 Merge branch 'master' into merge 2024-12-13 11:21:40 -08:00
Vighnesh
906a00d498 Sync to upstream/release/655
* General
- Fix the benchmark require wrapper function to work in Lua
- Fix memory leak in the new Luau C API test

* New Solver
- Luau: type functions should be able to signal whether or not irreducibility is due to an error
- Do not generate extra expansion constraint for uninvoked user-defined type functions
- Print in a user-defined type function should be reported as an error
instead of logging to stdout
- Many e-graphs bugfixes and performance improvements
- Many general bugfixes and improvements to the new solver as a whole
- Fixed issue with Luau used-defined type functions not having all environments initialized
- Infer types of globals under new type solver

* Fragment Autocomplete
- Miscellaneous fixes to make interop with the old solver better

* Runtime
- Support disabling specific Luau built-in functions from being
fast-called or constant-evaluated
- Added constant folding for vector arithmetic
- Added constant propagation and type inference for Vector3 globals

----------------------------------------------------------
9 contributors:

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Daniel Angel <danielangel@roblox.com>
Co-authored-by: Jonathan Kelaty <jkelaty@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Varun Saini <vsaini@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-12-13 11:20:43 -08:00
jkelaty-rbx
8b8118b027
Convert Luau heap dumps to Chrome heap snapshots (#1554)
Adds a script for (approximately) converting Luau heap dumps to Chrome
heap snapshots. Useful for visually inspecting a heap dump within
Chrome's UI.
2024-12-06 10:04:57 -08:00
Aviral Goel
d0222bb554
Sync to upstream/release/654 (#1552)
# What's Changed

* Support dead store elimination for `STORE_VECTOR` instruction
* Fix parser hang when a separator is used between Luau class
declaration properties
* Provide properties and metatable for built-in vector type definition
to fix type errors
* Fix Fragment Autocomplete to ensure correct parentheses insertion
behavior.
* Add support for 'thread' and 'buffer' primitive types in user-defined
type functions

---------

Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-12-02 16:16:33 -08:00
Aviral Goel
dfdcff0897 Merge branch 'upstream' into merge 2024-12-02 15:11:46 -08:00
Aviral Goel
341aa38768 Merge branch 'master' into merge 2024-12-02 15:04:30 -08:00
Aviral Goel
230ab81326 Sync to upstream/release/654
* Luau: support dead store elimination for STORE_VECTOR instruction
* Fixed hang when Luau class declaration props are incorrectly separated
* Provide properties and a metatable for Luau built-in vector type
* Pick the correct global scope based on the solver
* Conversational AI gets all required scripts as context
* Clip LuauRequireCyclesDontAlwaysReturnAny
* Fix Parentheses in Fragment Autocomplete
* Remove write-only locals in `Luau::getDocumentOffsets`
* The lexer can resume parsing from any arbitrary position
* Added support for 'thread' and 'buffer' primitive types in Luau user-defined type functions

---------

Co-authored-by: Andrew Miranti <amiranti@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Shiqi Ai <sai@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Yohoo Lin <yohoo@roblox.com>
2024-12-02 15:02:24 -08:00
ramdoys
8cc289fae4
Replace parser test getParseError function for matchParseError (#1532)
Removes the getParseError function in Parser.test.cpp to use
matchParseError instead.
2024-11-27 07:34:14 -08:00
Arseny Kapoulkine
8f2ab4cbad
Minor tweak to FASTCALL3 instruction (#1548)
In all other places, L->top is extracted to a local when writing to
stack; this helps compilers without TBAA (MSVC) to not reload L->top
redundantly.

Also assert that we do in fact have 2 slots of stack space (which we
do).
2024-11-27 07:07:37 -08:00
Arseny Kapoulkine
b5801d3377
CodeGen: Optimize arithmetics for basic identities (#1545)
This change folds:

	a * 1 => a
	a / 1 => a
	a * -1 => -a
	a / -1 => -a
	a * 2 => a + a
	a / 2^k => a * 2^-k
	a - 0 => a
	a + (-0) => a

Note that the following folds are all invalid:

	a + 0 => a (breaks for negative zero)
	a - (-0) => a (breaks for negative zero)
	a - a => 0 (breaks for Inf/NaN)
	0 - a => -a (breaks for negative zero)

Various cases of UNM_NUM could be optimized (eg (-a) * (-b) = a * b),
but that doesn't happen in benchmarks.

While it would be possible to also fold inverse multiplications (k * v),
these do not happen in benchmarks and rarely happen in bytecode due
to type based optimizations. Maybe this can be improved with some sort
of
IR canonicalization in the future if necessary.

I've considered moving some of these, like division strength reduction,
to IR translation (as this is where POW is lowered presently) but it
didn't
seem better one way or the other.

This change improves performance on some benchmarks, e.g. trig and
voxelgen,
and should be a strict uplift as it never generates more instructions or
longer
latency chains. On Apple M2, without division->multiplication
optimization, both
benchmarks see 0.1-0.2% uplift. Division optimization makes trig 3%
faster; I expect
the gains on X64 will be more muted, but on Apple this seems to allow
loop iterations
to overlap better by removing the division bottleneck.
2024-11-27 04:44:39 -08:00
ayoungbloodrbx
d19a5f0699
Sync to upstream/release/653 (#1541)
## What's Changed?

* Optimized the vector dot product by up to 24%
* Allow for x/y/z/X/Y/Z vector field access by registering a `vector`
metatable
with an `__index` method (Fixes #1521)
* Fixed a bug preventing consistent recovery from parse errors in table
types.
* Optimized `k*n` and `k+n` when types are known
* Allow fragment autocomplete to handle cases like the automatic
insertion of
parens, keywords, strings, etc., while maintaining a correct relative
positioning

### New Solver

* Allow for `nil` assignment to tables and classes with indexers

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Varun Saini <vsaini@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-11-22 13:00:51 -08:00
Alexander Youngblood
4fa6e97caa Merge branch 'upstream' into merge 2024-11-22 12:39:19 -08:00
Alexander Youngblood
0bd9321957 Sync to upstream/release/653
## What's Changed?

* Optimized the vector dot product by up to 24%
* Allow for x/y/z/X/Y/Z vector field access by registering a `vector` metatable
with an `__index` method
* Fixed a bug preventing consistent recovery from parse errors in table types.
* Optimized `k*n` and `k+n` when types are known
* Allow fragment autocomplete to handle cases like the automatic insertion of
parens, keywords, strings, etc., while maintaining a correct relative positioning

 ### New Solver

* Added support for 'thread' and 'buffer' primitive types in Luau user-defined
type functions
* Allow for `nil` assignment to tables and classes with indexers

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Varun Saini <vsaini@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-11-22 12:37:17 -08:00
Alexander Youngblood
dd7a19d8ea Merge branch 'master' into merge 2024-11-22 11:42:42 -08:00
vegorov-rbx
7a6142e792
Update vector-math benchmark name to correctly display (#1539)
While we could update the `awk` regular expression, we can just make the
test name compatible.
2024-11-19 16:13:52 -08:00
Arseny Kapoulkine
b1b21f395a
Compiler: Optimize k*n and k+n when types are known (#1529)
When type information is specified, we can compile k*n and k+n into
MULK/ADDK forms that are faster to execute, as long as we think n is a
number. Since we generally restrict type aware optimizations to O2, this
does that as well.

This makes trig benchmark ~4% faster on Apple M2 in VM, and also a tiny
improvement on scimark (~0.1%) can be observed. The optimization only
affects interpreted execution, as NCG already can synthesize optimal
code here.

If the type information is not truthful (e.g. user annotates type as a
number and it's not), the worst case scenario is flipped arguments to
metamethods like __add/__mul for constant left hand side.

Fixes #626 (the fix requires type information or NCG but I doubt any
further work on this is warranted)

---------

Co-authored-by: vegorov-rbx <75688451+vegorov-rbx@users.noreply.github.com>
2024-11-19 07:42:44 -08:00
Varun Saini
c2e4ee0203
Fix benchmark runner bug introduced in release 0.652 (#1530)
### Problem

In release 0.652, `RequireResolver` was refactored to add support for
`luau-analyze`.

As part of this update, `RuntimeRequireContext` introduced a new
convention where a file's chunkname must be prefixed with `@` (e.g.,
`@./some/path.luau`). This change applies to all chunknames generated
within `RuntimeRequireContext`. However, when a `.luau` file is executed
directly from the command line (e.g., `luau ./my/script.luau`), the
chunkname is still generated with the old `=` prefix (e.g.,
`=./some/path.luau`).

Since `RuntimeRequireContext` no longer recognizes chunknames prefixed
with `=`, any attempt to directly execute a `.luau` file from the
command line fails. For example, running `luau ./my/script.luau` results
in an error stating that the context is unsupported. [This issue also
affects tools like the benchmark
runner](https://github.com/luau-lang/luau/pull/1525#issuecomment-2480454018),
which rely on direct file execution.

### Solution

Update `runFile` to replace the `=` prefix in generated chunknames with
`@`.
2024-11-18 04:20:05 -08:00
Varun Saini
e905e30570
Sync to upstream/release/652 (#1525)
## What's new?

* Add support for mixed-mode type checking, which allows modules checked
in the old type solver to be checked and autocompleted by the new one.
* Generalize `RequireResolver` to support require-by-string semantics in
`luau-analyze`.
* Fix a bug in incremental autocomplete where `DefId`s associated with
index expressions were not correctly picked up.
* Fix a bug that prevented "complex" types in generic parameters (for
example, `local x: X<(() -> ())?>`).

### Issues fixed
* #1507
* #1518

---

Internal Contributors:

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Varun Saini <vsaini@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
2024-11-15 14:29:30 -08:00
Varun Saini
4709fec28c Parser.test.cpp: auto (int) -> size_t 2024-11-15 13:15:39 -08:00
Varun Saini
ebd074803f Merge branch 'upstream' into merge 2024-11-15 11:58:26 -08:00
Varun Saini
3c87474398 Merge branch 'master' into merge 2024-11-15 11:51:19 -08:00
Varun Saini
f6f4d92107 Sync to upstream/release/652 2024-11-15 11:37:29 -08:00
Varun Saini
d1025d0029
Remove noexcepts from Config (#1523)
Fixes https://github.com/luau-lang/luau/issues/1515.

By removing these `noexcept`s, we guarantee that the internal call to
`std::swap` uses move semantics when a `Config` is copy-assigned.
2024-11-12 14:25:04 -08:00
Arseny Kapoulkine
53e6e4b8f0
Fix mesh-normal-vector benchmark array access (#1514)
mesh-normal-scalar correctly fills sequential values in the output for
triangle cone function, but mesh-normal-vector accidentally reuses the
loop index, which results in writes to every third index of the array
(1, 4, etc.).

This is both slower (as the table turns into a hash map), and incorrect,
especially as we have a scalar version of the benchmark that does the
right thing.

Note: there's a bunch of inefficiencies in the benchmark code that I
have not fixed (around field access mostly, e.g. writing to `v.n` and
then immediately reading it again). These are not ideal for performance,
but they can be valuable to keep as is because this redundancy is common
in real-world code, and it would be nice to see codegen optimizations
eliminating most of that overhead. This one, however, is a straight up
bug, and sparse arrays should not really be the thing this benchmark
hits.
2024-11-11 12:39:09 -08:00
Arseny Kapoulkine
e6bf71871a
CodeGen: Rewrite dot product lowering using a dedicated IR instruction (#1512)
Instead of doing the dot product related math in scalar IR, we lift the
computation into a dedicated IR instruction.

On x64, we can use VDPPS which was more or less tailor made for this
purpose. This is better than manual scalar lowering that requires
reloading components from memory; it's not always a strict improvement
over the shuffle+add version (which we never had), but this can now be
adjusted in the IR lowering in an optimal fashion (maybe even based on
CPU vendor, although that'd create issues for offline compilation).

On A64, we can either use naive adds or paired adds, as there is no
dedicated vector-wide horizontal instruction until SVE. Both run at
about the same performance on M2, but paired adds require fewer
instructions and temporaries.

I've measured this using mesh-normal-vector benchmark, changing the
benchmark to just report the time of the second loop inside
`calculate_normals`, testing master vs #1504 vs this PR, also increasing
the grid size to 400 for more stable timings.

On Zen 4 (7950X), this PR is comfortably ~8% faster vs master, while I
see neutral to negative results in #1504.
On M2 (base), this PR is ~28% faster vs master, while #1504 is only
about ~10% faster.

If I measure the second loop in `calculate_tangent_space` instead, I
get:

On Zen 4 (7950X), this PR is ~12% faster vs master, while #1504 is ~3%
faster
On M2 (base), this PR is ~24% faster vs master, while #1504 is only
about ~13% faster.

Note that the loops in question are not quite optimal, as they store and
reload various vectors to dictionary values due to inappropriate use of
locals. The underlying gains in individual functions are thus larger
than the numbers above; for example, changing the `calculate_normals`
loop to use a local variable to store the normalized vector (but still
saving the result to dictionary value), I get a ~24% performance
increase from this PR on Zen4 vs master instead of just 8% (#1504 is
~15% slower in this setup).
2024-11-08 16:23:09 -08:00
Hunter Goldstein
a36a3c41cc
Sync to upstream/release/651 (#1513)
### What's New?

* Fragment Autocomplete: a new API allows for type checking a small
fragment of code against an existing file, significantly speeding up
autocomplete performance in large files.

### New Solver

* E-Graphs have landed: this is an ongoing approach to make the new type
solver simplify types in a more consistent and principled manner, based
on similar work (see: https://egraphs-good.github.io/).
* Adds support for exporting / local user type functions (previously
they were always exported).
* Fixes a set of bugs in which the new solver will fail to complete
inference for simple expressions with just literals and operators.

### General Updates
* Requiring a path with a ".lua" or ".luau" extension will now have a
bespoke error suggesting to remove said extension.
* Fixes a bug in which whether two `Luau::Symbol`s are equal depends on
whether the new solver is enabled.

---

Internal Contributors:

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Varun Saini <vsaini@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-11-08 13:41:45 -08:00
Hunter Goldstein
9dc829b584 Use size_t in getDocumentOffsets 2024-11-08 12:32:51 -08:00
Hunter Goldstein
df67e4d62c Add headers missing when compiling with GCC 2024-11-08 11:56:37 -08:00
Hunter Goldstein
af9d9ba13e Merge branch 'upstream' into merge 2024-11-08 11:35:18 -08:00
Hunter Goldstein
4399b17f95 Merge branch 'master' into merge 2024-11-08 11:33:48 -08:00
Hunter Goldstein
c799a548e4 Sync to version/652
> What's new?

* Fragment Autocomplete: a new API allows for type checking a small
  fragment of code against an existing file, significantly speeding up
  autocomplete performance in large files.

> New Solver

* E-Graphs have landed: this is an ongoing approach to make the new type solver
  simplify types in a more consistent and principled manner, based on
  similar work (e.g.: https://egraphs-good.github.io/).
* Adds support for exported / local user type functions.
* Fixes a set of bugs in which the new solver will fail to complete
  inference for simple expressions with just literals and operators.

> General

* It is now an explicit runtime error to `require` a path with a ".lua" or
  ".luau" extension, and the error message will suggest removing the extension.
  ```
  require("path/to/mymodule.lua")
  ```
* Fixes a bug in which whether two `Symbol`s are equal depends on
  whether the new solver is enabled.
2024-11-08 11:01:20 -08:00
Barış
26b2307a8b
Replace old site urls (#1505)
this PR replaces all the old site urls from luau-lang.org to luau.org
2024-11-06 15:23:33 -08:00
aaron
47543e5df1
Set the defining module even when the new solver cloned the type. (#1506)
Follow up to #1495: a small fixup for the defining module and location
to get set even when cloning was required.
2024-11-05 15:25:38 -08:00
checkraisefold
f1d4621d59
Pre-populate/duplicate check class definitions (new solver) (#1493)
Closes #1492
Tested and working with the test case in the aforementioned issue, along
with the full defs of luau-lsp with no issues or type errors

In normal Luau files, you can use type aliases and type functions before
they are declared. The same extends to declaration files, **except** in
the new solver. The old solver perfectly allows this, and in fact
intentionally adds it:
db809395bf/Analysis/src/TypeInfer.cpp (L1711-L1717)

This causes *much* headache and pain for external projects that make use
of declaration files; namely, luau-lsp generates them from MaximumADHD's
API dump, which is not ordered by dependency. This means silent
error-types popping up everywhere because types are used before they are
declared. The workaround would be to make code to manually reorder class
definitions based on their dependencies with a bunch of code, but this
is clearly not ideal, and won't work for classes dependent on each
other/recursive.

The solution used here is the same as is used for type aliases - the
name binding for the class is given a blocked type before running the
rest of constraint generation on the block. Questions remain:
- Should the logic be split off of `checkAliases`?
- Should a bound type be used, or should the (blocked) binding type be
directly emplaced with the class type? What are the ramifications of
emplacing with the bound versus the raw type? One ramification was
initially ran into through an assertion because the class
`superTy`/`parent` was bound, and several pieces of code assume it is
not, so it had to be made followed.
- Is folllowing `superTy` to set `parent` the correct workaround for the
assertions thrown, or should the code expecting `parent` to be a
ClassType without following it be modified instead to follow `parent`?
- Should `scope->privateTypeBindings` also be checked for the duplicate
error? I would presume so, since having a class with the same name as a
private alias or type function should error as well?

The extraneous whitespace changes are clang-format ones done
automatically that should've been done in the last release - I can
remove them if necessary and let another sync or OSS cleanup commit fix
it.
2024-11-05 15:21:18 -08:00
checkraisefold
9a4bc6aeb8
Fix definition module name & location (#1495)
Closes #1441 

Brings behavior to parity with the old solver by filling in
definitionLocation and definitionModuleName for Luau-consuming
programs/libraries to use.
2024-11-05 10:33:21 -08:00
Andy Friesen
a251bc68a2
Sync to upstream/release/650 (#1502)
* New `vector` library! See https://rfcs.luau.org/vector-library.html
for details
* Replace the use of non-portable `strnlen` with `memchr`. `strnlen` is
not part of any C or C++ standard.
* Introduce `lua_newuserdatataggedwithmetatable` for faster tagged
userdata creation of userdata with metatables registered with
`lua_setuserdatametatable`

Old Solver

* It used to be the case that a module's result type would
unconditionally be inferred to be `any` if it imported any module that
participates in any import cycle. This is now fixed.

New Solver

* Improve inference of `table.freeze`: We now infer read-only properties
on tables after they have been frozen.
* We now correctly flag cases where `string.format` is called with 0
arguments.
* Fix a bug in user-defined type functions where table properties could
be lost if the table had a metatable
* Reset the random number seed for each evaluation of a type function
* We now retry subtyping arguments if it failed due to hidden variadics.

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Junseo Yoo <jyoo@roblox.com>
2024-11-01 12:06:07 -07:00
Andy Friesen
c6cf1f829a Merge branch 'upstream' into merge 2024-11-01 09:47:58 -07:00
Andy Friesen
25de210308 Merge branch 'master' into merge 2024-11-01 09:47:55 -07:00
Andy Friesen
ee5b473b86 Sync to upstream/release/650 2024-11-01 09:47:10 -07:00
aaron
db809395bf
Sync to upstream/release/649 (#1489) 2024-10-25 16:15:01 -04:00
Aaron Weiss
e85fb91cfd Merge branch 'upstream' into merge 2024-10-25 09:46:45 -07:00
Aaron Weiss
ed05da573a Merge branch 'master' into merge 2024-10-25 09:46:40 -07:00
Aaron Weiss
1de169f006 Sync to upstream/release/649 2024-10-25 09:46:08 -07:00
vegorov-rbx
e491128f95
Sync to upstream/release/648 (#1477)
## What's new

* Added `math.map` function to the standard library, based on
https://rfcs.luau-lang.org/function-math-map.html
* `FileResolver` can provide an implementation of
`getRequireSuggestions` to provide auto-complete suggestions for
require-by-string

## New Solver

* In user-defined type functions, `readproperty` and `writeproperty`
will return `nil` instead of erroring if property is not found
* Fixed incorrect scope of variadic arguments in the data-flow graph
* Fixed multiple assertion failures

---

Internal Contributors:

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Varun Saini <vsaini@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-10-18 10:27:15 -07:00
Vyacheslav Egorov
ce9f1eb905 Merge branch 'upstream' into merge 2024-10-18 18:17:08 +03:00
Vyacheslav Egorov
bbd18bf0bd Merge branch 'master' into merge 2024-10-18 18:17:04 +03:00
Vyacheslav Egorov
47e3123863 Sync to upstream/release/648 2024-10-18 18:08:01 +03:00
Vighnesh-V
d7842e08ae
OSS cleanup - Sync/upstream/647 introduced unecessary new lines in Differ.cpp - this cleans it up (#1472) 2024-10-14 10:33:24 -07:00
Vighnesh-V
77295c3610
Sync to upstream/release/647 (#1469)
# General Updates
Fix an old solver crash that occurs in the presence of cyclic
`requires()`

## New Solver
- Improvements to Luau user-defined type function library
- Avoid asserting on unexpected metatable types
- Properties in user defined type functions should have a consistent
iteration order - in this case it is insertion ordering

# Runtime
- Track VM allocations for telemetry

---
Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: James McNellis <jmcnellis@roblox.com>
Co-authored-by: Varun Saini <vsaini@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Junseo Yoo <jyoo@roblox.com>
2024-10-11 17:48:30 -07:00
Vighnesh
fb8c190849 replicate changes to cmakelists 2024-10-11 17:16:39 -07:00
Vighnesh
268e0b2ab7 revert differ.h change 2024-10-11 17:13:33 -07:00
Vighnesh
837bba31e4 revert cmake file + update makefile 2024-10-11 16:57:52 -07:00
Vighnesh
5d74685798 unconditially add suppressing flag 2024-10-11 16:54:35 -07:00
Vighnesh
a7324ce8fb what happens if we remove the flag 2024-10-11 16:50:14 -07:00
Vighnesh
03c9fa721e try again? 2024-10-11 16:47:27 -07:00
Vighnesh
b5d3544d18 update build flags 2024-10-11 16:39:31 -07:00
Vighnesh
0276d18314 one more try 2024-10-11 16:30:15 -07:00
Vighnesh
b1fae55636 nit differ 2024-10-11 16:02:48 -07:00
Vighnesh
50e8dd0d71 more macro crimes 2024-10-11 15:47:58 -07:00
Vighnesh
dafee8bbef nest again 2024-10-11 15:37:54 -07:00
Vighnesh
c9ff5d1c0a nest pragma 2024-10-11 15:28:51 -07:00
Vighnesh
848fa0be8e insert new lines 2024-10-11 15:23:33 -07:00
Vighnesh
b76941b678 annotate this optional use 2024-10-11 15:21:25 -07:00
Vighnesh
507832b1f0 default initialize 2024-10-11 15:05:31 -07:00
Vighnesh
68b9214b03 push same change to shared code allocator 2024-10-11 14:51:28 -07:00
Vighnesh
a197f044b5 i messed up the macro syntax again 2024-10-11 14:49:34 -07:00
Vighnesh
ef95ce5266 newline 2024-10-11 14:40:12 -07:00
Vighnesh
39899ade4f macro out the failing tests 2024-10-11 14:33:15 -07:00
Vighnesh-V
34801b9310
Delete tests/.#TypeVar.test.cpp 2024-10-11 14:17:53 -07:00
Vighnesh
70d5a0ede8 newline 2024-10-11 14:16:41 -07:00
Vighnesh
74c1ac33a9 nit - insert new lines 2024-10-11 13:53:08 -07:00
Vighnesh
ee6a45b13b more macro madness 2024-10-11 13:46:54 -07:00
Vighnesh
69776f6fc1 re-add assertion 2024-10-11 13:39:14 -07:00
Vighnesh
f52fe9f351 nit - remove shared code allocator test section that tests self move 2024-10-11 13:22:20 -07:00
Vighnesh
a02bee5acc update -Wmaybe-uninitialized 2024-10-11 13:09:26 -07:00
Vighnesh
5bb24aa888 nit: turn int osize, nsize into size_t 2024-10-11 13:01:31 -07:00
Vighnesh
38d76e658d disable maybe uninitialized error 2024-10-11 12:56:47 -07:00
Vighnesh
0e1504fa52 try a different syntax for disabling warnings 2024-10-11 12:13:24 -07:00
Vighnesh
afa4fa601e try to disable self-move warning 2024-10-11 12:04:28 -07:00
Vighnesh
912798e5c7 Merge branch 'upstream' into merge 2024-10-11 10:05:43 -07:00
Vighnesh
40a054abe8 Merge branch 'master' into merge 2024-10-11 10:02:42 -07:00
Vighnesh
aa2e5c096d Sync to upstream/release/647 2024-10-11 09:38:27 -07:00
Micah
4559ef26b2
Support function attributes in luau-ast (#1466)
Noticed while using luau-ast that function attributes aren't included in
the output. This PR corrects that.
---------

Co-authored-by: vegorov-rbx <75688451+vegorov-rbx@users.noreply.github.com>
2024-10-08 06:57:41 -07:00
Micah
ae7b07d60f
Rename type field of AstStatTypeAlias in JSON Encoder (#1461)
Closes #1460.

This renames the `type` field of `AstStatTypeAlias` to `value` during
the JSON encoding process.

I've chosen to just rename the field in the JSON encoder rather than
rename the actual field since it's a lot further reaching. Another
option would have been to rename what the actual type of an AST node is
written to be something like `tokenType` instead of `type`, but that's a
bigger diff and technically breaking (as opposed to this one which
isn't!)
2024-10-04 16:00:25 -07:00
Andy Friesen
543de6e939
Sync to upstream/release/646 (#1458)
# General Updates

* Fix some cases where documentation symbols would not be available when
mouseovering at certain positions in the code
* Scaffolding to help embedders have more control over how `typeof(x)`
refines types
* Refinements to require-by-string semantics. See
https://github.com/luau-lang/rfcs/pull/56 for details.
* Fix for https://github.com/luau-lang/luau/issues/1405

# New Solver

* Fix many crashes (thanks you for your bug reports!)
* Type functions can now call each other
* Type functions all evaluate in a single VM. This should improve
typechecking performance and reduce memory use.
* `export type function` is now forbidden and fails with a clear error
message
* Type functions that access locals in the surrounding environment are
now properly a parse error
* You can now use `:setindexer(types.never, types.never)` to delete an
indexer from a table type.

# Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Varun Saini <vsaini@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-10-04 11:29:55 -07:00
Andy Friesen
aabaf01484 Merge branch 'upstream' into merge 2024-10-04 09:46:59 -07:00
Andy Friesen
469ba7712f Merge branch 'master' into merge 2024-10-04 09:46:55 -07:00
Andy Friesen
8531df04c9 Sync to upstream/release/646 2024-10-04 09:42:22 -07:00
aaron
02241b6d24
Sync to upstream/release/645 (#1440)
In this update, we continue to improve the overall stability of the new
type solver. We're also shipping some early bits of two new features,
one of the language and one of the analysis API: user-defined type
functions and an incremental typechecking API.

If you use the new solver and want to use all new fixes included in this
release, you have to reference an additional Luau flag:
```c++
LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease)
```
And set its value to `645`:
```c++
DFInt::LuauTypeSolverRelease.value = 645; // Or a higher value for future updates
```

## New Solver

* Fix a crash where scopes are incorrectly accessed cross-module after
they've been deallocated by appropriately zeroing out associated scope
pointers for free types, generic types, table types, etc.
* Fix a crash where we were incorrectly caching results for bound types
in generalization.
* Eliminated some unnecessary intermediate allocations in the constraint
solver and type function infrastructure.
* Built some initial groundwork for an incremental typecheck API for use
by language servers.
* Built an initial technical preview for [user-defined type
functions](https://rfcs.luau-lang.org/user-defined-type-functions.html),
more work still to come (including calling type functions from other
type functions), but adventurous folks wanting to experiment with it can
try it out by enabling `FFlag::LuauUserDefinedTypeFunctionsSyntax` and
`FFlag::LuauUserDefinedTypeFunction` in their local environment. Special
thanks to @joonyoo181 who built up all the initial infrastructure for
this during his internship!

## Miscellaneous changes

* Fix a compilation error on Ubuntu (fixes #1437)

---

Internal Contributors:

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Jeremy Yoo <jyoo@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Junseo Yoo <jyoo@roblox.com>
2024-09-27 11:58:21 -07:00
Aaron Weiss
d7bbe3ff04 fix cmake for user-defined type functions 2024-09-27 10:49:00 -07:00
Aaron Weiss
a3da985449 Merge branch 'upstream' into merge 2024-09-27 10:12:18 -07:00
Aaron Weiss
e8880490d2 Merge branch 'master' into merge 2024-09-27 10:12:15 -07:00
Aaron Weiss
7a7521a7ab Sync to upstream/release/645 2024-09-27 10:11:46 -07:00
aaron
c188715605
Include luau-ast in the releases (#1439) 2024-09-25 16:36:12 -04:00
vegorov-rbx
946a097e93
Update benchmark.yml to run new solver using the new flag (#1433) 2024-09-20 10:39:39 -07:00
vegorov-rbx
f5dabc2998
Sync to upstream/release/644 (#1432)
In this update we improve overall stability of the new type solver and
address some type inference issues with it.

If you use the new solver and want to use all new fixes included in this
release, you have to reference an additional Luau flag:
```c++
LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease)
```
And set its value to `644`:
```c++
DFInt::LuauTypeSolverRelease.value = 644; // Or a higher value for future updates
```

## New Solver
* Fixed a debug assertion failure in autocomplete (Fixes #1391)
* Fixed type function distribution issue which transformed `len<>` and
`unm<>` into `not<>` (Fixes #1416)
* Placed a limit on the possible normalized table intersection size as a
temporary measure to avoid hangs and out-of-memory issues for complex
type refinements
* Internal recursion limits are now respected in the subtyping
operations and in autocomplete, to avoid stack overflow crashes
* Fixed false positive errors on assignments to tables whose indexers
are unions of strings
* Fixed memory corruption crashes in subtyping of generic types
containing other generic types in their bounds

---

Internal Contributors:

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-09-20 09:53:26 -07:00
Vyacheslav Egorov
45594a571d Fixed the issue 2024-09-20 19:42:14 +03:00
Vyacheslav Egorov
48f3c85cba Maybe more? 2024-09-20 19:11:51 +03:00
Vyacheslav Egorov
0106dfaffa Maybe reduce the base 2024-09-20 18:58:46 +03:00
Vyacheslav Egorov
9655780bd3 Reduce it even more 2024-09-20 18:48:51 +03:00
Vyacheslav Egorov
66ba3608ca Smaller limit under ASAN for autocomplete_subtyping_recursion_limit 2024-09-20 18:43:56 +03:00
Vyacheslav Egorov
0d6b70b80b Merge branch 'upstream' into merge 2024-09-20 17:38:03 +03:00
Vyacheslav Egorov
d66e088af8 Merge branch 'master' into merge 2024-09-20 17:29:25 +03:00
Vyacheslav Egorov
a45eb2c9e0 Sync to upstream/release/644 2024-09-20 17:14:29 +03:00
Vighnesh-V
e8a7acb802
Fix syntax error in release.yml file (#1411)
Validated this with yamllint.
2024-09-13 13:26:35 -07:00
Vighnesh-V
cd1803e35e
upgrade release.yml to upload-artifact@v4 (#1410)
Github has deprecated `v1` and `v2` of `actions/upload-artifact` which
causes occassional CI failures and will affect our ability to make new
releases in the future. This PR updates the version used in
`release.yml`.
2024-09-13 13:09:42 -07:00
Vighnesh-V
b765d7bafd
Sync to upstream/release/643 (#1408)
Pretty small release this week as well.

## New Solver
* We now unconditionally generalize functions with explicit generics
* Bugfixes for how we run builtin tests

## VM
* Fixed running Luau conformance tests in LUA_VECTOR_SIZE == 4
configuration

Internal Contributors:

Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: James McNellis <jmcnellis@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Junseo Yoo <jyoo@roblox.com>
2024-09-13 11:48:25 -07:00
karl-police
f2488bdfa4
Fix crash with the index type function, where it would stack overflow due to not waiting for a pending-expansion (#1407)
Fix for https://github.com/luau-lang/luau/issues/1406

While it is good to let ``index`` wait for the pending-expansion. To
re-produce the issue you need more than just this code:
https://i.imgur.com/b3OmUGF.png

It needs this, else it won't crash.

```lua
local function ProblemCauser(key: Keys, value)
    PlayerData[key] = value
end
```

&nbsp;

But regarding "pending things", I'd recommend **generalized functions**
for sanity checks like these, since there will be more cases of similar
issues I believe. But I am 100% sure that eventually this issue here can
maybe be prevented if looking at the Constraints. _(And optimization)_
Not sure if ``index`` needs the table fully completed, or if it is
preferred that the info is available based on **how much info is
available at the current position in the code**.

But if this gets done, I hope that they'll be connected to the Solver
Logger, because I actually refined mine with colors and more info _(yet
need to finish that)_ to understand the Luau Source Code more and to
debug issues.

---------

Co-authored-by: aaron <aweiss@hey.com>
2024-09-13 10:16:30 -07:00
Vighnesh
4df4a1d408 Merge branch 'upstream' into merge 2024-09-13 10:16:25 -07:00
Vighnesh
23e9fa53cd Merge branch 'master' into merge 2024-09-13 10:16:09 -07:00
Vighnesh
cd27a20223 Sync to upstream/release/643 2024-09-13 10:14:29 -07:00
karl-police
5cdea64b7a
Add default "out" folder for CMake Project Visual Studio (#1394)
https://learn.microsoft.com/en-us/cpp/build/cmake-projects-in-visual-studio?view=msvc-170#ide-integration

Apparently ``/out`` is a default folder when opening the project as a
CMake project in Visual Studio.

&nbsp;

Could it be added to the ``.gitignore`` please.


![image](https://github.com/user-attachments/assets/10add17c-bbfc-4811-8c43-0ed2e84c5b9e)
2024-09-13 09:51:10 -07:00
karl-police
a8047b2e46
keyof - fix LUAU_ASSERT when there's only one key entry (#1388)
Fixes https://github.com/luau-lang/luau/issues/1387

Was suggested by @alexmccord 

I changed ``singletons[0]`` to ``singletons.front()``, unsure if that
makes a huge difference, and then I added the rest of the things needed
for the return type.

Maybe it's also the ideal location since doing it before looping through
``keys`` won't add the string into the type arena.

I put comments next to it based on how I thought it would make sense.

&nbsp;

``LUAU_ASSERT`` seems to trigger when there's only one entry being put
inside a UnionType. It's as if it was put there for quality.

Allow edits by maintainers is enabled.


I tested this with a quick Unit Test something like

```lua
local test: keyof<typeof({a="test"})>
```
2024-09-09 13:51:33 -07:00
Andy Friesen
d9536cecd8
Sync to upstream/release/642 (#1385)
## New Solver

* The type functions `keyof` and `index` now also walk the inheritance
chain when they are used on class types like Roblox instances.

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
2024-09-06 13:34:50 -07:00
Andy Friesen
ef8e1e3945 Merge branch 'upstream' into merge 2024-09-06 11:31:06 -07:00
Andy Friesen
538b0c071f Merge branch 'master' into merge 2024-09-06 11:31:02 -07:00
Andy Friesen
15557d8d01 Sync to upstream/release/642 2024-09-06 11:30:31 -07:00
aaron
b23d43496b
Sync to upstream/release/641 (#1382)
### What's new

* Light update this week, mostly fast flag cleanups.

### New Solver

* Rename flag to enable new solver from
`DebugLuauDeferredConstraintResolution` to `LuauSolverV2`
* Added support for magic functions for the new type checker (as opposed
to the type inference component)
* Improved handling of `string.format` with magic function improvements
* Cleaning up some of the reported errors by the new type checker
* Minor refactoring of `TypeChecker2.cpp` that happens to make the diff
very hard to read.

---

### Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Junseo Yoo <jyoo@roblox.com>
2024-08-30 13:16:51 -07:00
Aaron Weiss
f86f0a48ff Merge branch 'upstream' into merge 2024-08-30 12:29:34 -07:00
Aaron Weiss
78ae885a51 Merge branch 'master' into merge 2024-08-30 12:29:31 -07:00
Aaron Weiss
a74031bae7 Sync release to upstream/release/641 2024-08-30 12:28:44 -07:00
vegorov-rbx
d518d14b92
Sync to upstream/release/640 (#1374)
### What's new

* Fixed many of the false positive errors in indexing of table unions
and table intersections
* It is now possible to run custom checks over Luau AST during
typechecking by setting `customModuleCheck` in `FrontendOptions`
* Fixed codegen issue on arm, where number->vector cast could corrupt
that number value for the next time it's read

### New Solver

* `error` type now behaves as the bottom type during subtyping checks
* Fixed the scope that is used in subtyping with generic types
* Fixed `astOriginalCallTypes` table often used by LSP to match the old
solver

---

### Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-08-23 09:35:30 -07:00
Vyacheslav Egorov
25db8ff2cb Success, DCR mode no longer fails any tests 2024-08-23 18:00:22 +03:00
Vyacheslav Egorov
5b9344a480 Missing sync elements 2024-08-23 17:58:14 +03:00
Vyacheslav Egorov
d414b56acb Merge branch 'upstream' into merge 2024-08-23 17:28:44 +03:00
Vyacheslav Egorov
caeba5a742 Merge branch 'master' into merge 2024-08-23 17:28:36 +03:00
Vyacheslav Egorov
ebdbcb1942 Sync to upstream/release/640 2024-08-23 17:24:33 +03:00
dependabot[bot]
1dde4de793
Bump jinja2 from 3.1.3 to 3.1.4 in /tools/fuzz (#1371)
Bumps [jinja2](https://github.com/pallets/jinja) from 3.1.3 to 3.1.4.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/pallets/jinja/releases">jinja2's
releases</a>.</em></p>
<blockquote>
<h2>3.1.4</h2>
<p>This is the Jinja 3.1.4 security release, which fixes security issues
and bugs but does not otherwise change behavior and should not result in
breaking changes.</p>
<p>PyPI: <a
href="https://pypi.org/project/Jinja2/3.1.4/">https://pypi.org/project/Jinja2/3.1.4/</a>
Changes: <a
href="https://jinja.palletsprojects.com/en/3.1.x/changes/#version-3-1-4">https://jinja.palletsprojects.com/en/3.1.x/changes/#version-3-1-4</a></p>
<ul>
<li>The <code>xmlattr</code> filter does not allow keys with
<code>/</code> solidus, <code>&gt;</code> greater-than sign, or
<code>=</code> equals sign, in addition to disallowing spaces.
Regardless of any validation done by Jinja, user input should never be
used as keys to this filter, or must be separately validated first.
GHSA-h75v-3vvj-5mfj</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/pallets/jinja/blob/main/CHANGES.rst">jinja2's
changelog</a>.</em></p>
<blockquote>
<h2>Version 3.1.4</h2>
<p>Released 2024-05-05</p>
<ul>
<li>The <code>xmlattr</code> filter does not allow keys with
<code>/</code> solidus, <code>&gt;</code>
greater-than sign, or <code>=</code> equals sign, in addition to
disallowing spaces.
Regardless of any validation done by Jinja, user input should never be
used
as keys to this filter, or must be separately validated first.
:ghsa:<code>h75v-3vvj-5mfj</code></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="dd4a8b5466"><code>dd4a8b5</code></a>
release version 3.1.4</li>
<li><a
href="0668239dc6"><code>0668239</code></a>
Merge pull request from GHSA-h75v-3vvj-5mfj</li>
<li><a
href="d655030770"><code>d655030</code></a>
disallow invalid characters in keys to xmlattr filter</li>
<li><a
href="a7863ba9d3"><code>a7863ba</code></a>
add ghsa links</li>
<li><a
href="b5c98e78c2"><code>b5c98e7</code></a>
start version 3.1.4</li>
<li><a
href="da3a9f0b80"><code>da3a9f0</code></a>
update project files (<a
href="https://redirect.github.com/pallets/jinja/issues/1968">#1968</a>)</li>
<li><a
href="0ee5eb41d1"><code>0ee5eb4</code></a>
satisfy formatter, linter, and strict mypy</li>
<li><a
href="20477c6357"><code>20477c6</code></a>
update project files (<a
href="https://redirect.github.com/pallets/jinja/issues/5457">#5457</a>)</li>
<li><a
href="e491223739"><code>e491223</code></a>
update pyyaml dev dependency</li>
<li><a
href="36f98854c7"><code>36f9885</code></a>
fix pr link</li>
<li>Additional commits viewable in <a
href="https://github.com/pallets/jinja/compare/3.1.3...3.1.4">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=jinja2&package-manager=pip&previous-version=3.1.3&new-version=3.1.4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/luau-lang/luau/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-19 06:38:10 -07:00
dependabot[bot]
91ab45ff43
Bump jinja2 from 3.1.2 to 3.1.3 in /tools/fuzz (#1144)
Bumps [jinja2](https://github.com/pallets/jinja) from 3.1.2 to 3.1.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/pallets/jinja/releases">jinja2's
releases</a>.</em></p>
<blockquote>
<h2>3.1.3</h2>
<p>This is a fix release for the 3.1.x feature branch.</p>
<ul>
<li>Fix for <a
href="https://github.com/pallets/jinja/security/advisories/GHSA-h5c8-rqwp-cp95">GHSA-h5c8-rqwp-cp95</a>.
You are affected if you are using <code>xmlattr</code> and passing user
input as attribute keys.</li>
<li>Changes: <a
href="https://jinja.palletsprojects.com/en/3.1.x/changes/#version-3-1-3">https://jinja.palletsprojects.com/en/3.1.x/changes/#version-3-1-3</a></li>
<li>Milestone: <a
href="https://github.com/pallets/jinja/milestone/15?closed=1">https://github.com/pallets/jinja/milestone/15?closed=1</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/pallets/jinja/blob/main/CHANGES.rst">jinja2's
changelog</a>.</em></p>
<blockquote>
<h2>Version 3.1.3</h2>
<p>Released 2024-01-10</p>
<ul>
<li>Fix compiler error when checking if required blocks in parent
templates are
empty. :pr:<code>1858</code></li>
<li><code>xmlattr</code> filter does not allow keys with spaces.
GHSA-h5c8-rqwp-cp95</li>
<li>Make error messages stemming from invalid nesting of <code>{% trans
%}</code> blocks
more helpful. :pr:<code>1918</code></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="d9de4bb215"><code>d9de4bb</code></a>
release version 3.1.3</li>
<li><a
href="50124e1656"><code>50124e1</code></a>
skip test pypi</li>
<li><a
href="9ea7222ef3"><code>9ea7222</code></a>
use trusted publishing</li>
<li><a
href="da703f7aae"><code>da703f7</code></a>
use trusted publishing</li>
<li><a
href="bce1746925"><code>bce1746</code></a>
use trusted publishing</li>
<li><a
href="7277d8068b"><code>7277d80</code></a>
update pre-commit hooks</li>
<li><a
href="5c8a105224"><code>5c8a105</code></a>
Make nested-trans-block exceptions nicer (<a
href="https://redirect.github.com/pallets/jinja/issues/1918">#1918</a>)</li>
<li><a
href="19a55db3b4"><code>19a55db</code></a>
Make nested-trans-block exceptions nicer</li>
<li><a
href="716795349a"><code>7167953</code></a>
Merge pull request from GHSA-h5c8-rqwp-cp95</li>
<li><a
href="7dd3680e6e"><code>7dd3680</code></a>
xmlattr filter disallows keys with spaces</li>
<li>Additional commits viewable in <a
href="https://github.com/pallets/jinja/compare/3.1.2...3.1.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=jinja2&package-manager=pip&previous-version=3.1.2&new-version=3.1.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/luau-lang/luau/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-19 06:21:55 -07:00
Vighnesh-V
25f91aa8b8
Sync to upstream/release/639 (#1368)
# What's Changed?

- Variety of bugfixes in the new solver

## New Solver

- Fix an issue where we would hit a recursion limit when applying long
chains of type refinements.
- Weaken the types of `table.freeze` and `table.clone` in the new solver
so we can accept common code patterns like `local a = table.freeze({x=5,
x=0})` at the expense of accepting code like `table.freeze(true)`.
- Don't warn when the # operator is used on a value of type never

## VM
- Fix a bug in lua_resume where too many values might be removed from
stack when resume throws an error

---
Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Junseo Yoo <jyoo@roblox.com>
2024-08-16 11:29:33 -07:00
Vighnesh
3613ee9ddc Merge branch 'upstream' into merge 2024-08-16 10:15:02 -07:00
Vighnesh
8c4f9070c5 Merge branch 'master' into merge 2024-08-16 10:14:44 -07:00
Vighnesh
497c3edb91 Sync to upstream/release/639 2024-08-16 09:48:02 -07:00
Ketasaja
1788e237fc
Make os.clock use clock_gettime on FreeBSD (#1364) 2024-08-14 08:31:59 -07:00
Petri Häkkinen
9dc299ecaf
Fix size of userdata metatable array (#1366)
Fix udatamt array to use the correct limit for tagged userdata.
2024-08-14 06:00:21 -07:00
Andy Friesen
bfad1fa777
Sync to upstream/release/638 (#1360)
New Solver

* Fix some type inference issues surrounding updates to upvalues eg

```luau
local x = 0

function f()
    x = x + 1
end
```

* User-defined type function progress
* Bugfixes for normalization of negated class types. eg `SomeClass &
(class & ~SomeClass)`
* Fixes to subtyping between tables and the top `table` type.

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Junseo Yoo <jyoo@roblox.com>
2024-08-09 10:18:20 -07:00
Andy Friesen
c229a9e598 Merge branch 'upstream' into merge 2024-08-09 09:46:45 -07:00
Andy Friesen
057bdf3556 Merge branch 'master' into merge 2024-08-09 09:46:41 -07:00
Andy Friesen
8a99f25381 Sync to upstream/release/638 2024-08-09 09:46:26 -07:00
Junseo Yoo
ce8495a69e
Sync to upstream/release/637 (#1354)
# What's Changed?

- Code refactoring with a new clang-format
- More bug fixes / test case fixes in the new solver

## New Solver

- More precise telemetry collection of `any` types
- Simplification of two completely disjoint tables combines them into a
single table that inherits all properties / indexers
- Refining a `never & <anything>` does not produce type family types nor
constraints
- Silence "inference failed to complete" error when it is the only error
reported

---
### Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Dibri Nsofor <dnsofor@roblox.com>
Co-authored-by: Jeremy Yoo <jyoo@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-08-02 07:30:04 -07:00
Junseo Yoo
1adbd45b20 Merge branch 'upstream' into merge 2024-08-01 16:26:06 -07:00
Junseo Yoo
5b401524f5 Merge branch 'master' into merge 2024-08-01 16:26:00 -07:00
Junseo Yoo
fad8aaf0ab Sync to upstream/release/637 2024-08-01 16:25:12 -07:00
ds-sloth
58f8c24ddb
BytecodeBuilder.cpp: fix invalid assumption that int32_t aliases int (#1347)
I found that Luau failed to build on the devkitPro toolchain for 3DS.

This target uses an ILP32 model, and `int32_t` is an alias of `long`.
`BytecodeBuilder.cpp` includes a line where an `int` is passed by
reference to a function expecting an `int32_t`.
2024-07-26 14:34:58 -07:00
Junseo Yoo
5e0779fd57
Sync to upstream/release/636 (#1346)
# What's Changed?

- Telemetry support for usage of any type in old/new solver
- Bug fixes and flag removals with the new solver

## New Solver

- Fixed constraint ordering bug to infer types more accurately
- Improved inferring a call to `setmetatable()`

## VM

- Restored global metatable lookup for `typeof` on lightuserdata to fix
unintentional API change (Fixes #1335)

---
### Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Dibri Nsofor <dnsofor@roblox.com>
Co-authored-by: Jeremy Yoo <jyoo@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-07-26 10:47:49 -07:00
Junseo Yoo
28c48cf460 Skip StudioReportLuauAny flag in tests 2024-07-26 10:46:07 -07:00
Junseo Yoo
bb7d5cf6c8 StudioReportLuauAny added to ExperimentalFlags 2024-07-26 10:22:34 -07:00
Junseo Yoo
48c0e9f625 Merge branch 'upstream' into merge 2024-07-25 17:24:36 -07:00
Junseo Yoo
fab4eec97b Merge branch 'master' into merge 2024-07-25 17:11:00 -07:00
Junseo Yoo
7dd10b16dc Sync to upstream/release/636 2024-07-25 17:10:42 -07:00
vegorov-rbx
a80abdb9b3
Add new solver pass to the coverage build (#1341) 2024-07-23 08:08:32 -07:00
Kalrnlo
39d2c295e7
Fix misspelling in parser.cpp (#1330)
Fixes the mispelling of instead
2024-07-22 15:36:17 -07:00
Vighnesh-V
a7299c3f0f
Sync to upstream/release/635 (#1337)
# What's Changed?

- Bugfixes in the new solver

## New Solver
- Equality graphs(E-Graphs) data structures were added
- Refactored even more instances of "type family" with "type function"
- `table.insert` no longer spuriously warns while selecting an overload
for reasonable arguments.
- Add time tracing for the new solver
- Miscellaneous fixes to unit tests

---
### Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Jeremy Yoo <jyoo@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Junseo Yoo <jyoo@roblox.com>
2024-07-19 11:20:47 -07:00
Vighnesh
ac97a491c1 Merge branch 'upstream' into merge 2024-07-19 10:40:51 -07:00
Vighnesh
79966e94a7 Merge branch 'master' into merge 2024-07-19 10:24:28 -07:00
Vighnesh
6fd26c55ff Sync to upstream/release/635 2024-07-19 10:21:40 -07:00
birds3345
2874ca9e86
Optimizations for UnionFind (#1334)
Implements ranks & path compression for union find.

---------

Co-authored-by: Alexander McCord <11488393+alexmccord@users.noreply.github.com>
2024-07-17 16:19:57 -07:00
JohnnyMorganz
623e1e30db
Store definitionLocation in ClassType (#1313)
Store the location of an `AstStatDeclareClass` in the `ClassType` as
`definitionLocation`.

This is useful for documentation comments and Go To Definition on a
class type

https://github.com/luau-lang/luau/issues/1137#issuecomment-2212413633
2024-07-17 07:43:31 -07:00
Alexander McCord
4f917420d7
Fix incorrect comment in sources of Luau.EqSat. (#1333) 2024-07-16 10:51:51 -07:00
Alexander McCord
e1bf6289c7
Equality graphs (#1285)
Working towards a full e-graph implementation as described by the [egg
paper](https://arxiv.org/pdf/2004.03082).

The type system has a couple of places where e-graphs would've been
useful and solved some classes of problems trivially. For example:

1. Normalization and simplification cannot handle cyclic types due to
the nature of their implementation.
2. Normalization can't tell when two tables or functions are equivalent,
but simplification theoretically can albeit not implemented.
3. Normalization requires deep normalization for inhabitance check,
whereas simplification would've returned the `never` type itself
indicating uninhabited.
4. Simplification requires constraint ordering to have perfect timing to
simplify.
5. Adding a rewrite rule requires implementing it twice, once in
simplification and once again in normalization with completely different
code design making it hard to verify that their behavior is materially
equivalent.
6. In cases where we must cache for performance, two different types
that are isomorphic have different cache entries resulting in cache
misses.
7. Type family reduction can handle cyclic type families, but only if
the cycle is not obscured by a different type family instance. (`t1
where t1 = union<number, add<t1, number>>` is irreducible)

I think we're getting the point!

---

Currently the implementation is missing a few features that makes
e-graphs actually useful. Those will be coming in a future PR.

1. Pattern matching,
6. Applying rewrites,
7. Rewrite until saturation, and
8. Extracting the best e-node according to some cost function.
2024-07-16 10:35:20 -07:00
Junseo Yoo
b6b74b4425
Sync to upstream/release/634 (#1325)
# What's Changed?

- Performance improvement in the old solver
- Bugfixes in the new solver

## Old Solver

- Mark types that do not need instantiation when being exported to
prevent unnecessary work from being done

## New Solver

- Refactored instances of "type family" with "type function"
- Index-out-of-bounds bug fix in the resolution resolver
- Subtyping reasonings are merged only if all failed

---
### Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-07-12 10:03:36 -07:00
Junseo Yoo
5a23350108 Merge branch 'upstream' into merge 2024-07-11 16:15:04 -07:00
Junseo Yoo
a62f6619c2 Merge branch 'master' into merge 2024-07-11 15:18:21 -07:00
Junseo Yoo
1c1476fa2d Sync to upstream/release/634 2024-07-11 15:13:45 -07:00
mttsner
6bfc38e61a
Fix incorrect comment in Bytecode.h (#1315)
The description of SUBRK/DIVRK is out of sync with the VM code.

Comment:
```cpp
// SUBRK, DIVRK: compute arithmetic operation between the constant and a source register and put the result into target register
// A: target register
// B: source register
// C: constant table index (0..255); must refer to a number
LOP_SUBRK,
LOP_DIVRK,
```
VM snippet:
```cpp
VM_CASE(LOP_DIVRK)
    {
        Instruction insn = *pc++;
        StkId ra = VM_REG(LUAU_INSN_A(insn));
        TValue* kv = VM_KV(LUAU_INSN_B(insn));
        StkId rc = VM_REG(LUAU_INSN_C(insn));
        ...
```
2024-07-09 16:00:17 -07:00
JohnnyMorganz
f5a2c5d55e
Keep commentLocations on SourceModule for definition files (#1314)
Right now, the commentLocations are not transferred over to the
SourceModule for definition files, like they are for normal source
modules. This means we lose out on finding documentation comments. We
add that in here

https://github.com/luau-lang/luau/issues/1137#issuecomment-2212413633
2024-07-09 15:28:21 -07:00
Vighnesh-V
a7be4fcc54
Sync to upstream/release/633 (#1318)
# What's Changed?

- Mostly stability and bugfixes with the new solver.

## New Solver

- Typechecking with the new solver should respect the no-check hot
comment.
- Record type alias locations and property locations of table
assignments
- Maintain location information for exported table types
- Stability fixes for normalization
- Report internal constraint solver errors.

---
### Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-07-08 14:57:06 -07:00
Vighnesh
bc1bead287 Merge branch 'upstream' into merge 2024-07-08 13:28:05 -07:00
Vighnesh
2010be1575 Merge branch 'master' into merge 2024-07-08 13:27:58 -07:00
Vighnesh
cfcb545b39 Sync to upstream/release/633 2024-07-08 13:22:11 -07:00
aaron
0d2688844a
Sync to upstream/release/632 (#1307)
# What's Changed?

- Fix #1137 by appropriately retaining additional metadata from
definition files throughout the type system.
- Improve Frontend for LSPs by appropriately allowing the cancellation
of typechecking while running its destructor.

## New Solver

- Added support for the `rawget` type function.
- Reduced overall static memory usage of builtin type functions.
- Fixed a crash where visitors could mutate a union or intersection type
and fail to invalidate iteration over them in doing so.
- Revised autocomplete functionality to not rely on a separate run of
the type solver when using the new solver.
- Implemented a more relaxed semantic rule for casting.
- Fixed some smaller crashes in the new solver.

## Native Code Generation

- Add additional codegen specialization for `math.sign`
- Cleaned up a large number of outstanding fflags in the code.

### Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: James McNellis <jmcnellis@roblox.com>
Co-authored-by: Jeremy Yoo <jyoo@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-06-28 17:34:49 -07:00
Aaron Weiss
da48758d04 Merge branch 'upstream' into merge 2024-06-28 17:08:27 -07:00
Aaron Weiss
c5a2a4b68f Merge branch 'master' into merge 2024-06-28 17:08:21 -07:00
Aaron Weiss
240a9d8f7f Sync to upstream/release/632 2024-06-28 17:07:35 -07:00
vegorov-rbx
caee04d82d
Sync to upstream/release/631 (#1299)
### What's new

* Added lint warning for using redundant `@native` attributes on
functions inside a `--!native` module
* Improved typechecking speed in old solver for modules with large types

### New Solver

* Fixed the length type function sealing the table prematurely
* Fixed crashes caused by general table indexing expressions

### VM

* Added support for a specialized 3-argument fast-call instruction to
improve performance of `vector` constructor, `buffer` writes and a few
`bit32` methods

---

### Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
2024-06-20 16:37:55 -07:00
Vyacheslav Egorov
dafb44dd3d Merge branch 'upstream' into merge 2024-06-21 01:24:20 +03:00
Vyacheslav Egorov
5d2e3de4b0 Merge branch 'master' into merge 2024-06-21 01:24:15 +03:00
Vyacheslav Egorov
816cb1d1c4 Sync to upstream/release/631 2024-06-21 01:23:57 +03:00
Vighnesh-V
7d4033071a
Sync to upstream/release/630 (#1295)
### What's new

* A bug in exception handling in GCC(11/12/13) on MacOS prevents our
test suite from running.
* Parser now supports leading `|` or `&` when declaring `Union` and
`Intersection` types (#1286)
* We now support parsing of attributes on functions as described in the
[rfc](https://github.com/luau-lang/rfcs/pull/30)
* With this change, expressions such as `local x = @native function(x)
    return x+1 end` and `f(@native function(x) return x+1 end)` are now
    valid.
* Added support for `@native` attribute - we can now force native
compilation of individual functions if the `@native` attribute is
specified before the `function` keyword (works for lambdas too).

### New Solver

* Many fixes in the new solver for crashes and instability
* Refinements now use simplification and not normalization in a specific
case of two tables
* Assume that compound assignments do not change the type of the
left-side operand
* Fix error that prevented Class Methods from being overloaded

### VM

* Updated description of Garbage Collector invariant

---
### Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-06-14 13:21:20 -07:00
Vighnesh
1ba3e5fd7b correct the flag name 2024-06-14 11:23:44 -07:00
Vighnesh
2a1359dad0 Merge branch 'upstream' into merge 2024-06-14 09:40:49 -07:00
Vighnesh
91790efb8d Merge branch 'master' into merge 2024-06-14 09:40:20 -07:00
Vighnesh
58b98097c5 Sync to upstream/release/630 2024-06-14 09:38:56 -07:00
Andy Friesen
0fa6a51c91
Sync to upstream/release/629 (#1290)
### What's new

* Implemented parsing logic for attributes
* Added `lua_setuserdatametatable` and `lua_getuserdatametatable` C API
methods for a faster userdata metatable fetch compared to
`luaL_getmetatable`. Note that metatable reference has to still be
pinned in memory!

### New Solver

* Further improvement to the assignment inference logic
* Fix many bugs surrounding constraint dispatch order

### Native Codegen

* Add IR lowering hooks for custom host userdata types
* Add IR to create new tagged userdata objects
* Remove outdated NativeState

---
### Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-06-07 10:51:12 -07:00
Andy Friesen
40e03164f7 Merge branch 'upstream' into merge 2024-06-07 10:20:14 -07:00
Andy Friesen
eae092a45a Merge branch 'master' into merge 2024-06-07 10:18:31 -07:00
Andy Friesen
5dd97352c1 Sync to upstream/release/629 2024-06-07 10:09:03 -07:00
Arseny Kapoulkine
81b2cc7dbe
tests: Adjust conformance tests to account for array invariant (#1289)
These were written before compiler optimizations and array invariant. It
is now impossible for t[1] to be stored in the hash part, as this would
violate the array invariant that says that elements 1..#t are stored in
the array.

For ipairs, it doesn't traverse the hash part anymore now, so we adjust
the code to make sure no elements outside of the 1..#t slice are
covered. For table.find, we can use find-with-offset to still access the
hash part.

Fixes #1283.
2024-06-07 10:05:50 -07:00
Arseny Kapoulkine
23b8726203
Fix incorrect comment in lgc.h (#1288)
The comment gave an incorrect (reversed) version of the invariant, which
could be confusing for people who haven't read the full description in
lgc.cpp.

Unfortunately this change is difficult to flag.

Fixes #1282.
2024-06-07 09:56:00 -07:00
Jack
43bf7c4e05
implement leading bar and ampersand in types (#1286)
Implements the [Leading `|` and `&` in types](https://rfcs.luau-lang.org/syntax-leading-bar-and-ampersand.html) RFC.

The changes to the parser are exactly as described in the RFC.

---------

Co-authored-by: Alexander McCord <11488393+alexmccord@users.noreply.github.com>
2024-06-05 07:52:30 -07:00
JohnnyMorganz
041b8ee4e7
Fix edge case in 'findBindingAtPosition' when looking up global binding at start of file (#1254)
The 'findBindingAtPosition' AstQuery function can be used to lookup a
local or global binding.

Inside of this function is a check to "Ignore this binding if we're
inside its definition. e.g. local abc = abc -- Will take the definition
of abc from outer scope".

However, this check is incorrect when we are looking up a global binding
at the start of a file.

Consider a complete file with the contents:
```lua
local x = stri|ng.char(1)
```

and we pass the location of the marker `|` as the position to the find
binding position. We will pick up the global binding of the definition
`string` coming from a builtin source (either defined via C++ code or a
definitions file and loaded into the global scope).

The global binding `string` will have a zero position: `0,0,0,0`.
However, the `findBindingLocalStatement` check works by looking up the
AstAncestry at the binding's defined begin position *in the current
source module*. This will then incorrectly return the local statement
for `local x`, as that is at the start of the source code. Then in turn,
we assume we are in the `local abc = abc` case, and end up skipping over
the correct binding.

We fix this by checking if the binding is at the global position. If so,
we early exit because it is impossible for a global binding to be
defined in a local statement.
2024-06-04 13:53:01 -07:00
Alexander McCord
daf79328fc
Sync to upstream/release/628 (#1278)
### What's new?

* Remove a case of unsound `table.move` optimization
* Add Luau stack slot reservations that were missing in REPL (fixes
#1273)

### New Type Solver

* Assignments have been completely reworked to fix a case of cyclic
constraint dependency
* When indexing, if the fresh type's upper bound already contains a
compatible indexer, do not add another upper bound
* Distribute type arguments over all type families sans `eq`, `keyof`,
`rawkeyof`, and other internal type families
* Fix a case where `buffers` component weren't read in two places (fixes
#1267)
* Fix a case where things that constitutes a strong ref were slightly
incorrect
* Fix a case where constraint dependencies weren't setup wrt `for ...
in` statement

### Native Codegen

* Fix an optimization that splits TValue store only when its value and
its tag are compatible
* Implement a system to plug additional type information for custom host
userdata types

---

### Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-05-31 12:18:18 -07:00
Alexander McCord
93a89dcfa1 Merge branch 'heads/upstream' into merge 2024-05-31 10:47:04 -07:00
Alexander McCord
09e46d1980 Merge branch 'master' into merge 2024-05-31 10:46:57 -07:00
Alexander McCord
fede4d6393 Sync to upstream/release/628 2024-05-31 10:46:33 -07:00
aaron
c8fe77c268
Sync to upstream/release/627 (#1266)
### What's new?

* Removed new `table.move` optimization because of correctness problems.

### New Type Solver

* Improved error messages for type families to describe what's wrong in
more detail, and ideally without using the term `type family` at all.
* Change `boolean` and `string` singletons in type checking to report
errors to the user when they've gotten an impossible type (indicating a
type error from their context).
* Split debugging flags for type family reduction
(`DebugLuauLogTypeFamilies`) from general solver logging
(`DebugLuauLogSolver`).
* Improve type simplification to support patterns like `(number |
string) | (string | number)` becoming `number | string`.

### Native Code Generation

* Use templated `luaV_doarith` to speedup vector operation fallbacks.
* Various small changes to better support arm64 on Windows.

### Internal Contributors
Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: James McNellis <jmcnellis@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-05-26 10:09:09 -07:00
Aaron Weiss
241fcf8eba Merge branch 'upstream' into merge 2024-05-26 08:34:47 -07:00
Aaron Weiss
26fb155507 Merge branch 'master' into merge 2024-05-26 08:34:43 -07:00
Aaron Weiss
bad9e1476e 627 2024-05-26 08:33:40 -07:00
birds3345
0dbe1a5022
add cmake folder to .gitignore (#1246)
In the readme file under the building section, it specifies that you
should run the command `mkdir cmake && cd cmake`; however, the folder is
not currently being ignored.
2024-05-22 13:07:15 -07:00
birds3345
c73ecd8e08
Fix typo in a comment (#1255)
ens -> ends
2024-05-21 13:58:33 -07:00
vegorov-rbx
fe0a819472
Sync to upstream/release/626 (#1258)
### New Type Solver

* Fixed crash in numeric binary operation type families
* Results of an indexing operation are now comparable to `nil` without a
false positive error
* Fixed a crash when a type that failed normalization was accessed
* Iterating on a free value now implies that it is iterable

---

### Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: James McNellis <jmcnellis@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
2024-05-16 16:02:03 -07:00
Vyacheslav Egorov
0f61e4e7a4 Merge fix 2024-05-16 15:25:15 -07:00
Vyacheslav Egorov
ca46dd6fe8 Merge branch 'upstream' into merge 2024-05-16 15:22:49 -07:00
Vyacheslav Egorov
e5de2ed3cc Merge branch 'master' into merge 2024-05-16 15:22:42 -07:00
Vyacheslav Egorov
0386eec734 Sync to upstream/release/626 2024-05-16 15:22:22 -07:00
Vighnesh-V
2a80f5e1d1
Sync to upstream/release/625 (#1252)
# What's changed?

* Fix warning issued when Cmake version is too low (contributed by OSS
community)

## New Solver

* Fix an issue with inhabitance testing of tables with cyclic properties
* Preserve error suppression during type unification
* Overhaul type reference counting in the constraint solver
* Other miscellaneous constraint ordering fixes

## Native Codegen

* Fix incorrect assertion check in loadBytecodeTypeInfo

---

## Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-05-10 11:21:45 -07:00
Vighnesh
f172471b87 Merge branch 'upstream' into merge 2024-05-10 09:21:07 -07:00
Vighnesh
f76a99b800 Merge branch 'master' into merge 2024-05-10 09:18:10 -07:00
Vighnesh
9bce20cb5c Sync to upstream/release/625 2024-05-10 09:17:09 -07:00
Bjorn
a775bbc6fc
Fix confusing warning when CMake version is too low (#1251)
Experienced brief moment of panic when this warning said I had clang 3
installed.
2024-05-10 03:36:37 -07:00
vegorov-rbx
905a37b928
Update native code generation note in the security guarantees (#1250) 2024-05-08 13:35:12 -07:00
Andy Friesen
8a64cb8b73
Sync to upstream/release/624 (#1245)
# What's changed?

* Optimize table.maxn.  This function is now 5-14x faster
* Reserve Luau stack space for error message.

## New Solver

* Globals can be type-stated, but only if they are already in scope
* Fix a stack overflow that could occur when normalizing certain kinds
of recursive unions of intersections (of unions of intersections...)
* Fix an assertion failure that would trigger when the __iter metamethod
has a bad signature

## Native Codegen

* Type propagation and temporary register type hints
* Direct vector property access should only happen for names of right
length
* BytecodeAnalysis will only predict that some of the vector value
fields are numbers

---

## Internal Contributors

Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-05-03 13:17:51 -07:00
Andy Friesen
1ad7b9cc56 Merge branch 'upstream' into merge 2024-05-03 09:49:16 -07:00
Andy Friesen
f4ecf437ba Merge branch 'master' into merge 2024-05-03 09:47:22 -07:00
Andy Friesen
93468ca88d Sync to upstream/release/624 2024-05-03 09:38:34 -07:00
vegorov-rbx
7edd58afed
Add benchmarks for native compilation with type info enabled (#1244) 2024-05-02 08:33:47 -07:00
Arseny Kapoulkine
f5303b3dd7
Make table.concat faster (#1243)
table.concat is idiomatic and should be the fastest way to concatenate
all table array elements together, but apparently you can beat it by
using `string.format`, `string.rep` and `table.unpack`:

```lua
string.format(string.rep("%*", #t), table.unpack(t))
```

... this just won't do, so we should fix table.concat performance.

The deficit comes from two places:

- rawgeti overhead followed by other stack accesses, all to extract a
string from what is almost always an in-bounds array lookup
- addlstring overhead in case separator is empty (extra function calls)

This change fixes this by using a fast path for in-bounds array lookup
for a string. Note that `table.concat` also supports numbers (these need
to be converted to strings which is a little cumbersome and has innate
overhead), and out-of-bounds accesses*. In these cases we fall back to
the old implementation.

To trigger out-of-bounds accesses, you need to skip the past-array-end
element (which is nil per array invariant), but this is achievable
because table.concat supports offset+length arguments. This should
almost never come up in practice but the per-element branches et al are
fairly cheap compared to the eventual string copy/alloc anyway.

This change makes table.concat ~2x faster when the separator is empty;
the table.concat benchmark shows +40% gains but it uses a variety of
string separators of different lengths so it doesn't get the full
benefit from this change.

---------

Co-authored-by: vegorov-rbx <75688451+vegorov-rbx@users.noreply.github.com>
2024-04-29 05:19:01 -07:00
Arseny Kapoulkine
af15c3cf17
CodeGen: Fix a typo in X64 (dis)assembler (#1238) 2024-04-26 11:14:13 -07:00
Alexander McCord
259e509038
Sync to upstream/release/623 (#1236)
# What's changed?

### New Type Solver

- Unification of two fresh types no longer binds them together.
- Replaced uses of raw `emplace` with `emplaceType` to catch cyclic
bound types when they are created.
- `SetIndexerConstraint` is blocked until the indexer result type is not
blocked.
- Fix a case where a blocked type got past the constraint solver.
- Searching for free types should no longer traverse into `ClassType`s.
- Fix a corner case that could result in the non-testable type `~{}`.
- Fix incorrect flagging when `any` was a parameter of some checked
function in nonstrict type checker.
- `IterableConstraint` now consider tables without `__iter` to be
iterables.

### Native Code Generation

- Improve register type info lookup by program counter.
- Generate type information for locals and upvalues

---

### Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: James McNellis <jmcnellis@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-04-25 15:26:09 -07:00
Alexander McCord
88dd289100 Fix missing include. 2024-04-25 14:03:47 -07:00
Alexander McCord
76ed1a5370 Merge branch 'heads/upstream' into merge 2024-04-25 13:57:53 -07:00
Alexander McCord
8d0a650a25 Merge branch 'master' into merge 2024-04-25 13:57:44 -07:00
Alexander McCord
50a2f8daa8 Sync to upstream/release/623 2024-04-25 13:57:23 -07:00
aaron
68bd1b2349
Sync to upstream/release/622 (#1232)
# What's changed?

* Improved the actual message for the type errors for `cannot call
non-function` when attempting to call a union of functions/callable
tables. The error now correctly explains the issue is an inability to
determine the return type of the call in this situation.
* Resolve an issue where tables and metatables were not correctly being
cloned during instantiation (fixes #1176).
* Refactor `luaM_getnextgcopage` to `luaM_getnextpage` (generally
removing `gco` prefix where appropriate).
* Optimize `table.move` between tables for large ranges of elements.
* Reformat a bunch of code automatically using `clang-format`.

### New Type Solver

* Clean up minimally-used or unused constraints in the constraint solver
(`InstantiationConstraint`, `SetOpConstraint`,
`SingletonOrTopTypeConstraint`).
* Add a builtin `singleton` type family to replace
`SingletonOrTopTypeConstraint` when inferring refinements.
* Fixed a crash involving type path reasoning by recording when type
family reduction has taken place in the path.
* Improved constraint ordering by blocking on unreduced types families
that are not yet proven uninhabitable.
* Improved the handling of `SetIndexerConstraints` for both better
inference quality and to resolve crashes.
* Fix a crash when normalizing cyclic unions of intersections.
* Fix a crash when normalizing an intersection with the negation of
`unknown`.
* Fix a number of crashes caused by missing `follow` calls on `TypeId`s.
* Changed type family reduction to correctly use a semantic notion of
uninhabited types, rather than checking for `never` types specifically.
* Refactor the `union` and `intersect` type families to be variadic.

### Native Code Generation

* Improve translation for userdata key get/set and userdata/vector
namecall.
* Provide `[top level]` and `[anonymous]` as function names to
`FunctionStats` as appropriate when no function name is available.
* Disable unwind support on Android platforms since it is unsupported.
*  

---

### Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-04-19 14:48:02 -07:00
Aaron Weiss
641e9f6eb5 Merge branch 'upstream' into merge 2024-04-19 14:05:22 -07:00
Aaron Weiss
5cf508a73b Merge branch 'master' into merge 2024-04-19 14:05:01 -07:00
Aaron Weiss
67b9145268 Sync to upstream/release/622 2024-04-19 14:04:30 -07:00
vegorov-rbx
9c2146288d
Sync to upstream/release/621 (#1229)
# What's changed?

* Support for new 'require by string' RFC with relative paths and
aliases in now enabled in Luau REPL application

### New Type Solver

* Fixed assertion failure on generic table keys (`[expr] = value`)
* Fixed an issue with type substitution traversing into the substituted
parts during type instantiation
* Fixed crash in union simplification when that union contained
uninhabited unions and other types inside
* Union types in binary type families like `add<a | b, c>` are expanded
into `add<a, c> | add<b, c>` to handle
* Added handling for type family solving creating new type families
* Fixed a bug with normalization operation caching types with unsolved
parts
* Tables with uninhabited properties are now simplified to `never`
* Fixed failures found by fuzzer

### Native Code Generation

* Added support for shared code generation between multiple Luau VM
instances
* Fixed issue in load-store propagation and new tagged LOAD_TVALUE
instructions
* Fixed issues with partial register dead store elimination causing
failures in GC assists

---

### Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: James McNellis <jmcnellis@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-04-12 10:18:49 -07:00
Vyacheslav Egorov
858b93a5f3 Sync fixup 2024-04-12 14:07:34 +03:00
Vyacheslav Egorov
f97e96dc29 Merge branch 'upstream' into merge 2024-04-12 13:46:23 +03:00
Vyacheslav Egorov
7c346a0a69 Merge branch 'master' into merge 2024-04-12 13:45:09 +03:00
Vyacheslav Egorov
0f0c0e4d28 Sync to upstream/release/621 2024-04-12 13:44:40 +03:00
Vighnesh-V
67e16cba18
Sync to upstream/release/620 (#1223)
# What's Changed

## New Type Solver
- Many more fixes to crashes, assertions, and hangs
- Annotated locals now countermand the inferred types of locals, meaning
that for a type `type MyType = number | string`, `local foo : MyType =
5` behaves the same as `local foo = 5 :: MyType`, where before, foo
would be assigned the type of the value on the rhs.
- Type Normalization now respects resource limits.
- Subtyping between classes and cyclic tables now supported

## Native Code Generation
- Work on the Native Code Generation(NCG) allocator continues

---

# Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: James McNellis <jmcnellis@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-04-05 13:45:09 -07:00
Vighnesh
5aa6d99340 transplant game engine fix for memory safety issues in normalization into OSS changes 2024-04-05 13:27:37 -07:00
Vighnesh
0f1973954c remove trailing . 2024-04-05 11:30:53 -07:00
Vighnesh
9cb93a98bc manually fix cmake configuration error 2024-04-05 11:27:48 -07:00
Vighnesh
2e1c0404d1 Merge branch 'upstream' into merge 2024-04-05 10:45:02 -07:00
Vighnesh
6bef0b10ca Merge branch 'master' into merge 2024-04-05 10:44:49 -07:00
Vighnesh
c730a51ca8 Sync to upstream/release/620 2024-04-05 10:41:05 -07:00
vegorov-rbx
9649e5e446
Fix CMake configuration error when CLI targets are not included again (#1219)
A mistake was made in https://github.com/luau-lang/luau/pull/1218

Fixes https://github.com/luau-lang/luau/issues/1208
2024-03-31 05:59:46 -07:00
Lily Brown
47ad768c69
Sync to upstream/release/619 (#1218)
# What's Changed

## New Type Solver
- Many fixes to crashes, assertions, and hangs
- Binary type family aliases now have a default parameter
- Added a debug check for unsolved types escaping the constraint solver
- Overloaded functions are no longer inferred
- Unification creates additional subtyping constraints for blocked types
- Attempt to guess the result type for type families that are too large
to resolve timely

## Native Code Generation
- Fixed `IrCmd::CHECK_TRUTHY` lowering in a specific case
- Detailed compilation errors are now supported
- More work on the new allocator

---

# Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: James McNellis <jmcnellis@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
2024-03-30 16:14:44 -07:00
Lily Brown
3e1b4130ea Merge branch 'upstream' into merge 2024-03-30 15:51:08 -07:00
Lily Brown
77598ed0a6 Merge branch 'master' into merge 2024-03-30 15:50:29 -07:00
Lily Brown
fb90dc083b Sync with upstream/release/620 2024-03-30 15:49:03 -07:00
vegorov-rbx
bac85116f6
Fix CMake configuration error when CLI targets are not included (#1213)
Doesn't really make sense to configure sources list based on target
presence.

Should fix https://github.com/luau-lang/luau/issues/1208
2024-03-25 09:08:15 -07:00
Andy Friesen
c1830d8b81
Sync to upstream/release/618 (#1205)
# What's changed

### Debugger

* Values after a 'continue' statement should not be accessible by
debugger in the 'until' condition

### New Type Solver

* Many fixes to crashes and hangs
* Better bidirectional inference of table literal expressions

### Native Code Generation

* Initial steps toward a shared code allocator

---

### Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-03-22 10:47:10 -07:00
Andy Friesen
d8f49d6ca2 Compiler fixes for MSVC and GCC. 2024-03-22 10:33:09 -07:00
Andy Friesen
4931165635 Merge branch 'upstream' into merge 2024-03-22 10:24:30 -07:00
Andy Friesen
a30b2aebfd Merge branch 'master' into merge 2024-03-22 10:21:47 -07:00
Andy Friesen
6fff08b621 Sync to upstream/release/618 2024-03-22 10:21:27 -07:00
Alexander McCord
d21b6fdb93
Sync to upstream/release/617 (#1204)
# What's Changed

* Fix a case where the stack wasn't completely cleaned up where
`debug.info` errored when passed `"f"` option and a thread.
* Fix a case of uninitialized field in `luaF_newproto`.

### New Type Solver

* When a local is captured in a function, don't add a new entry to the
`DfgScope::bindings` if the capture occurs within a loop.
* Fix a poor performance characteristic during unification by not trying
to simplify an intersection.
* Fix a case of multiple constraints mutating the same blocked type
causing incorrect inferences.
* Fix a case of assertion failure when overload resolution encounters a
return typepack mismatch.
* When refining a property of the top `table` type, we no longer signal
an unknown property error.
* Fix a misuse of free types when trying to infer the type of a
subscript expression.
* Fix a case of assertion failure when trying to resolve an overload
from `never`.

### Native Code Generation

* Fix dead store optimization issues caused by partial stores.

---

### Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-03-15 16:37:39 -07:00
Alexander McCord
c1bbf1ebec Merge branch 'heads/upstream' into merge 2024-03-15 14:15:29 -07:00
Alexander McCord
5e9a567e09 Merge branch 'master' into merge 2024-03-15 14:11:30 -07:00
Alexander McCord
f27d4f52c3 Sync to upstream/release/617 2024-03-15 14:01:00 -07:00
Arseny Kapoulkine
a7683110d7
CodeGen: Preserve known tags for LOAD_TVALUE synthesized from LOADK (#1201)
When lowering LOADK for booleans/numbers/nils, we deconstruct the
operation using STORE_TAG which informs the rest of the optimization
pipeline about the tag of the value. This is helpful to remove various
tag checks.

When the constant is a string or a vector, we just use
LOAD_TVALUE/STORE_TVALUE. For strings, this could be replaced by pointer
load/store, but for vectors there's no great alternative using current
IR ops; in either case, the optimization needs to be carefully examined
for profitability as simply copying constants into registers for
function calls could become more expensive.

However, there are cases where it's still valuable to preserve the tag.
For vectors, doing any math with vector constants contains tag checks
that could be removed. For both strings and vectors, storing them into a
table has a barrier that for vectors could be elided, and for strings
could be simplified as there's no need to confirm the tag.

With this change we now carry the optional tag of the value with
LOAD_TVALUE. This has no performance effect on existing benchmarks but
does reduce the generated code for benchmarks by ~0.1%, and it makes
vector code more efficient (~5% lift on X64 log1p approximation).
2024-03-15 09:49:00 -07:00
Arseny Kapoulkine
d2ed2150ca
Work around ASLR+ASAN compatibility issues in GHA (#1203)
vm.mmap_rnd_bits has been recently changed to 32 on GHA, which triggers
issues in ASAN builds that spuriously fail on startup. The fix requires
a more recent clang/gcc than the agents have available (clang 17, not
sure what GCC version), so for now we need to work around this by
restricting the ASLR randomness.

See https://github.com/google/sanitizers/issues/1614
2024-03-15 09:32:27 -07:00
Arseny Kapoulkine
9aa82c6fb9
CodeGen: Improve lowering of NUM_TO_VEC on A64 for constants (#1194)
When the input is a constant, we use a fairly inefficient sequence of
fmov+fcvt+dup or, when the double isn't encodable in fmov,
adr+ldr+fcvt+dup.

Instead, we can use the same lowering as X64 when the input is a
constant, and load the vector from memory. However, if the constant is
encodable via fmov, we can use a vector fmov instead (which is just one
instruction and doesn't need constant space).

Fortunately the bit encoding of fmov for 32-bit floating point numbers
matches that of 64-bit: the decoding algorithm is a little different
because it expands into a larger exponent, but the values are
compatible, so if a double can be encoded into a scalar fmov with a
given abcdefgh pattern, the same pattern should encode the same float;
due to the very limited number of mantissa and exponent bits, all values
that are encodable are also exact in both 32-bit and 64-bit floats.

This strategy is ~same as what gcc uses. For complex vectors, we
previously used 4 instructions and 8 bytes of constant storage, and now
we use 2 instructions and 16 bytes of constant storage, so the memory
footprint is the same; for simple vectors we just need 1 instruction (4
bytes).

clang lowers vector constants a little differently, opting to synthesize
a 64-bit integer using 4 instructions (mov/movk) and then move it to the
vector register - this requires 5 instructions and 20 bytes, vs ours/gcc
2 instructions and 8+16=24 bytes. I tried a simpler version of this that
would be more compact - synthesize a 32-bit integer constant with
mov+movk, and move it to vector register via dup.4s - but this was a
little slower on M2, so for now we prefer the slightly larger version as
it's not a regression vs current implementation.

On the vector approximation benchmark we get:

- Before this PR (flag=false): ~7.85 ns/op
- After this PR (flag=true): ~7.74 ns/op
- After this PR, with 0.125 instead of 0.123 in the benchmark code (to
use fmov): ~7.52 ns/op
- Not part of this PR, but the mov/dup strategy described above: ~8.00
ns/op
2024-03-13 12:56:11 -07:00
Maxwell Ruben
209fd506c9
Fix REPL help message formatting (#1186)
The last line of the help message was missing a newline character. I
feel a little silly creating a pull request for a 2 character change but
it was bothering me. Fixes #1185
2024-03-11 05:28:40 -07:00
aaron
ae459a0197
Sync to upstream/release/616 (#1184)
# What's Changed

* Add a compiler hint to improve Luau memory allocation inlining

### New Type Solver

* Added a system for recommending explicit type annotations to users in
cases where we've inferred complex generic types with type families.
* Marked string library functions as `@checked` for use in new
non-strict mode.
* Fixed a bug with new non-strict mode where we would incorrectly report
arity mismatches when missing optional arguments.
* Implement an occurs check for unifications that would produce
self-recursive types.
* Fix bug where overload resolution would fail when applied to
non-overloaded functions.
* Fix bug that caused the subtyping to report an error whenever a
generic was instantiated in an invariant context.
* Fix crash caused by `SetPropConstraint` not blocking properly.

### Native Code Generation

* Implement optimization to eliminate dead stores
* Optimize vector ops for X64 when the source is computed (thanks,
@zeux!)
* Use more efficient lowering for UNM_* (thanks, @zeux!)

---

### Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-03-08 16:47:53 -08:00
Aaron Weiss
1ebdfe093a Merge branch 'upstream' into merge 2024-03-08 16:05:03 -08:00
Aaron Weiss
9e1a26c9c8 Merge branch 'master' into merge 2024-03-08 15:59:15 -08:00
Aaron Weiss
27a05c0023 Sync to upstream/release/616 2024-03-08 15:57:12 -08:00
vegorov-rbx
9323be6110
Fix ConstraintSolver linker errors in release configuration (#1180)
This fixes linker errors reported in
https://github.com/luau-lang/luau/issues/1178
2024-03-05 09:04:28 -08:00
vegorov-rbx
443903aa00
Sync to upstream/release/615 (#1175)
# What's changed?

* Luau allocation scheme was changed to handle allocations in 513-1024
byte range internally without falling back to global allocator
* coroutine/thread creation no longer requires any global allocations,
making it up to 15% faster (vs libc malloc)
* table construction for 17-32 keys or 33-64 array elements is up to 30%
faster (vs libc malloc)

### New Type Solver

* Cyclic unary negation type families are reduced to `number` when
possible
* Class types are skipped when searching for free types in unifier to
improve performance
* Fixed issues with table type inference when metatables are present
* Improved inference of iteration loop types
* Fixed an issue with bidirectional inference of method calls
* Type simplification will now preserve error suppression markers

### Native Code Generation

* Fixed TAG_VECTOR skip optimization to not break instruction use counts
(broken optimization wasn't included in 614)
* Fixed missing side-effect when optimizing generic loop preparation
instruction

---

### Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
2024-03-01 10:45:26 -08:00
Vyacheslav Egorov
ed4ce84e55 Merge fixes 2024-03-01 16:27:22 +02:00
Vyacheslav Egorov
f36cae2109 Merge branch 'upstream' into merge 2024-03-01 16:08:36 +02:00
Vyacheslav Egorov
532fd109e7 Merge branch 'master' into merge 2024-03-01 16:04:44 +02:00
Vyacheslav Egorov
d4a266528a Sync to upstream/release/615 2024-03-01 15:58:44 +02:00
Arseny Kapoulkine
cc51e616ce
CodeGen: Optimize vector ops for X64 when the source is computed (#1174)
With the TAG_VECTOR change, we can now confidently distinguish cases
when the .w component
contains TVECTOR tag from cases where it doesn't: loads and tag ops
produce the tag, whereas
other instructions don't.

We now take advantage of this fact and only apply vandps with a mask
when we need to.

It would be possible to use a positive filter (explicitly checking for
source coming from ADD_VEC
et al), but there are more instructions to check this way and this is
purely an optimization so
it is allowed to be conservative (as in, the cost of a mistake here is a
potential slowdown,
not a correctness issue).

Additionally, this change only performs vandps once when the arguments
are the same instead
of doing it twice.

On the function that computes a polynomial approximation this change
makes it ~20% faster on Zen4.
2024-03-01 03:32:43 -08:00
Arseny Kapoulkine
c9324853e5
luau-compile: Fix usage of vector-ctor without vector-lib (#1172)
When --vector-lib is not specified, CompileOptions::vectorLib was set to
an empty string. This resulted in the builtin matching not working,
since vectorLib must either be a null pointer or a pointer to a valid
global identifier.

---------

Co-authored-by: vegorov-rbx <75688451+vegorov-rbx@users.noreply.github.com>
2024-02-26 09:15:13 -08:00
Vighnesh-V
3b0e93bec9
Sync to upstream/release/614 (#1173)
# What's changed?
Add program argument passing to scripts run using the Luau REPL! You can
now pass `--program-args` (or shorthand `-a`) to the REPL which will
treat all remaining arguments as arguments to pass to executed scripts.
These values can be accessed through variadic argument expansion. You
can read these values like so:
```
local args = {...} -- gets you an array of all the arguments
```
For example if we run the following script like `luau test.lua -a test1
test2 test3`:
```
-- test.lua
print(...)
```
you should get the output:
```
test1 test2 test3
```

### Native Code Generation

* Improve A64 lowering for vector operations by using vector
instructions
* Fix lowering issue in IR value location tracking! 
- A developer reported a divergence between code run in the VM and
Native Code Generation which we have now fixed

### New Type Solver

* Apply substitution to type families, and emit new constraints to
reduce those further
* More progress on reducing comparison  (`lt/le`)type families
* Resolve two major sources of cyclic types in the new solver

### Miscellaneous
* Turned internal compiler errors (ICE's) into warnings and errors

-------
Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-02-23 12:08:34 -08:00
Vighnesh
0ab33af5c2 Merge branch 'upstream' into merge 2024-02-23 11:12:27 -08:00
Vighnesh
ccb5385a72 Merge branch 'master' into merge 2024-02-23 10:55:08 -08:00
Vighnesh
22686ef1b0 Sync to upstream/release/614 2024-02-23 10:40:00 -08:00
Arseny Kapoulkine
80928acb92
CodeGen: Extract all vector tag patching into TAG_VECTOR (#1171)
Instead of patching the tag component with TVECTOR in every instruction
that produces a vector value, we now use a separate IR instruction to do
this. This reduces implementation redundancy, but more importantly
allows for a class of optimizations:

- NUM_TO_VECTOR previously patched the component unconditionally but the
result was used only in MUL/DIV_VEC instructions that ignore it anyway;
we can now remove this.

- ADD_VEC et al can now forward the source of TAG_VECTOR instruction of
either input; this shortens the latency chain and in the future could
allow us to generate optimal vector instruction sequence once the
temporary stores are marked as dead.

- In the future on X64, ADD_VEC et al will be able to analyze the input
instruction and remove tag masking conditionally. This is not part of
this PR as it requires a decision around expected FP environment and/or
the necessity of the existing masking to begin with.

I've also renamed NUM_TO_VECTOR to NUM_TO_VEC so that "VEC" always
refers to "3 float values" and for consistency with ADD/etc.

Note: ADD_VEC input forwarding is currently performed unconditionally;
it may or may not increase the spills that can't be reloaded from the
stack.

On A64 this makes the Taylor series computation a tiny bit faster
(11.3ns => 11.0ns) as it removes the redundant ins instructions along
the NUM_TO_VEC path. Curiously, the optimization of forwarding
TAG_VECTOR input to arithmetic instructions actually has a small penalty
as without it this PR runs at 10.9 ns. I don't know if this is a
property of the benchmark though, as I just noticed that in this
benchmark type inference actually fails to infer parts of the
computation as a vector op. If desired I will happily omit this part of
the change and we can explore that separately.
2024-02-21 07:06:11 -08:00
Arseny Kapoulkine
c5f4d973d7
Improve A64 lowering for vector operations by using vector instructions (#1164)
This change replaces scalar versions of vector opcodes for A64 with
actual vector instructions.

We take the approach similar to X64: patch last component with zero,
perform the math, patch last component with type tag. I'm hoping that in
the future the type tag will be placed separately (separate IR opcode?)
because right now chains of math operations result in excessive type tag
operations.

To patch the type tag without always keeping a mask in a register,
ins.4s instructions can be used; unfortunately it's only capable of
patching a register in-place, so we need an extra register copy in case
it's not last-use. Usually it's last-use so the patch is free; probably
with IR rework mentioned above all of this can be improved (e.g.
load-with-patch will never need to copy).

~It's not 100% clear if we *have* to patch type tag: Apple does preserve
denormals but we'd need to benchmark this to see if there's an actual
performance impact. But for now we're playing it safe.~

This was tested by running the conformance tests, and new opcode
implementations were checked by comparing the result with
https://armconverter.com/.

Performance testing is complicated by the fact that OSS Luau doesn't
support vector constructor out of the box, and other limitations of
codegen. I've hacked vector constructor/type into REPL and confirmed
that on a test that calls this function in a loop (not inlined):

```
function fma(a: vector, b: vector, c: vector)
        return a * b + c
end
```

... this PR improves performance by ~6% (note that probably most of the
overhead here is the call dispatch; I didn't want to brave testing a
more complex expression). The assembly for an individual operation
changes as follows:

Before:

```
#   %14 = MUL_VEC %12, %13                                    ; useCount: 2, lastUse: %22
 dup         s29,v31.s[0]
 dup         s28,v30.s[0]
 fmul        s29,s29,s28
 ins         v31.s[0],v29.s[0]
 dup         s29,v31.s[1]
 dup         s28,v30.s[1]
 fmul        s29,s29,s28
 ins         v31.s[1],v29.s[0]
 dup         s29,v31.s[2]
 dup         s28,v30.s[2]
 fmul        s29,s29,s28
 ins         v31.s[2],v29.s[0]
```

After:

```
#   %14 = MUL_VEC %12, %13                                    ; useCount: 2, lastUse: %22
 ins         v31.s[3],w31
 ins         v30.s[3],w31
 fmul        v31.4s,v31.4s,v30.4s
 movz        w17,#4
 ins         v31.s[3],w17
```

**edit** final form (see comments):

```
#   %14 = MUL_VEC %12, %13                                    ; useCount: 2, lastUse: %22
 fmul        v31.4s,v31.4s,v30.4s
 movz        w17,#4
 ins         v31.s[3],w17
```
2024-02-16 08:30:35 -08:00
vegorov-rbx
ea14e65ea0
Sync to upstream/release/613 (#1167)
# What's changed?

* Compiler now targets bytecode version 5 by default, this includes
support for vector type literals and sub/div opcodes with a constant on
lhs

### New Type Solver

* Normalizer type inhabitance check has been optimized
* Added ability to reduce cyclic `and`/`or` type families 

### Native Code Generation

* `CodeGen::compile` now returns more specific causes of a code
generation failure
* Fixed linking issues on platforms that don't support unwind frame data
registration

---

### Internal Contributors

Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
2024-02-15 18:04:39 -08:00
Vyacheslav Egorov
b5f2813ab4 Merge branch 'upstream' into merge 2024-02-16 03:26:02 +02:00
Vyacheslav Egorov
1778950554 Merge branch 'master' into merge 2024-02-16 03:25:56 +02:00
Vyacheslav Egorov
158d60c223 Sync to upstream/release/613 2024-02-16 03:25:31 +02:00
Andy Friesen
d6c2472f0c
Sync to upstream/release/612 (#1162)
New solver

* Fix bugs where bidirectional type inference would fail to take effect
at the proper stage.
* Improve inference of mutually recursive functions
* Fix crashes

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-02-09 09:51:12 -08:00
Andy Friesen
ffd9f32d2c Merge branch 'upstream' into merge 2024-02-09 09:35:57 -08:00
Andy Friesen
45e72ee97b Merge branch 'master' into merge 2024-02-09 09:33:24 -08:00
Andy Friesen
1a6da94547 Sync to upstream/release/612 2024-02-09 09:32:52 -08:00
Alexander McCord
67ce75e870
Sync to upstream/release/611 (#1160)
# What's changed?

### Native Code Generation

* Fixed an UAF relating to reusing a hash key after a weak table has
undergone some GC.
* Fixed a bounds check on arm64 to allow access to the last byte of a
buffer.

### New Type Solver

* Type states now preserves error-suppression, i.e. `local x: any = 5`
and `x.foo` does not error.
* Made error-suppression logic in subtyping more accurate.
* Subtyping now knows how to reduce type families.
* Fixed function call overload resolution so that the return type
resolves to the correct overload.
* Fixed a case where we attempted to reduce irreducible type families a
few too many times, leading to duplicate errors.
* Type checker needs to type check annotations in function signatures to
be able to report errors relating to those annotations.
* Fixed an UAF from a pointer to stack-allocated data in Subtyping's
`explainReasonings`.

### Nonstrict Type Checker

* Fixed a crash when calling a checked function of the form `math.abs`
with an incorrect argument type.
* Fixed a crash when calling a checked function with a number of
arguments that did not exactly match the number of parameters required.

---

### Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-02-02 13:32:42 -08:00
Alexander McCord
5559c7fbd5 Fix the stack-use-after-scope. 2024-02-02 13:20:35 -08:00
Alexander McCord
88d2b93351 Merge branch 'heads/upstream' into merge 2024-02-02 10:28:24 -08:00
Alexander McCord
f8f0dd94f7 Merge branch 'master' into merge 2024-02-02 10:20:39 -08:00
Alexander McCord
dfa512ba36 Sync to upstream/release/611 2024-02-02 10:20:03 -08:00
Arseny Kapoulkine
974963a870
Enable newly added M1 GHA CI (#1158)
GitHub just released support for M1 runners for open source projects via
GHA. It can be selected by using macos-14 OS to run on.

This is very valuable because until now, Luau wasn't tested in OSS CI on
Arm64, despite the presence of a fairly large volume of AArch64 specific
code courtesy of A64 codegen. This change fixes that.

In the future we could also expand codecov reporting as it seems to work
well but it needs a little bit of cleanup for the build in question so
let's start small.
2024-02-01 05:18:27 -08:00
Alex Orlenko
d409f7946d
Fix setting sandbox on Lua instance without strlib (#1156)
Currently calling `luaL_sandbox` on Lua instance without loaded strlib
causes crash (assertion).
It happens because inside `luaL_sandbox` there is no check that
metatable for strings is present.
2024-01-30 07:23:58 -08:00
JohnnyMorganz
c0b17daebd
Handle autocomplete in table when no initial character present (#1155)
Closes #685
2024-01-29 09:52:00 -08:00
aaron
9c588be16d
Sync to upstream/release/610 (#1154)
# What's changed?

* Check interrupt handler inside the pattern match engine to eliminate
potential for programs to hang during string library function execution.
* Allow iteration over table properties to pass the old type solver. 

### Native Code Generation

* Use in-place memory operands for math library operations on x64.
* Replace opaque bools with separate enum classes in IrDump to improve
code maintainability.
* Translate operations on inferred vectors to IR.
* Enable support for debugging native-compiled functions in Roblox
Studio.

### New Type Solver

* Rework type inference for boolean and string literals to introduce
bounded free types (bounded below by the singleton type, and above by
the primitive type) and reworked primitive type constraint to decide
which is the appropriate type for the literal.
* Introduce `FunctionCheckConstraint` to handle bidirectional
typechecking for function calls, pushing the expected parameter types
from the function onto the arguments.
* Introduce `union` and `intersect` type families to compute deferred
simplified unions and intersections to be employed by the constraint
generation logic in the new solver.
* Implement support for expanding the domain of local types in
`Unifier2`.
* Rework type inference for iteration variables bound by for in loops to
use local types.
* Change constraint blocking logic to use a set to prevent accidental
re-blocking.
* Add logic to detect missing return statements in functions.

### Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-01-26 19:20:56 -08:00
Aaron Weiss
ce2665d9f1 Merge branch 'upstream' into merge 2024-01-26 18:31:35 -08:00
Aaron Weiss
e3aba9292e Merge branch 'master' into merge 2024-01-26 18:31:32 -08:00
Aaron Weiss
0edacdded4 Sync to upstream/release/610 2024-01-26 18:30:40 -08:00
vegorov-rbx
cdd1a380db
Sync to upstream/release/609 (#1150)
### What's changed?
* Syntax for [read-only and write-only
properties](https://github.com/luau-lang/rfcs/pull/15) is now parsed,
but is not yet supported in typechecking

### New Type Solver
* `keyof` and `rawkeyof` type operators have been updated to match final
text of the [RFC](https://github.com/luau-lang/rfcs/pull/16)
* Fixed issues with cyclic type families that were generated for mutable
loop variables

### Native Code Generation
* Fixed inference for number / vector operation that caused an
unnecessary VM assist

---
### Internal Contributors
Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-01-19 10:04:46 -08:00
Vyacheslav Egorov
2fd3da3c14 Merge branch 'upstream' into merge 2024-01-19 17:17:03 +02:00
Vyacheslav Egorov
59a29fd322 Merge branch 'master' into merge 2024-01-19 17:16:21 +02:00
Vyacheslav Egorov
064d845269 Sync to upstream/release/609 2024-01-19 17:13:08 +02:00
Aleksi
73360e5399
Fix missing include for std::exchange (#1147) 2024-01-16 04:41:40 -08:00
Vighnesh-V
f31232d301
Sync to upstream/release/608 (#1145)
# Old Solver:

- Fix a bug in the old solver where a user could use the keyword
`typeof` as the name of a type alias.
- Fix stringification of scientific notation to omit a trailing decimal
place when not followed by a digit e.g. `1.e+20` -> `1e+20`
# New Solver
- Continuing work on the New non-strict mode
- Introduce `keyof` and `rawkeyof` type function for acquiring the type
of all keys in a table or class
(https://github.com/luau-lang/rfcs/pull/16)

---
Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-01-12 14:25:27 -08:00
Vighnesh
38aa074767 resolve memory leak in VecDeque 2024-01-12 14:11:40 -08:00
Vighnesh
2f7509dedd Merge branch 'upstream' into merge 2024-01-12 11:18:51 -08:00
Vighnesh
50b4779798 Sync to upstream/release/608 2024-01-12 11:16:39 -08:00
Vighnesh
79328350ab Merge branch 'upstream' into merge 2024-01-11 22:55:44 -08:00
Vighnesh
74b0d004ec Merge branch 'master' into merge 2024-01-11 22:47:40 -08:00
Vighnesh
0d5c842434 Sync to upstream/release/608 2024-01-11 21:28:14 -08:00
Vighnesh
d4883bfb32 Merge branch 'upstream' of https://github.com/luau-lang/luau into upstream 2024-01-11 14:55:06 -08:00
JohnnyMorganz
c0f5538947
Clone TableType typeLocation (#1136)
This was missed in #1046
2024-01-08 06:25:27 -08:00
vegorov-rbx
e76802f2ce
Add a prefix to TaggedLuData flag to have it auto-enabled in flags ON mode (#1130) 2023-12-15 13:51:14 -08:00
aaron
ff502f0943
Sync to upstream/release/607 (#1131)
# What's changed?

* Fix up the `std::iterator_traits` definitions for some Luau data
structures.
* Replace some of the usages of `std::unordered_set` and
`std::unordered_map` with Luau-provided data structures to increase
performance and reduce overall number of heap allocations.
* Update some of the documentation links in comments throughout the
codebase to correctly point to the moved repository.
* Expanded JSON encoder for AST to support singleton types.
* Fixed a bug in `luau-analyze` where exceptions in the last module
being checked during multithreaded analysis would not be rethrown.

### New type solver

* Introduce a `refine` type family to handle deferred refinements during
type inference, replacing the old `RefineConstraint`.
* Continued work on the implementation of type states, fixing some known
bugs/blockers.
* Added support for variadic functions in new non-strict mode, enabling
broader support for builtins and the Roblox API.

### Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-12-15 13:29:06 -08:00
Aaron Weiss
adea0f883c fix ubuntu build with import for cstddef in DenseHash 2023-12-15 13:11:25 -08:00
Aaron Weiss
d6226187b0 Merge branch 'upstream' into merge 2023-12-15 12:53:11 -08:00
Aaron Weiss
89090a16a6 Merge branch 'master' into merge 2023-12-15 12:53:07 -08:00
Aaron Weiss
f9c5cdd4fb Sync to upstream/release/607 2023-12-15 12:52:08 -08:00
Petri Häkkinen
2173938eb0
Add tagged lightuserdata (#1087)
This change adds support for tagged lightuserdata and optional custom
typenames for lightuserdata.

Background: Lightuserdata is an efficient representation for many kinds
of unmanaged handles and resources in a game engine. However, currently
the VM only supports one kind of lightuserdata, which makes it
problematic in practice. For example, it's not possible to distinguish
between different kinds of lightuserdata in Lua bindings, which can lead
to unsafe practices and even crashes when a wrong kind of lightuserdata
is passed to a binding function. Tagged lightuserdata work similarly to
tagged userdata, i.e. they allow checking the tag quickly using
lua_tolightuserdatatagged (or lua_lightuserdatatag).

The tag is stored in the 'extra' field of TValue so it will add no cost
to the (untagged) lightuserdata type.

Alternatives would be to use full userdata values or use bitpacking to
embed type information into lightuserdata on application level.
Unfortunately these options are not that great in practice: full
userdata have major performance implications and bitpacking fails in
cases where full 64 bits are already used (e.g. pointers or 64-bit
hashes).

Lightuserdata names are not strictly necessary but they are rather
convenient when debugging Lua code. More precise error messages and
tostring returning more specific typename are useful to have in practice
(e.g. "resource" or "entity" instead of the more generic "userdata").

Impl note: I did not add support for renaming tags in
lua_setlightuserdataname as I'm not sure if it's possible to free fixed
strings. If it's simple enough, maybe we should allow renaming (although
I can't think of a specific need for it)?

---------

Co-authored-by: Petri Häkkinen <petrih@rmd.remedy.fi>
2023-12-14 15:05:51 -08:00
vegorov-rbx
c26d820902
Sync to upstream/release/606 (#1127)
New Solver
* Improvements to data flow analysis

Native Code Generation
* Block limit is now per-function instead of per-module

Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-12-08 13:50:16 -08:00
Vyacheslav Egorov
139b169361 Merge fix 2023-12-08 17:58:25 +02:00
Vyacheslav Egorov
6068432285 Merge branch 'upstream' into merge 2023-12-08 17:47:11 +02:00
Vyacheslav Egorov
f5441d7030 Merge branch 'master' into merge 2023-12-08 17:46:40 +02:00
Vyacheslav Egorov
69728e87cf Sync to upstream/release/606 2023-12-08 17:42:54 +02:00
Vighnesh-V
2ea7193777
Resolve RequireByString test suite failures on Ubuntu CI (#1124)
This PR resolves the RequireByString test suite failures in CI on
Ubuntu. The issue was that paths on linux are case sensitive and thus
MacOS/Windows machines simply behaved as if the paths to the test files
were case-insensitive, but the linux machine was unable to find the test
files. Those tests were `#ifdef'd` out, and have been un `#ifdef'd` to
demonstrate these changes work.
2023-12-05 14:31:02 -08:00
Vyacheslav Egorov
aafea36235
Fixed the backwards compatible benchmark support library require (#1125)
Previous benchmark require fix wasn't actually working correctly for the
old style require (or running in Lua).
2023-12-04 12:48:31 -08:00
menarulalam
765ac75fe9
Update ExperimentalFlags.h (#1123)
Add LuauUpdatedRequireByStringSemantics to experimental flags.
2023-12-04 14:26:39 -05:00
Vyacheslav Egorov
ce1800746b
Fix 'require' in benchmarks to work with new relative system (#1120)
Old style require is now called with `pcall` to support comparing
against Lua.
New style require is now a third option.

Edit: this will be a temporary solution until the 'paths' support in
.luaurc is fixed.
2023-12-02 14:20:54 -08:00
Vyacheslav Egorov
6413ea8574
Only build fuzzer with CMake if 3.26 is available (#1119)
`cmake_minimum_required` is a blocking check and raising required
minimal CMake version only for the fuzzer is not good for the users
(<3.19 was supported before, but not sure which exact one)
2023-12-02 13:04:25 -08:00
Vighnesh-V
c755875479
Sync to upstream/release/605 (#1118)
- Implemented [Require by String with Relative
Paths](https://github.com/luau-lang/rfcs/blob/master/docs/new-require-by-string-semantics.md)
RFC
- Implemented [Require by String with
Aliases](https://github.com/luau-lang/rfcs/blob/master/docs/require-by-string-aliases.md)
RFC with support for `paths` and `alias` arrays in .luarc
- Added SUBRK and DIVRK bytecode instructions to speed up
constant-number and constant/number operations
- Added `--vector-lib`, `--vector-ctor` and `--vector-type` options to
luau-compile to support code with vectors
 
New Solver
- Correctness fixes to subtyping
- Improvements to dataflow analysis

Native Code Generation
- Added bytecode analysis pass to predict type tags used in operations
- Fixed rare cases of numerical loops being generated without an
interrupt instruction
- Restored optimization data propagation into the linear block
- Duplicate buffer length checks are optimized away

Miscellaneous
- Small performance improvements to new non-strict mode
- Introduced more scripts for fuzzing Luau and processing the results,
including fuzzer build support for CMake

Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-12-01 23:46:57 -08:00
Vighnesh
c48ffc32a4 endif 2023-12-01 20:52:10 -08:00
Vighnesh
c932b8e03f remove tests for require by string 2023-12-01 20:50:35 -08:00
Vighnesh
41669c9f30 Sync to upstream/release/605 2023-12-01 20:40:43 -08:00
Vighnesh
c592f50e20 can CI handle this? 2023-12-01 20:37:13 -08:00
Vighnesh
79854143e4 resolve one last merge conflict 2023-12-01 19:11:31 -08:00
Vighnesh
e70eec0c59 Merge branch 'upstream' into merge
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-12-01 18:56:54 -08:00
Vighnesh
98d2db7325 add stdint import 2023-12-01 18:35:40 -08:00
Vighnesh
557e77a676 VM
- Add SUBRK and DIVRK bytecode instructions
    - Enables future performance optimizations

Miscellaneous
- Small performance improvements to new non-strict mode
- Introduce more scripts for fuzzing
- Improcements to dataflow analysis
2023-12-01 18:04:44 -08:00
Arseny Kapoulkine
89b437bb4e
Add SUBRK and DIVRK bytecode instructions to bytecode v5 (#1115)
Right now, we can compile R\*K for all arithmetic instructions, but K\*R
gets compiled into two instructions (LOADN/LOADK + arithmetic opcode).

This is problematic since it leads to reduced performance for some code.
However, we'd like to avoid adding reverse variants of ADDK et al for
all opcodes to avoid the increase in I$ footprint for interpreter.

Looking at the arithmetic instructions, % and // don't have interesting
use cases for K\*V; ^ is sometimes used with constant on the left hand
side but this would need to call pow() by necessity in all cases so it
would be slow regardless of the dispatch overhead. This leaves the four
basic arithmetic operations.

For + and \*, we can implement a compiler-side optimization in the
future that transforms K\*R to R\*K automatically. This could either be
done unconditionally at -O2, or conditionally based on the type of the
value (driven by type annotations / inference) -- this technically
changes behavior in presence of metamethods, although it might be
sensible to just always do this because non-commutative +/* are evil.

However, for - and / it is impossible for the compiler to optimize this
in the future, so we need dedicated opcodes. This only increases the
interpreter size by ~300 bytes (~1.5%) on X64.

This makes spectral-norm and math-partial-sums 6% faster; maybe more
importantly, voxelgen gets 1.5% faster (so this change does have
real-world impact).

To avoid the proliferation of bytecode versions this change piggybacks
on the bytecode version bump that was just made in 604 for vector
constants; we would still be able to enable these independently but
we'll consider v5 complete when both are enabled.

Related: #626

---------

Co-authored-by: vegorov-rbx <75688451+vegorov-rbx@users.noreply.github.com>
2023-11-28 07:35:01 -08:00
Arseny Kapoulkine
7fb7f4382d
Use ubuntu-latest on GHA when possible (#1112)
We need ubuntu-20.04 for coverage analysis (clang after 10 doesn't seem
to properly interact with gcov version used for codecov) and for
releases (to produce binaries targeting earlier glibc).

However, we still should be verifying that Luau builds on latest,
because newer toolchains have stricter standard library headers and/or
warnings; without this we're at risk of constantly regressing the build
for packaging or external applications.

Note that release.yml can probably just be deleted but for now we simply
adjust it.
2023-11-27 03:24:57 -08:00
vegorov-rbx
1cda8304bf
Add missing include file to BytecodeSummary.h (#1110) 2023-11-21 08:28:54 -08:00
JohnnyMorganz
ae53051814
Include module name for definitions files (#861)
Closes #818 

We set the `moduleName` of the source module to the provided
`packageName`.
This then populates the relevant `definitionModuleName`'s on
CTV/FTV/TTVs, so it allows us to lookup where they originated from.

The one place I can see this having an impact inside of Luau code is the
following:

1fa8311a18/Analysis/src/Error.cpp (L70-L90)
Which will potentially change they way error messages are formatted.
Should there be a way to exclude definition files when calling
`getDefinitionModuleName`, so we can preserve this behaviour?
2023-11-21 08:12:18 -08:00
Andy Friesen
74c532053f
Sync to upstream/release/604 (#1106)
New Solver

* New algorithm for inferring the types of locals that have no
annotations. This
algorithm is very conservative by default, but is augmented with some
control
  flow awareness to handle most common scenarios.
* Fix bugs in type inference of tables
* Improve performance of by switching out standard C++ containers for
`DenseHashMap`
* Infrastructure to support clearer error messages in strict mode

Native Code Generation

* Fix a lowering issue with buffer.writeu8 and 0x80-0xff values: A
constant
  argument wasn't truncated to the target type range and that causes an
  assertion failure in `build.mov`.
* Store full lightuserdata value in loop iteration protocol lowering
* Add analysis to compute function bytecode distribution
* This includes a class to analyze the bytecode operator distribution
per
function and a CLI tool that produces a JSON report. See the new cmake
      target `Luau.Bytecode.CLI`

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-11-17 10:46:18 -08:00
Andy Friesen
2d4a544709 Merge branch 'upstream' into merge 2023-11-17 10:18:37 -08:00
Andy Friesen
43201531b3 Merge branch 'master' into merge 2023-11-17 10:18:24 -08:00
Andy Friesen
674c6c40c0 Sync to upstream/release/604 2023-11-17 10:15:31 -08:00
Petri Häkkinen
298cd70154
Optimize vector literals by storing them in the constant table (#1096)
With this optimization, built-in vector constructor calls with 3/4 arguments are detected by the compiler and turned into vector constants when the arguments are constant numbers.
Requires optimization level 2 because built-ins are not folded otherwise by the compiler.
Bytecode version is bumped because of the new constant type, but old bytecode versions can still be loaded.

The following synthetic benchmark shows ~6.6x improvement.
```
local v
for i = 1, 10000000 do
	v = vector(1, 2, 3)
	v = vector(1, 2, 3)
	v = vector(1, 2, 3)
	v = vector(1, 2, 3)
	v = vector(1, 2, 3)
	v = vector(1, 2, 3)
	v = vector(1, 2, 3)
	v = vector(1, 2, 3)
	v = vector(1, 2, 3)
	v = vector(1, 2, 3)
end
```

Also tried a more real world scenario and could see a few percent improvement.

Added a new fast flag LuauVectorLiterals for enabling the feature.

---------

Co-authored-by: Petri Häkkinen <petrih@rmd.remedy.fi>
Co-authored-by: vegorov-rbx <75688451+vegorov-rbx@users.noreply.github.com>
Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
2023-11-17 04:54:32 -08:00
Kostadin
0492ecffdf
Add #include <algorithm> to fix building with gcc 14 (#1104)
With gcc 14 some C++ Standard Library headers have been changed to no
longer include other headers that were used internally by the library.
In luau's case it is the `<algorithm>` header.
2023-11-16 11:51:16 -08:00
Arseny Kapoulkine
d2059dd50d
Update codecov.yml
Make status checks informational
2023-11-15 06:30:40 -08:00
Arseny Kapoulkine
64d2d0fb76
Update README.md
Use rfcs.luau-lang.org for the .luaurc link
2023-11-13 07:41:15 -08:00
JohnnyMorganz
4b2af900c2
Fix link to .luaurc info in README (#1101) 2023-11-12 13:48:41 -08:00
Alexander McCord
c2ba1058c3
Sync to upstream/release/603 (#1097)
# What's changed?

- Record the location of properties for table types (closes #802)
- Implement stricter UTF-8 validations as per the RFC
(https://github.com/luau-lang/rfcs/pull/1)
- Implement `buffer` as a new type in both the old and new solvers.
- Changed errors produced by some `buffer` builtins to be a bit more
generic to avoid platform-dependent error messages.
- Fixed a bug where `Unifier` would copy some persistent types, tripping
some internal assertions.
- Type checking rules on relational operators is now a little bit more
lax.
- Improve dead code elimination for some `if` statements with complex
always-false conditions

## New type solver

- Dataflow analysis now generates phi nodes on exit of branches.
- Dataflow analysis avoids producing a new definition for locals or
properties that are not owned by that loop.
- If a function parameter has been constrained to `never`, report errors
at all uses of that parameter within that function.
- Switch to using the new `Luau::Set` to replace `std::unordered_set` to
alleviate some poor allocation characteristics which was negatively
affecting overall performance.
- Subtyping can now report many failing reasons instead of just the
first one that we happened to find during the test.
- Subtyping now also report reasons for type pack mismatches.
- When visiting `if` statements or expressions, the resulting context
are the common terms in both branches.

## Native codegen

- Implement support for `buffer` builtins to its IR for x64 and A64.
- Optimized `table.insert` by not inserting a table barrier if it is
fastcalled with a constant.

## Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Arseny Kapoulkine <arseny@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-11-10 13:10:07 -08:00
Alexander McCord
e57cbf6132 Merge branch 'heads/upstream' into merge 2023-11-10 10:13:36 -08:00
Alexander McCord
63436480a0 Merge branch 'master' into merge 2023-11-10 10:08:30 -08:00
Alexander McCord
4b68791b2c Sync to upstream/release/603 2023-11-10 10:05:48 -08:00
aaron
7105c81579
Sync to upstream/release/602 (#1089)
# What's changed?

* Fixed a bug in type cloning by maintaining persistent types.
* We now parse imprecise integer literals to report the imprecision as a
warning to developers.
* Add a compiler flag to specify the name of the statistics output file.

### New type solver

* Renamed `ConstraintGraphBuilder` to `ConstraintGenerator`
* LValues now take into account the type being assigned during
constraint generation.
* Normalization performance has been improved by 33% by replacing the an
internal usage of `std::unordered_set` with `DenseHashMap`.
* Normalization now has a helper to identify types that are equivalent
to `unknown`, which is being used to fix some bugs in subtyping.
* Uses of the old unifier in the new type solver have been eliminated.
* Improved error explanations for subtyping errors in `TypeChecker2`.

### Native code generation

* Expanded some of the statistics recorded during compilation to include
the number of instructions and blocks.
* Introduce instruction and block count limiters for controlling what
bytecode is translated into native code.
* Implement code generation for byteswap instruction.

### Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
2023-11-03 16:45:04 -07:00
Aaron Weiss
efe133abe9 Merge branch 'upstream' into merge 2023-11-03 12:48:56 -07:00
Aaron Weiss
a0b9950541 Sync to upstream/release/602 2023-11-03 12:47:28 -07:00
JohnnyMorganz
1a9159daff
Record table type alias property locations (#1046)
For table type aliases, records the location of the property in the
alias declaration.

This is an alternate solution to the particular case noted in #802.
Instead of tracking the type alias definition for FTVs, it tracks it for
the encompassing property instead.

Note that we still don't have positions in the following case:

```
type Func = () -> ()
```

Closes #802 (Although not completely...)
2023-11-02 09:14:51 -07:00
Arseny Kapoulkine
5622474b6a Merge remote-tracking branch 'origin/merge' 2023-11-01 18:11:17 -07:00
Arseny Kapoulkine
b647288375
Delete rfcs directory
This folder is now hosted in a separate repository https://github.com/luau-lang/rfcs (see https://github.com/luau-lang/luau/issues/1074 for details)
2023-10-30 10:49:36 -07:00
Arseny Kapoulkine
88eec1da37
Update CONTRIBUTING.md
Fix the guide to point to docs and rfcs repositories for associated changes
2023-10-30 09:16:59 -07:00
Arseny Kapoulkine
6d564abf81
Update README.md
Correct links and language to docs and RFCs repository and mention usage in non-Roblox games.
2023-10-30 09:02:22 -07:00
Arseny Kapoulkine
45f4b87991 Attempt to disable codecov patch status check 2023-10-27 17:11:17 -07:00
Lily Brown
e5ec0cdff3
Sync to upstream/release/601 (#1084)
## What's changed

- `bit32.byteswap` added
([RFC](4f543ec23b/docs/function-bit32-byteswap.md))
- Buffer library implementation
([RFC](4f543ec23b/docs/type-byte-buffer.md))
- Fixed a missing `stdint.h` include
- Fixed parser limiter for recursive type annotations being kind of
weird (fixes #645)

### Native Codegen
- Fixed a pair of issues when lowering `bit32.extract`
- Fixed a narrow edge case that could result in an infinite loop without
an interruption
- Fixed a negative array out-of-bounds access issue
- Temporarily reverted linear block predecessor value propagation

### New type solver
- We now type check assignments to annotated variables
- Fixed some test cases under local type inference
- Moved `isPending` checks for type families to improve performance
- Optimized our process for testing if a free type is sufficiently
solved
- Removed "none ptr" from lea instruction disassembly logging

### Build system & tooling
- CMake configuration now validates dependencies to maintain separation
between components
- Improvements to the fuzzer coverage
- Deduplicator for fuzzed callstacks

---------

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
2023-10-27 14:18:41 -07:00
Lily Brown
98217437ec perhaps this works better 2023-10-27 13:42:42 -07:00
Lily Brown
9216e4cb27 attempt allowing a more complex expression here 2023-10-27 13:22:39 -07:00
Lily Brown
592469373b Merge branch 'upstream' into merge 2023-10-27 13:04:04 -07:00
Lily Brown
a4ceaa10ec Merge branch 'master' into merge 2023-10-27 12:34:25 -07:00
Lily Brown
8237b2f593 Sync to upstream/release/601 2023-10-27 12:33:36 -07:00
Arseny Kapoulkine
87d955d5eb Fix remaining references to Roblox org outside of source code 2023-10-24 10:32:59 -07:00
Arseny Kapoulkine
88fcbd58e6
Update README.md
Fix status badges and various repository links
2023-10-24 10:28:19 -07:00
Arseny Kapoulkine
ff59ced5ee
Delete docs directory
The directory was moved to https://github.com/luau-lang/site

See #1074 for context
2023-10-24 10:22:54 -07:00
Maciej Barć
26f79951f7
TypePairHash.h: include cstdint (#1078)
luau fails to compile w/o this on Gentoo

Signed-off-by: Maciej Barć <xgqt@gentoo.org>
2023-10-24 10:02:46 -07:00
Arseny Kapoulkine
13e3af2724
Delete papers directory
The directory was moved to https://github.com/luau-lang/research

See https://github.com/Roblox/luau/issues/1074 for context
2023-10-23 09:02:08 -07:00
Arseny Kapoulkine
d70a02a2d0
Delete CNAME 2023-10-23 08:57:58 -07:00
Micah
011c1afbde
Implement bit32.byteswap (#1075)
I've decided to take a stab at implementing `bit32.byteswap` from the
[recently merged
RFC](https://github.com/Roblox/luau/blob/master/rfcs/function-bit32-byteswap.md).
I asked on Discord for some guidance, but for the sake of posterity:
this is my first time doing this and I am likely to have made some
mistakes.

The biggest gaps in this implementation are the lack of tests and the
lack of native codegen support. I'd appreciate help with those since I'm
not sure what's relevant for me to touch for tests, and I'm told that
relevant assembler instructions don't exist publicly yet. Intuition
tells me that Luau-side tests would go into
`tests/conformance/bitwise.luau` but this is not well documented and I'm
not sure how I'm meant to test built-in implementations.

The current implementation compiles down to `bswap` and `rev` on x86 and
ARM respectively when optimized.

---------

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
2023-10-23 08:00:48 -07:00
Vighnesh-V
fd6250cf9d
Sync to upstream/release/600 (#1076)
### What's Changed

- Improve readability of unions and intersections by limiting the number
of elements of those types that can be presented on a single line (gated
under `FFlag::LuauToStringSimpleCompositeTypesSingleLine`)
- Adds a new option to the compiler `--record-stats` to record and
output compilation statistics
- `if...then...else` expressions are now optimized into `AND/OR` form
when possible.

### VM

- Add a new `buffer` type to Luau based on the [buffer
RFC](https://github.com/Roblox/luau/pull/739) and additional C API
functions to work with it; this release does not include the library.
- Internal C API to work with string buffers has been updated to align
with Lua version more closely

### Native Codegen

- Added support for new X64 instruction (rev) and new A64 instruction
(bswap) in the assembler
- Simplified the way numerical loop condition is translated to IR

### New Type Solver

- Operator inference now handled by type families
- Created a new system called `Type Paths` to explain why subtyping
tests fail in order to improve the quality of error messages.
- Systematic changes to implement Data Flow analysis in the new solver
(`Breadcrumb` removed and replaced with `RefinementKey`)

---
Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
2023-10-20 18:10:30 -07:00
Vighnesh
46f18bb352 comment out whole test case 2023-10-20 17:37:59 -07:00
Vighnesh
43915a4675 More test uncommenting 2023-10-20 17:07:09 -07:00
Vighnesh
7602c43214 Disable failing tests that rely on type paths 2023-10-20 16:09:58 -07:00
Vighnesh
8002cbb125 Merge branch 'upstream' into merge
Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-10-20 14:02:26 -07:00
Vighnesh
6e91ab9227 Merge branch 'master' into merge 2023-10-20 13:37:59 -07:00
Vighnesh
897a5da14e Sync to upstream/release/600 2023-10-20 13:36:26 -07:00
Rerumu
380bb7095d
RFC: Built in buffer type and library (#739)
[Rendered
document](https://github.com/Rerumu/luau/blob/rfc-type-byte-array/rfcs/type-byte-buffer.md).

---------

Co-authored-by: vegorov-rbx <75688451+vegorov-rbx@users.noreply.github.com>
Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Micah <dekkonot@rocketmail.com>
2023-10-19 01:13:58 -07:00
Arseny Kapoulkine
61afc5e0dd
Create CNAME 2023-10-16 16:25:47 -07:00
Arseny Kapoulkine
cb59964bda
Delete CNAME 2023-10-16 16:25:28 -07:00
Alexander McCord
6439719cd2
Facelift of the Account OO example. (#578)
When the original version was written, bidirectional type inference
didn't exist, so this new kind of pattern was simply not possible.

Now that it does, we can stop telling users to use the original typeof
workaround.

The only thing that's a bit unfortunate is the cast of `{}` into
`AccountImpl`, but it's a necessary evil for now until we get a few more
big ticket items. When we have that, we can likely kill the cast and
call it redundant.
2023-10-16 16:06:53 -07:00
Micah
7e5643a4ad
RFC: Add bit32.byteswap to support swapping the endianness of integers (#1052)
This function might be of particular use if #739 is accepted, but as it
stands, it still brings a measurable reduction in complexity for
swapping byte order and is measurably faster (around a 50% reduction in
time per operation on my machine), albeit on the order of magnitude of
nanoseconds.
2023-10-16 08:47:23 -07:00
Lily Brown
24fdac4c05
Sync to upstream/release/599 (#1069)
## What's Changed

- Improve POSIX compliance in `CLI/FileUtils.cpp` by @SamuraiCrow #1064
- `AstStat*::hasEnd` is deprecated; use `AstStatBlock::hasEnd` instead
- Added a lint for common misuses of the `#` operator
- Luau now issues deprecated diagnostics for some uses of `getfenv` and
`setfenv`
- Fixed a case where we included a trailing space in some error
stringifications

### Compiler

- Do not do further analysis in O2 on self functions
- Improve detection of invalid repeat..until expressions vs continue

### New Type Solver

- We now cache subtype test results to improve performance
- Improved operator inference mechanics (aka type families)
- Further work towards type states
- Work towards [new non-strict
mode](https://github.com/Roblox/luau/blob/master/rfcs/new-nonstrict.md)
continues

### Native Codegen

- Instruction last use locations should follow the order in which blocks
are lowered
- Add a bonus assertion to IrLoweringA64::tempAddr

---

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
2023-10-13 13:20:12 -07:00
Lily Brown
dc6a75b450 Merge branch 'upstream' into merge 2023-10-13 12:43:34 -07:00
Lily Brown
4d2be9727e Merge remote-tracking branch 'origin/master' into merge 2023-10-13 12:42:29 -07:00
Lily Brown
34efdb3b0a Sync to upstream/release/599 2023-10-13 12:38:31 -07:00
Alex Orlenko
5c94984935
Add lua_getallocf API function (#1068)
This function matches the corresponding Lua 5.1-5.4 function:

[`lua_getallocf`](https://www.lua.org/manual/5.4/manual.html#lua_getallocf)
and [source](https://www.lua.org/source/5.4/lapi.c.html#lua_getallocf)

It would be useful to get/manipulate auxiliary "userdata" pointer that was originally passed to `lua_newstate`.
2023-10-13 08:47:33 -07:00
JohnnyMorganz
1173e415ef
Modify the way line breaks are added when printing unions/intersections (#1047)
Adds an option `compositeTypesSingleLineLimit` to control when
unions/intersections are broken up onto multiple lines when printed.
Simple unions/intersections will remain on one line. Default value is
set to `5`.

I took the liberty to always expand overloaded function intersections on
multiple lines, as I believe they are best viewed that way, even if
theres only 2 functions.

Closes #963
2023-10-12 09:09:47 -07:00
Samuel Crow
984336448c
Improve POSIX compliance in CLI/FileUtils.cpp (#1064)
Some POSIX platforms, such as Haiku and some BSDs, don't supply DT_*
identifiers and the corresponding d_type field in stat. This fixes that
and has been tested to still work on 64-bit Linux as well as 64-bit
Haiku.
2023-10-12 05:46:53 -07:00
Arseny Kapoulkine
3d4e99fc6c rfcs: Update status
String interpolation and table.getn/et al deprecation has been live for
a while.

getfenv/setfenv linter diagnostics is going to ship in the next release,
so preemptively marking this as Implemented as well.
2023-10-11 08:29:25 -07:00
Alan Jeffrey
497dd1bad4
Incorrectness 2024 paper (#1048) 2023-10-09 19:00:31 -05:00
Alan Jeffrey
ffc28da331
RFC for new non-strict mode (#1037) 2023-10-09 18:57:50 -05:00
Andy Friesen
36e0e64715
Sync to upstream/release/598 (#1063)
* Include `windows.h` rather than `Windows.h` to make things compile on
MinGW.
* Custom implementation of timegm/os.time for all platforms
* Disable builtin constant folding when getfenv/setfenv are used
* Fixes https://github.com/Roblox/luau/issues/1042
* Fixes https://github.com/Roblox/luau/issues/1043

New Type Checker

* Initial work toward type states.
* Rework most overloadable operators to use type families.
* Initial work toward our new nonstrict mode.


Native Codegen

* Fix native code generation for dead loops
* Annotate top-level functions as cold
* Slightly smaller/faster x64 Luau calls
* emitInstCall used to not set savedpc itself, but now it does for
consistency with all other implementations
* Implement cmov support for X64
* Fix assertion in luau-compile when module is empty
* Optimize A64 calls at some code size cost
* Inline constant array index offset into the load/store instruction
* Increase x64 spill slots from 5 to 13

---------

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
2023-10-06 12:02:32 -07:00
Andy Friesen
ff9c57255c Turn this down a little temporarily. We'll sort out the root cause later. 2023-10-06 11:39:08 -07:00
Andy Friesen
008b77a09a Merge branch 'upstream' into merge 2023-10-06 10:40:24 -07:00
Andy Friesen
47374ff2a6 Merge branch 'master' into merge 2023-10-06 10:36:16 -07:00
Andy Friesen
22e3d1fa46 Sync to upstream/release/598 2023-10-06 10:31:16 -07:00
Arseny Kapoulkine
225a4a0870
Update compatibility.md (#1059)
Add a note about Lua 5.3 change to semantics of __eq metamethod.

We currently do not plan to implement this as this breaks backwards
compatibility and makes these operators harder to reason about from the
type checking perspective.

Fixes #1051.
2023-10-03 07:39:48 -07:00
Radiant
2a3a030341
Rename Windows.h to windows.h (#1055)
Make it able to compile on MingW

Fixes #873.

Co-authored-by: Radiant <i.like.using.discord@gmail.com>
2023-10-03 06:59:44 -07:00
Alexander McCord
1d0b449181
Sync to upstream/release/597 (#1054)
# New Type Solver

- Implement bidirectional type inference for higher order functions so
that we can provide a more precise type improving the autocomplete's
human factors.
- We seal all tables, so we changed the stringification to make it a
little lighter on users.
- Fixed a case of array-out-of-bound access.
- Type families no longer depends on `TxnLog` and `Unifier`.
- Type refinements now waits until the free types are sufficiently
solved.

# Native Code Generation

- Remove cached slot lookup for `executeSETTABLEKS` function because it
is a fallback in the event of a cache miss, making the cached slot
lookup redundant.
- Optimized repeated array lookups, e.g. `a[3]` in `a[3] = a[3] / 2` is
done once.

# Misc

- On some platforms, it is necessary to use `gmtime_s` with the
arguments reversed to get the current time. You can now define
`DOCTEST_CONFIG_USE_GMTIME_S` to build and run unit tests on those
platforms.

---------

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
2023-09-29 18:13:05 -07:00
Alexander McCord
d42188a2da Merge branch 'heads/upstream' into merge 2023-09-29 17:25:41 -07:00
Alexander McCord
c6b581addc Merge branch 'master' into merge 2023-09-29 17:23:20 -07:00
Alexander McCord
3bfc864280 Sync to upstream/release/597 2023-09-29 17:22:06 -07:00
aaron
16fbfe912c
Sync to upstream/release/596 (#1050)
- Cleaned up `FFlag::FixFindBindingAtFunctionName`,
`FFlag::LuauNormalizeBlockedTypes`, `FFlag::LuauPCallDebuggerFix`
- Added support for break and continue into control flow analysis
- The old type unification engine will now report a more fine-grained
error at times, indicating that type normalization in particular failed

# New Type Solver

- Refactor of Unifier2, the new unification implementation for Luau
- Completed MVP of new unification implementation
- Dramatically simplified overload selection logic
- Type family reduction can now apply sooner to free types that have
been solved
- Subtyping now supports table indexers
- Generalization now replaces bad generics with unknown

# Native Code Generation

- Reduce stack spills caused by FINDUPVAL and STORE_TAG
- Improve Generate SHL/SHR/SAR/rotates with immediate operands in X64
- Removed redundant case re-check in table lookup fallback

---------

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
2023-09-22 12:12:15 -07:00
Aaron Weiss
3882ab1488 Merge branch 'upstream' into merge 2023-09-22 11:12:50 -07:00
Aaron Weiss
d4de6f7502 Merge branch 'master' into merge 2023-09-22 11:12:44 -07:00
Aaron Weiss
81681e2948 Sync to upstream/release/596 2023-09-22 11:10:49 -07:00
Amber Grace
d00e93c82c
Support Control Flow type Refinements for "break" and "continue" statements (#1004)
Fixes: https://github.com/Roblox/luau/issues/913

This PR adds support for type refinements around guard clauses that use
`break` and `continue` statements inside a loop, similar to how guard
clauses with `return` is supported.

I had some free time today, so I figure I'd give a shot at a naïve fix
for this at the very least.

---

## Resulting Change:

Luau now supports type refinements within loops where a `continue` or
`break` guard clause was used.
For example:
```lua
for _, object in objects :: {{value: string?}} do
    if not object.value then
        continue
    end
    local x: string = object.value -- OK; Used to emit "Type 'string?' could not be converted into 'string'"
end
```

---------

Co-authored-by: Alexander McCord <amccord@roblox.com>
2023-09-21 15:28:42 -07:00
Arseny Kapoulkine
309001020a
Update benchmark.yml
Update apt-get cache before installing valgrind as it looks like the default cache got out of date.
2023-09-16 12:21:09 +02:00
Andy Friesen
31a017c5c7
Sync to upstream/release/595 (#1044)
* Rerun clang-format on the code
* Fix the variance on indexer result subtyping. This fixes some issues
with inconsistent error reporting.
* Fix a bug in the normalization logic for intersections of strings

New Type Solver

* New overload selection logic
* Subtype tests now correctly treat a generic as its upper bound within
that generic's scope
* Semantic subtyping for negation types
* Semantic subtyping between strings and compatible table types like
`{lower: (string) -> string}`
* Further work toward finalizing our new subtype test
* Correctly generalize module-scope symbols

Native Codegen

* Lowering statistics for assembly
* Make executable allocation size/limit configurable without a rebuild.
Use `FInt::LuauCodeGenBlockSize` and `FInt::LuauCodeGenMaxTotalSize`.

---------

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
2023-09-15 10:26:59 -07:00
Andy Friesen
c0449320f0 Merge branch 'upstream' into merge 2023-09-15 09:28:12 -07:00
Andy Friesen
55d7a9912d Merge branch 'master' into merge 2023-09-15 09:28:09 -07:00
Andy Friesen
d8b97f4745 Sync to upstream/release/595 2023-09-15 09:27:45 -07:00
Someon1e
a35d3d4588
Demo site: case sensitive highlighting (#1040)
Before:
![image](https://github.com/Roblox/luau/assets/142684596/6a5ec602-9946-48aa-98c7-ddebc404c5dd)

After:
![image](https://github.com/Roblox/luau/assets/142684596/d7a9bf3d-2163-417c-8407-43d65d6eca01)
2023-09-11 09:09:00 -07:00
vegorov-rbx
c7c986b996
Sync to upstream/release/594 (#1036)
* Fixed `Frontend::markDirty` not working on modules that were not
typechecked yet
* Fixed generic variadic function unification succeeding when it should
have reported an error

New Type Solver:
* Implemented semantic subtyping check for function types

Native Code Generation:
* Improved performance of numerical loops with a constant step
* Simplified IR for `bit32.extract` calls extracting first/last bits
* Improved performance of NaN checks
2023-09-07 17:13:49 -07:00
Vyacheslav Egorov
789ece0941 Merge branch 'upstream' into merge 2023-09-07 16:33:17 -07:00
Vyacheslav Egorov
5143f5ecd3 Merge branch 'master' into merge 2023-09-07 16:33:12 -07:00
Vyacheslav Egorov
8e28c240bd Sync to upstream/release/594 2023-09-07 16:28:45 -07:00
Arseny Kapoulkine
bf1fb8f1e4
Update syntax.md (#1035)
Add a note about floor division support.
2023-09-06 18:14:03 -07:00
Arseny Kapoulkine
105f54b233
fix build & test conformance issues on mingw32/gcc and mingw64/clang (#1034)
CLI/Reduce.cpp:
Never worked under MinGW as-is. Add check for MinGW as they're defined.

VM/src/lmem.cpp:
MinGW layout follows more closely MSVC. Checks before were only correct
for 32-bit gcc / clang in non-Windows.

NOTES:
__MINGW32__ is defined on both 32-bit and 64-bit, __MINGW64__ is 64-bit
only. 32-bit MinGW compilers (both Clang & GCC) have a floating point
test error on math.noise, specifically math.lua:258. All other test
cases / unit tests pass modulo stack size issues (so requires optimized build).

---------

Co-authored-by: jdp_ <42700985+jdpatdiscord@users.noreply.github.com>
2023-09-05 10:22:35 -07:00
Arseny Kapoulkine
f0a2e79365
rfcs: Mark floor division RFC as implemented 2023-09-01 14:10:22 -07:00
Arseny Kapoulkine
cb18b53396
Update compatibility.md
Mark floor division operator as implemented
2023-09-01 14:09:18 -07:00
Lily Brown
551a43c424
Sync to upstream/release/593 (#1024)
- Updated Roblox copyright to 2023
- Floor division operator `//` (implements #832)
- Autocomplete now shows `end` within `do` blocks
- Restore BraceType when using `Lexer::lookahead` (fixes #1019)

# New typechecker

- Subtyping tests between metatables and tables
- Subtyping tests between string singletons and tables
- Subtyping tests for class types

# Native codegen

- Fixed macOS test failure (wrong spill restore offset)
- Fixed clobbering of non-volatile xmm registers on Windows
- Fixed wrong storage location of SSA reg spills
- Implemented A64 support for add/sub extended
- Eliminated zextReg from A64 lowering
- Remove identical table slot lookups
- Propagate values from predecessor into the linear block
- Disabled reuse slot optimization
- Keep `LuaNode::val` check for nil when optimizing `CHECK_SLOT_MATCH`
- Implemented IR translation of `table.insert` builtin
- Fixed mmap error handling on macOS/Linux

# Tooling

- Used `|` as a column separator instead of `+` in `bench.py`
- Added a `table.sort` micro-benchmark
- Switched `libprotobuf-mutator` to a less problematic version
2023-09-01 10:58:27 -07:00
Lily Brown
09fdb4cfbd Merge branch 'upstream' into merge 2023-09-01 10:15:30 -07:00
Lily Brown
a2df5951d4 Merge branch 'master' into merge 2023-09-01 09:40:20 -07:00
Lily Brown
397dbb1188 Sync to upstream/release/593 2023-09-01 09:38:53 -07:00
vegorov-rbx
ce9414cb98
Sync to upstream/release/592 (#1018)
* AST queries at position where function name is will now return
AstExprLocal
* Lexer performance has been slightly improved
* Fixed incorrect string singleton autocomplete suggestions (fixes #858)
* Improved parsing error messages
* Fixed crash on null pointer access in unification (fixes #1017)
* Native code support is enabled by default and `native=1`
(make)/`LUAU_NATIVE` (CMake)/`-DLUA_CUSTOM_EXECUTION` configuration is
no longer required

New typechecker:
* New subtyping check can now handle generic functions and tables
(including those that contain cycles)

Native code generation:
* Loops with non-numeric parameters are now handled by VM to streamline
native code
* Array size check can be optimized away in SETLIST
* On failure, CodeGen::compile returns a reason
* Fixed clobbering of non-volatile xmm registers on Windows
2023-08-25 10:23:55 -07:00
Vyacheslav Egorov
51d4d18ec0 Late fixes 2023-08-25 18:46:28 +03:00
Vyacheslav Egorov
8cf13435c8 Merge branch 'upstream' into merge 2023-08-25 18:45:54 +03:00
Vyacheslav Egorov
263c282635 Merge branch 'master' into merge 2023-08-25 18:45:50 +03:00
Vyacheslav Egorov
a811050505 Sync to upstream/release/592 2023-08-25 18:25:09 +03:00
Andy Friesen
c3fc0d7bc8
RFC for local type inference. (#1007)
[Rendered](0e1082108f/rfcs/local-type-inference.md)
2023-08-23 15:52:59 -07:00
Arseny Kapoulkine
535f85ebd3
Update performance.md (#1016)
Document tostring/tonumber and math.pi/huge optimizations
2023-08-23 10:55:14 -07:00
Alan Jeffrey
5097c60f10
Fix HATRA 23 citations (#1015) 2023-08-21 15:25:25 -05:00
Alan Jeffrey
d5e37ab367
Responding to referee comments on the HATRA 23 paper (#1014) 2023-08-21 14:21:10 -05:00
Andy Friesen
e25b0a6275
Sync to upstream/release/591 (#1012)
* Fix a use-after-free bug in the new type cloning algorithm
* Tighten up the type of `coroutine.wrap`. It is now `<A..., R...>(f:
(A...) -> R...) -> ((A...) -> R...)`
* Break `.luaurc` out into a separate library target `Luau.Config`. This
makes it easier for applications to reason about config files without
also depending on the type inference engine.
* Move typechecking limits into `FrontendOptions`. This allows embedders
more finely-grained control over autocomplete's internal time limits.
* Fix stability issue with debugger onprotectederror callback allowing
break in non-yieldable contexts

New solver:

* Initial work toward [Local Type
Inference](0e1082108f/rfcs/local-type-inference.md)
* Introduce a new subtyping test. This will be much nicer than the old
test because it is completely separate both from actual type inference
and from error reporting.

Native code generation:

* Added function to compute iterated dominance frontier
* Optimize barriers in SET_UPVALUE when tag is known
* Cache lua_State::global in a register on A64
* Optimize constant stores in A64 lowering
* Track table array size state to optimize array size checks
* Add split tag/value store into a VM register
* Check that spills can outlive the block only in specific conditions

---------

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-08-18 11:15:41 -07:00
Andy Friesen
bdc2f6907b Merge branch 'upstream' into merge 2023-08-18 10:12:05 -07:00
Andy Friesen
434e618378 Merge branch 'master' into merge 2023-08-18 10:11:38 -07:00
Andy Friesen
433d966ea8 Sync to upstream/release/591 2023-08-18 10:06:29 -07:00
Maciej Barć
a2a47104c8
LinterConfig.h: add missing stdint.h include (#1010) 2023-08-14 06:10:56 -07:00
vegorov-rbx
d98256bb80
Sync to upstream/release/590 (#1008)
* Better indentation in multi-line type mismatch error messages
* Error message clone can no longer cause a stack overflow (when
typechecking with retainFullTypeGraphs set to false); fixes
https://github.com/Roblox/luau/issues/975
* `string.format` with %s is now ~2x faster on strings smaller than 100
characters

Native code generation:
* All VM side exits will block return to the native execution of the
current function to preserve correctness
* Fixed executable page allocation on Apple platforms when using
hardened runtime
* Added statistics for code generation (no. of functions compiler,
memory used for different areas)
* Fixed issue with function entry type checks performed more that once
in some functions
2023-08-11 07:42:37 -07:00
Vyacheslav Egorov
50571a7a15 Merge branch 'upstream' into merge 2023-08-11 15:57:09 +03:00
Vyacheslav Egorov
d841a32095 Merge branch 'master' into merge 2023-08-11 15:57:03 +03:00
Vyacheslav Egorov
089da9e924 Sync to upstream/release/590 2023-08-11 15:55:30 +03:00
Arseny Kapoulkine
bd229816c0 Remove old benchmark-dev workflows
Nobody is maintaining this and we haven't really used it and are
unlikely to start due to a high degree of noise and lack of dedicated
machines for this setup. Callgrind has worked for us well enough, with
additional profiling performed locally by engineers - this is not
perfect but it doesn't look like we have a path to making this better.
2023-08-08 12:15:10 -07:00
John Hui
3f478bb439
Add luau-compile to .gitignore (#995) 2023-08-07 16:26:16 -07:00
LoganDark
2b03c8f72a
Use const char* const* over const char** (#1005)
The FFI difference becomes significant in languages like Rust where you
can't necessarily just cast a const pointer.
2023-08-07 13:45:04 -07:00
Andy Friesen
0b2755f964
Sync to upstream/release/589 (#1000)
* Progress toward a diffing algorithm for types. We hope that this will
be useful for writing clearer error messages.
* Add a missing recursion limiter in `Unifier::tryUnifyTables`. This was
causing a crash in certain situations.
* Luau heap graph enumeration improvements:
    * Weak references are not reported
    * Added tag as a fallback name of non-string table links
* Included top Luau function information in thread name to understand
where thread might be suspended
* Constant folding for `math.pi` and `math.huge` at -O2
* Optimize `string.format` and `%*`
* This change makes string interpolation 1.5x-2x faster depending on the
number and type of formatted components, assuming a few are using
primitive types, and reduces associated GC pressure.

New type checker:

* Initial work toward tracking the upper and lower bounds of types
accurately.

Native code generation (JIT):

* Add IrCmd::CHECK_TRUTHY for improved assert fast-calls
* Do not compute type map for modules without types
* Capture metatable+readonly state for NEW_TABLE IR instructions
* Replace JUMP_CMP_ANY with CMP_ANY and existing JUMP_EQ_INT
* Add support for exits to VM with reentry lock in VmExit

---------

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-08-04 12:18:54 -07:00
Vyacheslav Egorov
63d9e3e209 Smaller recursion limit for NoOpt in the failing test 2023-08-04 21:07:54 +03:00
Andy Friesen
b26eeacfd2 Merge branch 'upstream' into merge 2023-08-04 10:02:13 -07:00
Andy Friesen
e25b717e54 Merge branch 'master' into merge 2023-08-04 10:02:07 -07:00
Andy Friesen
25cc75b096 * Progress toward a diffing algorithm for types. We hope that this will be useful for writing clearer error messages.
* Add a missing recursion limiter in `Unifier::tryUnifyTables`.  This was causing a crash in certain situations.
* Luau heap graph enumeration improvements:
    * Weak references are not reported
    * Added tag as a fallback name of non-string table links
    * Included top Luau function information in thread name to understand where thread might be suspended
* Constant folding for `math.pi` and `math.huge` at -O2
* Optimize `string.format` and `%*`
    * This change makes string interpolation 1.5x-2x faster depending on the number and type of formatted components, assuming a few are using primitive types, and reduces associated GC pressure.

New solver

* Initial work toward tracking the upper and lower bounds of types more accurately.

JIT

* Add IrCmd::CHECK_TRUTHY for improved assert fast-calls
* Do not compute type map for modules without types
* Capture metatable+readonly state for NEW_TABLE IR instructions
* Replace JUMP_CMP_ANY with CMP_ANY and existing JUMP_EQ_INT
* Add support for exits to VM with reentry lock in VmExit
2023-08-04 10:01:35 -07:00
Federico Cassano
fff897a75f
Fix some EBNF cases in grammar.md (#999) 2023-08-02 13:20:57 -07:00
Someon1e
8b510d3dbb
Add dark theme (#991)
Fixes #333 


![image](https://github.com/Roblox/luau/assets/120410318/539fec9f-3ad1-49cd-ba2f-a47b6c1ee42c)

![image](https://github.com/Roblox/luau/assets/120410318/981981c6-2fbe-46ae-99b1-a1fc2c647a6a)

---------

Co-authored-by: Someone-dQw4w9WgXcQ <dcheunggb@outlook.com>
2023-08-01 09:14:02 -07:00
vegorov-rbx
a08c3409b8
Luau Recap: July 2023 (#993)
If this does make it to the Roblox forum, I think I will cut intro about
new type solver and JIT.


[Rendered](https://github.com/vegorov-rbx/luau/blob/post-recap-july-2023/docs/_posts/2023-07-28-luau-recap-july-2023.md)


[Preview](https://vegorov-rbx.github.io/luau/2023/07/28/luau-recap-july-2023.html)
2023-07-31 13:02:24 -07:00
vegorov-rbx
76f67e0733
Sync to upstream/release/588 (#992)
Type checker/autocomplete:
* `Luau::autocomplete` no longer performs typechecking internally, make
sure to run `Frontend::check` before performing autocomplete requests
* Autocomplete string suggestions without "" are now only suggested
inside the ""
* Autocomplete suggestions now include `function (anonymous autofilled)`
key with a full suggestion for the function expression (with arguments
included) stored in `AutocompleteEntry::insertText`
* `AutocompleteEntry::indexedWithSelf` is provided for function call
suggestions made with `:`
* Cyclic modules now see each other type exports as `any` to prevent
memory use-after-free (similar to module return type)

Runtime:
* Updated inline/loop unroll cost model to better handle assignments
(Fixes https://github.com/Roblox/luau/issues/978)
* `math.noise` speed was improved by ~30%
* `table.concat` speed was improved by ~5-7%
* `tonumber` and `tostring` now have fastcall paths that execute ~1.5x
and ~2.5x faster respectively (fixes #777)
* Fixed crash in `luaL_typename` when index refers to a non-existing
value
* Fixed potential out of memory scenario when using `string.sub` or
`string.char` in a loop
* Fixed behavior of some fastcall builtins when called without arguments
under -O2 to match original functions
* Support for native code execution in VM is now enabled by default
(note: native code still has to be generated explicitly)
* `Codegen::compile` now accepts `CodeGen_OnlyNativeModules` flag. When
set, only modules that have a `--!native` hot-comment at the top will be
compiled to native code

In our new typechecker:
* Generic type packs are no longer considered to be variadic during
unification
* Timeout and cancellation now works in new solver
* Fixed false positive errors around 'table' and 'function' type
refinements
* Table literals now use covariant unification rules. This is sound
since literal has no type specified and has no aliases
* Fixed issues with blocked types escaping the constraint solver
* Fixed more places where error messages that should've been suppressed
were still reported
* Fixed errors when iterating over a top table type

In our native code generation (jit):
* 'DebugLuauAbortingChecks' flag is now supported on A64
* LOP_NEWCLOSURE has been translated to IR
2023-07-28 08:13:53 -07:00
Someon1e
087be529b0
Improve demo site (#990)
Updated builtins tables, made `` highlighted as a string, removed code
that replaced "stdin:" from print messages

Before:

![image](https://github.com/Roblox/luau/assets/120410318/ba399f0b-d612-4f69-a523-56369eafb037)
After:

![image](https://github.com/Roblox/luau/assets/120410318/98ab06c8-9b2a-4d9b-aa86-0a8432296417)

---------

Co-authored-by: Someone-dQw4w9WgXcQ <dcheunggb@outlook.com>
2023-07-28 08:11:16 -07:00
Vyacheslav Egorov
4c6a370872 Merge branch 'upstream' into merge 2023-07-28 15:05:40 +03:00
Vyacheslav Egorov
9fd79ade04 Merge branch 'master' into merge 2023-07-28 15:05:00 +03:00
Vyacheslav Egorov
5e1aca164c Sync to upstream/release/588 2023-07-28 14:37:00 +03:00
Arseny Kapoulkine
f80229c517
docs: Document return value for table.remove (#987)
Fixes #984.
2023-07-26 06:42:23 -07:00
MagelessMayhem
743afe6ec9
README.md: Add Gentoo Linux to listed distributions (#976)
Gentoo recently added Luau to their official overlay (the term they use
to describe their package repository) and it would only be appropriate
to include that detail here.

It may as well also be the first distribution to *officially* package
it, given the maintainer is one of Gentoo's own.

Signed-off-by: Sebastian France <MagelessMayhem@protonmail.com>
2023-07-24 07:21:44 -07:00
Alan Jeffrey
57368fbc58
HATRA 23 paper (#979)
A one-page "progress report" paper.
2023-07-21 15:05:26 -05:00
vegorov-rbx
218159140c
Sync to upstream/release/584 (#977)
* Added support for async typechecking cancellation using a token passed
through frontend options
* Added luaC_enumheap for building debug tools that need a graph of Luau
heap

In our new typechecker:
* Errors or now suppressed when checking property lookup of
error-suppressing unions

In our native code generation (jit):
* Fixed unhandled value type in NOT_ANY lowering
* Fast-call tag checks will exit to VM on failure, instead of relying on
a native fallback
* Added vector type to the type information
* Eliminated redundant direct jumps across dead blocks
* Debugger APIs are now disabled for call frames executing natively
* Implemented support for unwind registration on macOS 14
2023-07-14 11:08:53 -07:00
Vyacheslav Egorov
f16d002db9 GCC fix 2023-07-14 10:38:54 -07:00
Vyacheslav Egorov
4ffa98f4df Merge branch 'upstream' into merge 2023-07-14 08:59:07 -07:00
Vyacheslav Egorov
03063d09e4 Merge branch 'master' into merge 2023-07-14 08:59:01 -07:00
Vyacheslav Egorov
b403075573 Sync to upstream/release/584 2023-07-14 08:57:16 -07:00
Andy Friesen
e25de95445
Sync to upstream/release/583 (#974)
* Fixed indexing table intersections using `x["prop"]` syntax:
https://github.com/Roblox/luau/pull/971
* Add console output codepage for Windows:
https://github.com/Roblox/luau/pull/967
* Added `Frontend::parse` for a fast source graph preparation
* luau_load should check GC
* Work toward a type-diff system for nicer error messages

New Solver
* Correctly suppress errors in more cases
* Further improvements to typechecking of function calls and return
statements
* Crash fixes
* Propagate refinements drawn from the condition of a while loop into
the loop body

JIT
* Fix accidental bailout for math.frexp/modf/sign in A64
* Work toward bringing type annotation info in
* Do not propagate Luau IR constants of wrong type into load
instructions
* CHECK_SAFEENV exits to VM on failure
* Implement error handling in A64 reg allocator
* Inline the string.len builtin
* Do not enter native code of a function if arguments don’t match

---------

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-07-07 13:10:48 -07:00
Andy Friesen
dc2a1cc12c GCC fix. 2023-07-07 12:41:34 -07:00
Andy Friesen
b92d4d7d99 Merge branch 'upstream' into merge 2023-07-07 10:14:50 -07:00
Andy Friesen
01cbf185e8 Merge branch 'master' into merge 2023-07-07 10:14:46 -07:00
Andy Friesen
e00dbbeaf2 Sync to upstream/release/583 2023-07-07 10:14:35 -07:00
Lodinu Kalugalage
c98a9d7051
Add console output codepage for windows (#967)
Previously unicode codepoints would be incorrectly shown in windows
terminals, such as with u2503(┃):
```ps
❯ "print('\u{2503}')" | luau.exe
Γöâ
```

This change fixes the issue by setting the console's codepage on
windows, resulting in fixed behaviour:
```ps
❯ "print('\u{2503}')" | luau.exe
┃
```
2023-07-06 13:00:34 -07:00
JohnnyMorganz
a02eb78c96
Fix indexing table intersections using x["prop"] syntax (#971)
Right now, you can index an intersection of tables fine when using
`x.prop` syntax, but not when using `x["prop"]` syntax.
This fixes it for the latter case

```lua
type Foo = {
	Bar: string,
} & { Baz: number }

local x: Foo = { Bar = "1", Baz = 2 }
local y = x["Bar"] -- TypeError: Expected type table, got '{| Bar: string |} & {| Baz: number |}' instead
```

Part of a fix to
https://github.com/Roblox/luau/issues/533#issuecomment-1291779353
2023-07-05 14:32:51 -07:00
vegorov-rbx
76bea81a7b
Sync to upstream/release/582 (#960)
* Optimized operations like instantiation and module export for very
large types

In our new typechecker:
* Typechecking of function calls was rewritten to handle more cases
correctly
* Fixed a crash that can happen after self-referential type is exported
from a module
* Fixed a false positive error in string comparison
* Added handling of `for...in` variable type annotations and fixed
issues with the iterator call inside
* Self-referential 'hasProp' and 'setProp' constraints are now handled
correctly
 
In our native code generation (jit):
* Added '--target' argument to luau-compile to test multiple
architectures different from host architecture
* GC barrier tag check is skipped if type is already known to be
GC-collectable
* Added GET_TYPE/GET_TYPEOF instructions for type/typeof fast-calls
* Improved code size of interrupt handlers on X64
2023-06-23 23:19:39 -07:00
Vyacheslav Egorov
e1a7c3b0f1 Merge branch 'upstream' into merge 2023-06-24 08:34:11 +03:00
Vyacheslav Egorov
7a2f94a6a0 Merge branch 'master' into merge 2023-06-24 08:34:06 +03:00
Vyacheslav Egorov
8bc2f51d89 Sync to upstream/release/582 2023-06-24 08:33:44 +03:00
Andy Friesen
d458d240cd
Sync to upstream/release/581 (#958)
* Definition files can now ascribe indexers to class types.
(https://github.com/Roblox/luau/pull/949)
* Remove --compile support from the REPL. You can just use luau-compile
instead.
* When an exception is thrown during parallel typechecking (usually an
ICE), we now gracefully stop typechecking and drain active workers
before rethrowing the exception.

New solver

* Include more source location information when we hit an internal
compiler error
* Improve the logic that simplifies intersections of tables

JIT

* Save testable type annotations to bytecode
* Improve block placement for linearized blocks
* Add support for lea reg, [rip+offset] for labels
* Unify X64 and A64 codegen for RETURN
* Outline interrupt handlers for X64
* Remove global rArgN in favor of build.abi
* Change A64 INTERRUPT lowering to match X64

---------

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-06-16 10:35:18 -07:00
Andy Friesen
00b9898ffc Merge branch 'upstream' into merge 2023-06-16 10:06:38 -07:00
Andy Friesen
f9cfef16c2 Merge branch 'master' into merge 2023-06-16 10:06:33 -07:00
Andy Friesen
6ee4f190ab Sync to upstream/release/581 2023-06-16 10:01:18 -07:00
JohnnyMorganz
bc0722471f
Add support for ClassType indexer in definition files (#949) 2023-06-12 13:02:54 -07:00
RadiatedExodus
7fa69377be
Fix Arch Linux install instructions to use AUR (#953)
The existing instruction is incorrect, the luau package exists on the
Arch Linux User Repository (AUR), not at the official repository.

Feel free to suggest edits if something is wrong/missing, thanks!

(I also have a pkgbuild that builds every executable possible excluding
tests (repl, analyze, ast, reduce), however I did not include this since
releases only include repl and analyze)
2023-06-12 12:06:28 -07:00
Arseny Kapoulkine
ba65b0e9cd
Build and use luau-compile in CI (#952) 2023-06-12 08:46:44 -07:00
vegorov-rbx
3ecd3a82ab
Sync to upstream/release/580 (#951)
* Added luau-compile executable target to build/test compilation without
having full REPL included.

In our new typechecker:
* Fixed the order in which constraints are checked to get more
deterministic errors in different environments
* Fixed `isNumber`/`isString` checks to fix false positive errors in
binary comparisons
* CannotCallNonFunction error is reported when calling an intersection
type of non-functions
 
In our native code generation (jit):
* Outlined X64 return instruction code to improve code size
* Improved performance of return instruction on A64
* Added construction of a dominator tree for future optimizations
2023-06-09 10:08:00 -07:00
Vyacheslav Egorov
212888c361 Fix build warning 2023-06-09 15:46:43 +03:00
Vyacheslav Egorov
a9becc9b70 Merge branch 'upstream' into merge 2023-06-09 15:21:03 +03:00
Vyacheslav Egorov
ef2e46e25e Merge branch 'master' into merge 2023-06-09 15:20:56 +03:00
Vyacheslav Egorov
88cd3dda87 Sync to upstream/release/580 2023-06-09 15:20:36 +03:00
Arseny Kapoulkine
febebde72a
Update performance.md (#948)
We haven't updated this document in a while and yet there was a fair
amount of performance work, some of which can be documented here.

Note that this is not fully comprehensive in that it excludes a lot of
internal tuning that is difficult to describe other than with "we made
things faster".
2023-06-06 15:11:29 -07:00
Ben Mactavsin
e78897229a
Fix website demo's string highlighting behaviour (#942)
Fixes #935:
* String literals that include `\z` escape sequence followed by newline
characters are now correctly highlighted.
* Unescaped backslash (`\`) character at the end of the line no longer
acts like the `\z` escape sequence inside string literals when
highlighting.
2023-06-06 11:22:31 -07:00
Ben Mactavsin
4f82a77396
Update lint.md to specify language on fenced code blocks which have none (#946)
Specified language on the last two fenced code blocks in the page as
they were inconsistent with the others on the same page.
2023-06-06 08:57:36 -07:00
Andy Friesen
63679f7288
Sync to upstream/release/579 (#943)
A pretty small changelist this week:

* When type inference fails to find any matching overload for a
function, we were declining to commit any changes to the type graph at
all. This was resulting in confusing type errors in certain cases. Now,
when a matching overload cannot be found, we always commit to the first
overload we tried.

JIT

* Fix missing variadic register invalidation in FALLBACK_GETVARARGS
* Add a missing null pointer check for the result of luaT_gettm

---------

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-06-02 12:52:15 -07:00
Andy Friesen
a3d22decf8 Merge branch 'upstream' into merge 2023-06-02 11:18:16 -07:00
Andy Friesen
349d6f4342 Merge branch 'master' into merge 2023-06-02 11:18:12 -07:00
Andy Friesen
f4357400c5 Sync to upstream/release/579 2023-06-02 11:17:31 -07:00
vegorov-rbx
271c509046
Sync to upstream/release/578 (#939)
* Fixed gcc warning about uninitialized `std::optional`
* Fixed inlining of functions when they are used to compute their own
arguments

In the new type solver:
* Type families that are not part of a function signature cannot be
resolved at instantiation time and will now produce an error. This will
be relaxed in the future when we get constraint clauses on function
signatures (internally)
* `never` type is now comparable
* Improved typechecking of `for..in` statements
* Fixed checks for number type in `Add` type family
* Performance was improved, with particularly large gains on large
projects

And in native code generation (jit):
* We eliminated the call instruction overhead when native code support
is enabled in the VM
* Small optimizations to arm64 lowering
* Reworked LOP_GETIMPORT handling to reduce assembly code size
* Fixed non-deterministic binary output
* Fixed bad code generation caused by incorrect SSA to VM register links
invalidation

---------

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
2023-05-25 14:36:34 -07:00
Vyacheslav Egorov
fc9557db1e Merge branch 'upstream' into merge 2023-05-25 23:47:23 +03:00
Vyacheslav Egorov
f97a897ccd Merge branch 'master' into merge 2023-05-25 23:47:17 +03:00
Vyacheslav Egorov
b8e9d07b20 Sync to upstream/release/578 2023-05-25 23:46:51 +03:00
Andy Friesen
721f6e10fb
Sync to upstream/release/577 (#934)
Lots of things going on this week:

* Fix a crash that could occur in the presence of a cyclic union. We
shouldn't be creating cyclic unions, but we shouldn't be crashing when
they arise either.
* Minor cleanup of `luau_precall`
* Internal change to make L->top handling slightly more uniform
* Optimize SETGLOBAL & GETGLOBAL fallback C functions.
* https://github.com/Roblox/luau/pull/929
* The syntax to the `luau-reduce` commandline tool has changed. It now
accepts a script, a command to execute, and an error to search for. It
no longer automatically passes the script to the command which makes it
a lot more flexible. Also be warned that it edits the script it is
passed **in place**. Do not point it at something that is not in source
control!

New solver

* Switch to a greedier but more fallible algorithm for simplifying union
and intersection types that are created as part of refinement
calculation. This has much better and more predictable performance.
* Fix a constraint cycle in recursive function calls.
* Much improved inference of binary addition. Functions like `function
add(x, y) return x + y end` can now be inferred without annotations. We
also accurately typecheck calls to functions like this.
* Many small bugfixes surrounding things like table indexers
* Add support for indexers on class types. This was previously added to
the old solver; we now add it to the new one for feature parity.

JIT

* https://github.com/Roblox/luau/pull/931
* Fuse key.value and key.tt loads for CEHCK_SLOT_MATCH in A64
* Implement remaining aliases of BFM for A64
* Implement new callinfo flag for A64
* Add instruction simplification for int->num->int conversion chains
* Don't even load execdata for X64 calls
* Treat opcode fallbacks the same as manually written fallbacks

---------

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-05-19 12:37:30 -07:00
Andy Friesen
123496b29c gcc fix. 2023-05-19 12:11:10 -07:00
Andy Friesen
f3a6e37324 Merge branch 'upstream' into merge 2023-05-19 12:03:51 -07:00
Andy Friesen
4fb7594eaa Merge branch 'master' into merge 2023-05-19 12:00:39 -07:00
Andy Friesen
eb7106016e Sync to upstream/release/577 2023-05-19 11:59:59 -07:00
Alex Orlenko
da0458bf6e
Add CodeGen C API (#931)
I'd like to experiment with the codegen feature (currently experimental)
and need a public C API for this.

This PR addresses this.
2023-05-18 04:03:29 -07:00
mundusnine
cf0ba32597
Enable compile-time user configuration for LUA_VECTOR_SIZE (#929)
Add #ifndef guard to enable users to define at compile-time without
having to modify the luau source.
2023-05-16 05:04:49 -07:00
vegorov-rbx
97965c7c0a
Sync to upstream/release/576 (#928)
* `ClassType` can now have an indexer defined on it. This allows custom
types to be used in `t[x]` expressions.
* Fixed search for closest executable breakpoint line. Previously,
breakpoints might have been skipped in `else` blocks at the end of a
function
* Fixed how unification is performed for two optional types `a? <: b?`,
previously it might have unified either 'a' or 'b' with 'nil'. Note that
this fix is not enabled by default yet (see the list in
`ExperimentalFlags.h`)

In the new type solver, a concept of 'Type Families' has been
introduced.
Type families can be thought of as type aliases with custom type
inference/reduction logic included with them.
For example, we can have an `Add<T, U>` type family that will resolve
the type that is the result of adding two values together.
This will help type inference to figure out what 'T' and 'U' might be
when explicit type annotations are not provided.
In this update we don't define any type families, but they will be added
in the near future.
It is also possible for Luau embedders to define their own type families
in the global/environment scope.

Other changes include:
* Fixed scope used to find out which generic types should be included in
the function generic type list
* Fixed a crash after cyclic bound types were created during unification

And in native code generation (jit):
* Use of arm64 target on M1 now requires macOS 13
* Entry into native code has been optimized. This is especially
important for coroutine call/pcall performance as they involve going
through a C call frame
* LOP_LOADK(X) translation into IR has been improved to enable type
tag/constant propagation
* arm64 can use integer immediate values to synthesize floating-point
values
* x64 assembler removes duplicate 64bit numbers from the data section to
save space
* Linux `perf` can now be used to profile native Luau code (when running
with --codegen-perf CLI argument)
2023-05-12 10:50:47 -07:00
Vyacheslav Egorov
59c2698970 Merge branch 'upstream' into merge 2023-05-12 15:30:03 +03:00
Vyacheslav Egorov
38ab2e5f71 Merge branch 'master' into merge 2023-05-12 15:29:52 +03:00
Vyacheslav Egorov
3247aabf75 Sync to upstream/release/576 2023-05-12 15:15:01 +03:00
JohnnyMorganz
8d8c7974f5
Use correct globalScope in on demand type checker (#923)
Fixes #922 

Unsure if this needs to be put behind another FFlag, since the on demand
type checker fflag currently exists
2023-05-11 04:35:56 -07:00
Jan
45c6476d41
Add missing include for integer types (#925)
Closes #924

`uintptr_t` and `uint32` (and possibly more) were undefined because
`cstdint` was not included.
2023-05-09 11:42:13 -07:00
Andy Friesen
0014905b69
Luau.Analyze.CLI must link pthreads on Linux. (#920) 2023-05-05 15:41:46 -07:00
Andy Friesen
f7c780164d Add pthread as a link dependency to Luau.Analyze.CLI for Linux. 2023-05-05 15:33:05 -07:00
Andy Friesen
8453570658
Sync to upstream/release/575 (#919)
* `Luau.Analyze.CLI` now has experimental support for concurrent type
checking. Use the option `-jN` where `N` is the number of threads to
spawn.
* Improve typechecking performance by ~17% by making the function
`Luau::follow` much more efficient.
* Tighten up the type of `os.date`
* Removed `ParseOptions::allowTypeAnnotations` and
`ParseOptions::supportContinueStatement`

New solver

* Improve the reliability of function overload resolution
* More work toward supporting parallel type checking
* Fix a bug in inference of `==` and `~=` which would erroneously infer
that the operands were `boolean`
* Better error reporting when `for...in` loops are used incorrectly.

CodeGen

* Fix unwind registration when libunwind is used on Linux
* Fixed replaced IR instruction use count
* Convert X64 unwind info generation to standard prologue
* Implement A64 unwind info support for Dwarf2
* Live in/out data for linear blocks is now created
* Add side-exit VM register requirements to the IR dump
* Reuse ConstPropState between block chains 
* Remove redundant base update

---------

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-05-05 14:52:49 -07:00
Andy Friesen
12c1edf6c6 This test fails on a64 so disable it for now. 2023-05-05 13:25:00 -07:00
Andy Friesen
95f0a04e06 Merge branch 'upstream' into merge 2023-05-05 12:57:26 -07:00
Andy Friesen
cc96f86c08 Merge branch 'master' into merge 2023-05-05 12:57:23 -07:00
Andy Friesen
716f63321a Sync to upstream/release/575 2023-05-05 12:57:12 -07:00
JohnnyMorganz
5891de6724
Fix grammar issues (#915)
- The type list for function declarations does not accept defaults, but
the grammar incorrectly allowed this.
- Allow the empty table type `{}`
- Allow composite types and types surrounded by parentheses (e.g.
`string??`, `(string)`, `(string | number) & boolean`)

---------

Co-authored-by: vegorov-rbx <75688451+vegorov-rbx@users.noreply.github.com>
2023-05-04 05:39:18 -07:00
JohnnyMorganz
e8c0550586
Improve the type for os.date using overloads (#874)
Improves the type of `os.date` based on its documentation
https://create.roblox.com/docs/reference/engine/libraries/os#date

We know that the DateTypeResult table is only returned when the format
string is either "*t" or "!*t", and otherwise its just a formatted
string. This changes the type to showcase this appropriately.

I put the more strict type first in the overload because the ordering
makes a difference

Note: the API docs used in studio
(https://github.com/MaximumADHD/Roblox-Client-Tracker/blob/roblox/api-docs/en-us.json)
may need to be updated to reflect the overloaded type instead.


![image](https://user-images.githubusercontent.com/19635171/226919224-e9e8703b-2729-44b0-93f6-434c01bda204.png)

It may be possible to add a lint of this too to provide analysis similar
to how `string.format` works with a FormatString error. Will leave that
for a different PR though
2023-05-01 08:09:05 -07:00
vegorov-rbx
4b267aa5c5
Sync to upstream/release/574 (#910)
* Added a limit on how many instructions the Compiler can safely produce
(reported by @TheGreatSageEqualToHeaven)

C++ API Changes:
* With work started on read-only and write-only properties,
`Property::type` member variable has been replaced with `TypeId type()`
and `setType(TypeId)` functions.
* New `LazyType` unwrap callback now has a `void` return type, all
that's required from the callback is to write into `unwrapped` field.

In our work on the new type solver, the following issues were fixed:

* Work has started to support https://github.com/Roblox/luau/pull/77 and
https://github.com/Roblox/luau/pull/79
* Refinements are no longer applied on l-values, removing some
false-positive errors
* Improved overload resolution against expected result type
* `Frontend::prepareModuleScope` now works in the new solver
* Cofinite strings are now comparable

And these are the changes in native code generation (JIT):

* Fixed MIN_NUM and MAX_NUM constant fold when one of the arguments is
NaN
* Added constant folding for number conversions and bit operations
* Value spilling and rematerialization is now supported on arm64
* Improved FASTCALL2K IR generation to support second argument constant
* Added value numbering and load/store propagation optimizations
* Added STORE_VECTOR on arm64, completing the IR lowering on this target
2023-04-28 12:55:13 -07:00
Vyacheslav Egorov
348f85cc1c Merge branch 'upstream' into merge 2023-04-28 14:57:08 +03:00
Vyacheslav Egorov
8bd3714566 Merge branch 'master' into merge 2023-04-28 14:56:34 +03:00
Vyacheslav Egorov
1c2ce0d731 Sync to upstream/release/574 2023-04-28 14:55:55 +03:00
JohnnyMorganz
8173491d86
Update grammar.md (#909)
Fix issues mentioned in https://github.com/Roblox/luau/issues/908
2023-04-24 14:21:26 -05:00
Andy Friesen
fe7621ee8c
Sync to upstream/release/573 (#903)
* Work toward affording parallel type checking
* The interface to `LazyType` has changed:
* `LazyType` now takes a second callback that is passed the `LazyType&`
itself. This new callback is responsible for populating the field
`TypeId LazyType::unwrapped`. Multithreaded implementations should
acquire a lock in this callback.
* Modules now retain their `humanReadableNames`. This reduces the number
of cases where type checking has to call back to a `ModuleResolver`.
* https://github.com/Roblox/luau/pull/902
* Add timing info to the Luau REPL compilation output

We've also fixed some bugs and crashes in the new solver as we march
toward readiness.
* Thread ICEs (Internal Compiler Errors) back to the Frontend properly
* Refinements are no longer applied to lvalues
* More miscellaneous stability improvements

Lots of activity in the new JIT engine:

* Implement register spilling/restore for A64
* Correct Luau IR value restore location tracking
* Fixed use-after-free in x86 register allocator spill restore
* Use btz for bit tests
* Finish branch assembly support for A64
* Codesize and performance improvements for A64
* The bit32 library has been implemented for arm and x64

---------

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-04-21 15:14:26 -07:00
Andy Friesen
1126490e24 Merge branch 'upstream' into merge 2023-04-21 14:45:20 -07:00
Andy Friesen
a0f9842ba2 Merge branch 'master' into merge 2023-04-21 14:41:12 -07:00
Andy Friesen
d5cdb687e0 Sync to upstream/release/573 2023-04-21 14:41:03 -07:00
JohnnyMorganz
8ed808eb52
Add prefix and name location to AstTypeReference (#902)
Closes #901
2023-04-17 07:19:56 -07:00
vegorov-rbx
d141a5c48d
Sync to upstream/release/572 (#899)
* Fixed exported types not being suggested in autocomplete
* `T...` is now convertible to `...any` (Fixes
https://github.com/Roblox/luau/issues/767)
* Fixed issue with `T?` not being convertible to `T | T` or `T?`
(sometimes when internal pointer identity is different)
* Fixed potential crash in missing table key error suggestion to use a
similar existing key
* `lua_topointer` now returns a pointer for strings

C++ API Changes:
* `prepareModuleScope` callback has moved from TypeChecker to Frontend
* For LSPs, AstQuery functions (and `isWithinComment`) can be used
without full Frontend data

A lot of changes in our two experimental components as well.

In our work on the new type-solver, the following issues were fixed:
* Fixed table union and intersection indexing
* Correct custom type environments are now used
* Fixed issue with values of `free & number` type not accepted in
numeric operations

And these are the changes in native code generation (JIT):
* arm64 lowering is almost complete with support for 99% of IR commands
and all fastcalls
* Fixed x64 assembly encoding for extended byte registers
* More external x64 calls are aware of register allocator
* `math.min`/`math.max` with more than 2 arguments are now lowered to IR
as well
* Fixed correctness issues with `math` library calls with multiple
results in variadic context and with x64 register conflicts
* x64 register allocator learnt to restore values from VM memory instead
of always using stack spills
* x64 exception unwind information now supports multiple functions and
fixes function start offset in Dwarf2 info
2023-04-14 11:06:22 -07:00
Vyacheslav Egorov
33b95582ac Build fix 2023-04-14 16:46:13 +03:00
Vyacheslav Egorov
1ffbf6cd14 Merge branch 'upstream' into merge 2023-04-14 15:06:30 +03:00
Vyacheslav Egorov
70ef0fbc83 Merge branch 'master' into merge 2023-04-14 15:06:24 +03:00
Vyacheslav Egorov
5e771b87ae Sync to upstream/release/572 2023-04-14 15:05:27 +03:00
Petri Häkkinen
7345891f6b
Add lua_getuserdatadtor (#870)
Some userdata objects may need to support manual destruction in addition
to automatic GC. For example, files, threads, GPU resources and objects
with large external allocations.

With Lua, a finalizer can be _generically_ called by invoking the __gc
metamethod manually, but this is currently not possible with tagged
userdata in Luau because it's not possible to query the destructor
associated with an userdata. While it is possible to workaround this by
duplicating the destructor table locally on client side (*), it's more
convenient to deduplicate the data and get the destructor using the API
instead.

(*) Note: a separate destructor table for each VM may be required if the
VMs use different set of tags.

Implementation notes:

1. I first considered adding a typedef for lua_Destructor but
unfortunately there are two kinds of destructors, one with and one
without the lua_State* argument, so I decided against it at this point.
Maybe it should be added later if the destructor API is unified (by
dropping the Lua state pointer argument?).

2. For some reason the conformance test produced warning "qualifier
applied to function type has no meaning; ignored" on VS2017 (possibly
because the test framework does not like function pointers for some
reason?). I silenced this by pulling out the test expressions from those
CHECKs.
2023-04-11 12:46:55 -07:00
Andy Friesen
ba67fb275e
Sync to upstream/release/571 (#895)
* `table.sort` was improved further. It now guarentees N*log(N) time
complexity in the worst case.
* Fix https://github.com/Roblox/luau/issues/880

We are also working on fixing final bugs and crashes in the new type
solver.

On the CodeGen front we have a few things going on:
* We have a smarter register allocator for the x86 JIT
* We lower more instructions on arm64
* The vector constructor builtin is now translated to IR

---------

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-04-07 14:01:29 -07:00
Andy Friesen
395bf5cdf5 Merge branch 'upstream' into merge 2023-04-07 12:56:54 -07:00
Andy Friesen
97f8bf6cd8 Merge branch 'master' into merge 2023-04-07 12:56:51 -07:00
Andy Friesen
5309401f49 Sync to upstream/release/571 2023-04-07 12:56:27 -07:00
vegorov-rbx
d148d7d574
Luau Recap: March 2023 (#886) 2023-03-31 11:52:31 -07:00
vegorov-rbx
1212fdacbf
Sync to upstream/release/570 (#885)
Once again, all of our changes this week are for new type solver and the
JIT.

In the new type solver, we fixed cyclic type alias handling and multiple
stability issues.

In the JIT, our main progress was for arm64, where, after lowering 36%
of instructions, we start seeing first Luau functions executing
natively.
For x64, we performed code cleanup and refactoring to allow for future
optimizations.
2023-03-31 11:42:49 -07:00
Vyacheslav Egorov
d071e410ce g++ build fix 2023-03-31 16:25:13 +03:00
Vyacheslav Egorov
d70df6362e Merge branch 'upstream' into merge 2023-03-31 15:23:00 +03:00
Vyacheslav Egorov
c6b6ab6150 Merge branch 'master' into merge 2023-03-31 15:22:57 +03:00
Vyacheslav Egorov
d1acde36bb Sync to upstream/release/570 2023-03-31 15:21:14 +03:00
Andy Friesen
b4ebad4862
Sync to upstream/release/569 (#878)
All of our changes this week have been focused on the new type solver
and the JIT.

As we march toward feature parity with the old solver, we've tightened
up a bunch of lingering issues with overload resolution, unsealed
tables, and type normalization. We've also fixed a bunch of crashes and
assertion failures in the new solver.

On the JIT front, we've started work on an A64 backend, improved the IR
analysis in a bunch of cases, and implemented assembly generation for
the builtin functions `type()` and `typeof()`.

---------

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-03-24 11:03:04 -07:00
Andy Friesen
c9554518c6 Merge branch 'upstream' into merge 2023-03-24 10:35:02 -07:00
Andy Friesen
ce188220ba Merge branch 'master' into merge 2023-03-24 10:34:57 -07:00
Andy Friesen
81200e13f6 Sync to upstream/release/569 2023-03-24 10:34:14 -07:00
Petri Häkkinen
0d6aacf407
RFC: Floor division operator (#832)
[Rendered](https://github.com/petrihakkinen/luau/blob/rfc-floor-division/rfcs/syntax-floor-division-operator.md)
2023-03-20 15:12:48 -07:00
Arseny Kapoulkine
a4b31f940a
Update compatibility.md
Remove weak opposition to `//` operator as we decided to merge the RFC to support it.
2023-03-20 11:09:58 -07:00
vegorov-rbx
42a2805f85
Sync to upstream/release/568 (#865)
* A small subset of control-flow refinements have been added to
recognize type options that are unreachable after a
conditional/unconditional code block. (Fixes
https://github.com/Roblox/luau/issues/356).

Some examples:
```lua
local function f(x: string?)
    if not x then return end

    -- x is 'string' here
end
```
Throwing calls like `error` or `assert(false)` instead of 'return' are
also recognized.
Existing complex refinements like type/typeof and tagged union checks
are expected to work, among others.

To enable this feature, `LuauTinyControlFlowAnalysis` exclusion has to
be removed from `ExperimentalFlags.h`.
If will become enabled unconditionally in the near future.

* Linter has been integrated into the typechecker analysis so that
type-aware lint warnings can work in any mode
`Frontend::lint` methods were deprecated, `Frontend::check` has to be
used instead with `runLintChecks` option set.
Resulting lint warning are located inside `CheckResult`.

* Fixed large performance drop and increased memory consumption when
array is filled at an offset (Fixes
https://github.com/Roblox/luau/issues/590)
* Part of [Type error suppression
RFC](https://github.com/Roblox/luau/blob/master/rfcs/type-error-suppression.md)
was implemented making subtyping checks with `any` type transitive.

---
In our work on the new type-solver:
* `--!nocheck` mode no longer reports type errors
* New solver will not be used for `--!nonstrict` modules until all
issues with strict mode typechecking are fixed
* Added control-flow aware type refinements mentioned earlier

In native code generation:
* `LOP_NAMECALL` has been translated to IR
* `type` and `typeof` builtin fastcalls have been translated to
IR/assembly
* Additional steps were taken towards arm64 support
2023-03-17 12:20:37 -07:00
Vyacheslav Egorov
1c4d7a6be7 Merge branch 'upstream' into merge 2023-03-17 17:00:29 +02:00
Vyacheslav Egorov
bbeec108a7 Merge branch 'master' into merge 2023-03-17 17:00:25 +02:00
Vyacheslav Egorov
e280064f45 Sync to upstream/release/568 2023-03-17 16:59:30 +02:00
Alan Jeffrey
9311c0c57a
Add RFC for type error suppression (#835)
This formalizes our strategy for suppressing type errors, and fixes the
weirdness of `any` being both a top and bottom type.
2023-03-13 19:33:27 -05:00
Arseny Kapoulkine
29d0ea10cf
Add DCR solver to analysis benchmarks (#862)
For now just do this in strict mode. This will help us track performance
over time, although for now the behavior is going to keep changing so
it's not going to be a fully solid metric for a few weeks.
2023-03-13 13:58:29 -07:00
Andy Friesen
1fa8311a18
Sync to upstream/release/567 (#860)
* Fix #817 
* Fix #850 
* Optimize math.floor/ceil/round with SSE4.1
    * Results in a ~7-9% speedup on the math-cordic benchmark.
* Optimized table.sort.
* table.sort is now ~4.1x faster (when not using a predicate) and ~2.1x
faster when using a simple predicate. Performance may improve further in
the future.
* Reorganize the memory ownership of builtin type definitions.
* This is a small initial step toward affording parallel typechecking.

The new type solver is coming along nicely. We are working on fixing
crashes and bugs.

A few major changes to native codegen landed this week:
* Fixed lowering of Luau IR mod instruction when first argument is a
constant
* Added VM register data-flow/capture analysis
* Fixed issues with optimizations in unreachable blocks

---------

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-03-10 12:21:07 -08:00
Andy Friesen
a0fe195305 Merge branch 'upstream' into merge 2023-03-10 11:21:41 -08:00
Andy Friesen
cfee765dc7 Merge branch 'master' into merge 2023-03-10 11:20:20 -08:00
Andy Friesen
4653484913 Sync to upstream/release/567 2023-03-10 11:20:04 -08:00
niansa/tuxifan
78798d4641
Fixed lerp() duplicate when compiling for C++20 (#851) 2023-03-06 04:42:59 -08:00
vegorov-rbx
140e5a1495
Sync to upstream/release/566 (#853)
* Fixed incorrect lexeme generated for string parts in the middle of an
interpolated string (Fixes https://github.com/Roblox/luau/issues/744)
* DeprecatedApi lint can report some issues without type inference
information
* Fixed performance of autocomplete requests when suggestions have large
intersection types (Solves
https://github.com/Roblox/luau/discussions/847)
* Marked `table.getn`/`foreach`/`foreachi` as deprecated ([RFC:
Deprecate
table.getn/foreach/foreachi](https://github.com/Roblox/luau/blob/master/rfcs/deprecate-table-getn-foreach.md))
* With -O2 optimization level, we now optimize builtin calls based on
known argument/return count.
Note that this change can be observable if `getfenv/setfenv` is used to
substitute a builtin, especially if arity is different.
Fastcall heavy tests show a 1-2% improvement.
* Luau can now be built with clang-cl (Fixes
https://github.com/Roblox/luau/issues/736)

We also made many improvements to our experimental components.

For our new type solver:
* Overhauled data flow analysis system, fixed issues with 'repeat'
loops, global variables and type annotations
* Type refinements now work on generic table indexing with a string
literal
* Type refinements will properly track potentially 'nil' values (like
t[x] for a missing key) and their further refinements
* Internal top table type is now isomorphic to `{}` which fixes issues
when `typeof(v) == 'table'` type refinement is handled
* References to non-existent types in type annotations no longer resolve
to 'error' type like in old solver
* Improved handling of class unions in property access expressions
* Fixed default type packs
* Unsealed tables can now have metatables
* Restored expected types for function arguments

And for native code generation:
* Added min and max IR instructions mapping to vminsd/vmaxsd on x64
* We now speculatively extract direct execution fast-paths based on
expected types of expressions which provides better optimization
opportunities inside a single basic block
* Translated existing math fastcalls to IR form to improve tag guard
removal and constant propagation
2023-03-03 12:21:14 -08:00
Vyacheslav Egorov
ac9718e31a Merge branch 'upstream' into merge 2023-03-03 15:53:22 +02:00
Vyacheslav Egorov
ca34b44be6 Merge branch 'master' into merge 2023-03-03 15:53:16 +02:00
Vyacheslav Egorov
9a281f0492 Sync to upstream/release/566 2023-03-03 15:45:38 +02:00
Arseny Kapoulkine
48172dd5b1
Update SECURITY.md
Note that native code gen is currently exempt from any security guarantees as it's a pre-production R&D component right now. This will change in the future as we deploy it to production.
2023-03-01 14:40:40 -08:00
Arseny Kapoulkine
6601c41bff
Correct table.getn/foreach deprecation RFC (#848)
It looks like all three functions actually have been deprecated in Lua
5.1, and removed in Lua 5.2.

We do not plan to remove them to retain backwards compatibility, but the
RFC should be more precise.
2023-02-27 12:30:33 -08:00
Arseny Kapoulkine
aef99ae0d4
docs: Add deprecation notice to getn/foreach/foreachi (#849)
RFC:
https://github.com/Roblox/luau/blob/master/rfcs/deprecate-table-getn-foreach.md
2023-02-27 12:30:23 -08:00
Andy Friesen
d2ab5df62b
Sync to upstream/release/565 (#845)
We've made a few small changes to reduce the amount of stack we use when
typechecking nested method calls (eg `foo:bar():baz():quux()`).

We've also fixed a small bytecode compiler issue that caused us to emit
redundant jump instructions in code that conditionally uses `break` or
`continue`.

On the new solver, we've switched to a new, better way to handle
augmentations to unsealed tables. We've also made some substantial
improvements to type inference and error reporting on function calls.
These things should both be on par with the old solver now.

The main improvements to the native code generator have been elimination
of some redundant type tag checks. Also, we are starting to inline
particular fastcalls directly to IR.

---------

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-02-24 13:49:38 -08:00
Andy Friesen
e58bb1b27f GCC fix. 2023-02-24 10:29:24 -08:00
Andy Friesen
7a822c9429 Merge branch 'upstream' into merge 2023-02-24 10:25:04 -08:00
Andy Friesen
3974fcc0e9 Merge branch 'master' into merge 2023-02-24 10:24:58 -08:00
Andy Friesen
1e7b23fbfc Sync to upstream/release/565 2023-02-24 10:24:22 -08:00
B. Gibbons
fbd93cbc03
docs: update readme installation guide (#792)
1. Added information about Homebrew installation (based off this
information [here](https://formulae.brew.sh/formula/luau#default))
2. Added details related to installing binary files that are downloaded
3. Rearranged and added titles for easier reading

Fixes #791.

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
2023-02-23 13:08:45 -08:00
vegorov-rbx
b570ff0a37
Sync to upstream/release/564 (#841)
This week we only have updates to new type solver and JIT. Both projects
are still in the process of being built out. Neither are ready for
general use yet.

In the new solver, we fixed issues with recursive type aliases.
Duplicated type parameters are once again reported, exported types are
being recorder and function argument names are placed inside function
types.
We also made improvements to restore parts of bidirectional type
tracking.

On native code generation side, namecall instruction lowering was fixed,
we fixed inconsistencies in IR command definitions and added utility
function to help with constant folding.
2023-02-17 15:41:51 -08:00
Vyacheslav Egorov
be06c31ce5 Merge branch 'upstream' into merge 2023-02-17 16:55:33 +02:00
Vyacheslav Egorov
1f8ec89465 Merge branch 'master' into merge 2023-02-17 16:55:28 +02:00
Vyacheslav Egorov
5c77305609 Sync to upstream/release/564 2023-02-17 16:53:37 +02:00
Arseny Kapoulkine
2e5f95ca58
Update STATUS.md 2023-02-14 12:25:58 -08:00
Arseny Kapoulkine
8f37fc6dfc
RFC: Deprecate table.getn/foreach/foreachi (#715)
[Rendered](https://github.com/Roblox/luau/blob/zeux-rfc-deptable/rfcs/deprecate-table-getn-foreach.md)
2023-02-14 12:25:12 -08:00
Arseny Kapoulkine
742702a17d
Update lower-bounds-calculation.md
This RFC is abandoned; for posterity we will keep this in the tree for now, as it's likely that it represents a subset of the future set of LTI rules
2023-02-14 12:23:54 -08:00
Andy Friesen
c5089def6e
Sync to upstream/release/563 (#833)
* Fix a bug where reading a property from an unsealed table caused
inference to improperly infer the existence of that property.
* Fix #827

We have also made a lot of progress on the new solver and the JIT. Both
projects are still in the process of being built out. Neither are ready
for general use yet.

We are mostly working to tighten up how the new solver handles
refinements and updates to unsealed tables to bring it up to the same
level as the old solver.

---------

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-02-10 11:40:38 -08:00
Andy Friesen
c3b194538f Merge branch 'upstream' into merge 2023-02-10 10:51:49 -08:00
Andy Friesen
41ff9cbe64 Merge branch 'master' into merge 2023-02-10 10:51:45 -08:00
Andy Friesen
b388e27995 Sync to upstream/release/563 2023-02-10 10:50:54 -08:00
Epix
18a1dc3440
Fix comments on jump instructions with AUX (#808)
It seems like for jump instructions that include an AUX 32-bit word,
that index is included as part of the jump offset.
2023-02-09 06:12:58 -08:00
vegorov-rbx
c5555c6573
News about string interpolation feature and a syntax page update (#829)
News can be viewed
[here](https://vegorov-rbx.github.io/luau/2023/02/02/luau-string-interpolation.html)
And syntax can be viewed
[here](https://vegorov-rbx.github.io/luau/syntax#string-interpolation)

Note that link appears to be broken in the news section. It goes to
`https://vegorov-rbx.github.io/syntax#string-interpolation` instead of
`https://vegorov-rbx.github.io/luau/syntax#string-interpolation`
The link I use in the source is `/syntax#string-interpolation` but the
root 'offset' is different on Luau website (doesn't have extra 'luau'
folder) and on my GitHub pages.
Hope this is ok. But if GitHub page masters know how to avoid this,
please let me know.

---------

Co-authored-by: Alan Jeffrey <403333+asajeffrey@users.noreply.github.com>
Co-authored-by: Alexander McCord <11488393+alexmccord@users.noreply.github.com>
2023-02-03 11:41:13 -08:00
vegorov-rbx
62483d40f0
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification

A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions

Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.

---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.

Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker

---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 11:26:13 -08:00
Vyacheslav Egorov
c76dd1ce9f Merge branch 'upstream' into merge 2023-02-03 14:34:42 +02:00
Vyacheslav Egorov
f49f073621 Merge branch 'master' into merge 2023-02-03 14:34:37 +02:00
Vyacheslav Egorov
dba2936823 Sync to upstream/release/562 2023-02-03 14:34:12 +02:00
Andy Friesen
f763f4c948
Sync to upstream/release/561 (#820)
* Fix a potential debugger crash by adding checks for invalid stack
index values

---------

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-01-27 14:28:31 -08:00
Andy Friesen
523db5eaca Another GCC fix. 2023-01-27 14:15:57 -08:00
Andy Friesen
b7af49c8b5 Fix signed/unsigned comparison warnings on GCC. 2023-01-27 14:04:10 -08:00
Andy Friesen
a17481baca Merge branch 'upstream' into merge 2023-01-27 13:29:29 -08:00
Andy Friesen
7c5dd3c263 Merge branch 'master' into merge 2023-01-27 13:29:24 -08:00
Andy Friesen
53d03f94f7 Sync to upstream/release/561 2023-01-27 13:28:45 -08:00
vegorov-rbx
4a2e8013c7
Sync to upstream/release/560 (#810)
* For autocomplete, additional information is included in Scope for type
alias name locations and names of imported modules
* Improved autocomplete suggestions in 'for' and 'while' loop headers
* String match functions return types are now optional strings and
numbers because match is not guaranteed at runtime
* Fixed build issue on gcc 11 and up (Fixes
https://github.com/Roblox/luau/issues/806)
2023-01-20 12:27:03 -08:00
Vyacheslav Egorov
b0b7dfb714 Fix a few style changes that went out-of-sync 2023-01-20 14:18:59 +02:00
Vyacheslav Egorov
7a43ae3b37 Merge branch 'upstream' into merge 2023-01-20 14:16:10 +02:00
Vyacheslav Egorov
652f31958d Merge branch 'master' into merge 2023-01-20 14:03:22 +02:00
Vyacheslav Egorov
eec289ad1b Sync to upstream/release/560 2023-01-20 14:02:39 +02:00
Harold Cindy
729bc44729
Fix lua_*upvalue() when upvalue names aren't in debug info (#787)
`lua_getupvalue()` and `lua_setupvalue()` don't behave as expected when
working with Lua closure whose `Proto` has no debug info. The code
currently uses `sizeupvalues` to do bounds checking of upvalue indices,
but that's the size of the upvalue _names_ array. It will always be `0`
if the `Proto` doesn't have debug info.

This uses `nups` instead, and just returns `""` as the upvalue name if
we don't have one, same as for C closures.

Co-authored-by: Harold Cindy <HaroldCindy@users.noreply.github.com>
2023-01-18 06:00:13 -08:00
Andy Friesen
a5c6a38b10
Sync to upstream/release/559 (#804)
* Fix autocompletion of if-then-else expressions
* Fix a potential crash surrounding improper use of `%*` in a string
format specifier
* All Python scripts now invoke Python via `python3` rather than
`python`.
* Improved error handling for string interpolation with too many
arguments.

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-01-13 14:10:01 -08:00
Andy Friesen
efaf15ef7c Merge branch 'upstream' into merge 2023-01-13 12:38:14 -08:00
Andy Friesen
b1000a6889 Merge branch 'master' into merge 2023-01-13 12:38:09 -08:00
Andy Friesen
96c1cafff2 Sync to upstream/release/559 2023-01-13 12:36:28 -08:00
JohnnyMorganz
0af10417cd
Attach definition location to TableType (#801)
Having the location where the table type is defined is useful for
tracking other info such as documentation comments, or pointing back
through "Go To Definition" functionality etc.

This adds in the required info
2023-01-12 06:21:25 -08:00
JohnnyMorganz
86494918f5
Pass string content to autocomplete callback (#800)
Closes #718 

We pass an extra `contents` parameter to the string callback function to
provide contextual information for autocomplete.

Because we support both string literals and simple interpolated strings,
we pass it through as a `std::string` value.

This is a breaking change
2023-01-11 08:28:11 -08:00
vegorov-rbx
be52bd91e4
Sync to upstream/release/558 (#796)
* Fixed garbage data in module scopes when type graph is not retained
* LOP_MOVE with the same source and target registers is no longer
generated (Fixes https://github.com/Roblox/luau/issues/793)
2023-01-06 13:14:35 -08:00
vegorov-rbx
685ca02a30
Keep using ubuntu-20.04 in GitHub Actions (and clang++-10 in coverage) (#795)
### Problem
ubuntu-latest was updated to 22.04 which removes clang++-10 we used for
coverage stats and creates a pre-compiled binary that requires a glibc
upgrade https://github.com/Roblox/luau/issues/773

### Solution
Pin to ubuntu-20.04 using multi-value matrix configurations.
In coverage configuration, we use clang++-10 once again.
2023-01-06 12:17:25 -08:00
Vyacheslav Egorov
a2365f2adf Fix build warning 2023-01-06 18:31:52 +02:00
Vyacheslav Egorov
5db9675537 Smaller recursion limit to not hit stack overflow in debug on Windows 2023-01-06 18:30:05 +02:00
Vyacheslav Egorov
cf45ce6960 Merge branch 'upstream' into merge 2023-01-06 18:10:33 +02:00
Vyacheslav Egorov
5e3fbc4ba1 Merge branch 'master' into merge 2023-01-06 18:10:31 +02:00
Vyacheslav Egorov
36f5009026 Sync to upstream/release/558 2023-01-06 18:07:19 +02:00
vegorov-rbx
75a2e95714
Sync to upstream/release/557 (#794)
* Fixed unions of `nil` types displaying as `?`
* Internal normalization now handles class types which can make
previously failing (incorrectly) sub-typing checks to succeed
2023-01-04 12:53:17 -08:00
Vyacheslav Egorov
1958676f29 Re-using uncleared normalizer in unsafe 2023-01-04 14:45:18 +02:00
Vyacheslav Egorov
ee364a33b4 Fixed iterator invalidation issue 2023-01-04 00:31:14 +02:00
Vyacheslav Egorov
c48b4d7228 Merge branch 'upstream' into merge 2023-01-03 19:49:36 +02:00
Vyacheslav Egorov
11e129ff00 Merge branch 'master' into merge 2023-01-03 19:49:30 +02:00
Vyacheslav Egorov
9958d23caa Sync to upstream/release/557 2023-01-03 19:33:19 +02:00
Andy Friesen
fb2f146123
Sync to upstream/release/556 (#782)
* The AST JSON encoder will now stringify infinity and NaN according to the JSON5 spec using the tokens `Infinity`, `-Infinity`, and `NaN`.
* Improve autocompletion of table keys if the type of that key is a union of string singletons.
2022-12-09 11:57:01 -08:00
Andy Friesen
1197bcd5c7 Merge branch 'upstream' into merge 2022-12-09 10:08:20 -08:00
Andy Friesen
4ba102f304 Merge branch 'master' into merge 2022-12-09 10:08:16 -08:00
Andy Friesen
abe6768a1d Sync to upstream/release/556 2022-12-09 10:07:25 -08:00
Alex Orlenko
e9d4ee7a33
Make pseudo-indices relative to LUAI_MAXCSTACK (#766)
This is useful in particular if redefine `LUAI_MAXCSTACK` to a higher
value than the current one (8000).
Ie. passing `-D LUAI_MAXCSTACK=1000000` would not work as overlaps with
`LUA_REGISTRYINDEX` and below.
2022-12-06 11:20:24 -08:00
vegorov-rbx
4e6ae32625
Luau Recap: November 2022 (#764)
Co-authored-by: Andy Friesen <andy.friesen@gmail.com>
2022-12-02 10:23:20 -08:00
vegorov-rbx
59ae47db43
Sync to upstream/release/555 (#768)
* Type mismatch errors now mention if unification failed in covariant or
invariant context, to explain why sometimes derived class can't be
converted to base class or why `T` can't be converted into `T?` and so
on
* Class type indexing is no longer an error in non-strict mode (still an
error in strict mode)
* Fixed cyclic type packs not being displayed in the type
* Added an error when unrelated types are compared with `==`/`~=`
* Fixed false positive errors involving sub-type tests an `never` type
* Fixed miscompilation of multiple assignment statements (Fixes
https://github.com/Roblox/luau/issues/754)
* Type inference stability improvements
2022-12-02 10:09:59 -08:00
vegorov-rbx
91302d1d4c
Fix coverage github action by using clang++ without a specific version (#769)
Github fails to find clang++-10.
Since we install llvm without a specific version number, we shouldn't
use clang++-10 with a version number.

I tried installing llvm-10, but that package is not available on github
(any more?).
2022-12-02 10:08:55 -08:00
Vyacheslav Egorov
6cd507dff0 Work-around for gcc 2022-12-02 18:22:01 +02:00
Vyacheslav Egorov
f10b294d62 What even is this 2022-12-02 15:46:09 +02:00
Vyacheslav Egorov
621d21d4c2 Merge branch 'upstream' into merge 2022-12-02 12:58:55 +02:00
Vyacheslav Egorov
471ec75a60 Merge branch 'master' into merge 2022-12-02 12:56:19 +02:00
Vyacheslav Egorov
fc459699da Sync to upstream/release/555 2022-12-02 12:46:05 +02:00
JohnnyMorganz
9095fc4b83
Support __call on class type vars (#762)
Currently, the metatable of a class type var is not correctly checked to
see if it is callable (i.e. has a `__call` metatable).
We resolve this issue by checking the metatable in `checkCallOverload`

Fixes #756

Co-authored-by: vegorov-rbx <75688451+vegorov-rbx@users.noreply.github.com>
2022-11-28 10:02:41 -08:00
JohnnyMorganz
7dbe47f4dd
Handle cyclically referenced declared classes (#729)
Closes #631 

Performs a "two pass" approach, where we first call `prototype` on the
class definition.
The prototype phase checks for errors and creates a base CTV for the
class, which other types can then reference.

The second `check` phase fills it out with all the defined
properties/methods

Co-authored-by: vegorov-rbx <75688451+vegorov-rbx@users.noreply.github.com>
2022-11-28 05:25:44 -08:00
Andy Friesen
95d9c6d194
Sync to upstream/release/553 (#751)
* Autocomplete support for interpolated strings.
* Improved parse errors in various situations involving interpolated
strings.
2022-11-18 11:47:21 -08:00
Andy Friesen
3ac3798e79 Merge branch 'upstream' into merge 2022-11-18 10:52:52 -08:00
Andy Friesen
d5ff348e4e Merge remote-tracking branch 'origin/master' into merge 2022-11-18 10:52:40 -08:00
Andy Friesen
f52169509c Sync to upstream/release/554 2022-11-18 10:45:14 -08:00
boyned//Kampfkarren
aa7c64517c
Fix string interpolation autocomplete and location (#748)
String interpolation autocomplete was not working and was just using
default autocomplete. This fixes it so that inside a string it is
treated as normal, and inside an expression it suggests expression level
autocomplete.

Also fixes the range, which was previously just the first lexeme.
2022-11-16 10:15:01 -08:00
vegorov-rbx
816e41a8f2
Sync to upstream/release/553 (#742)
* Type inference of `a and b` and `a or b` has been improved (Fixes
https://github.com/Roblox/luau/issues/730)
* Improved error message when `for ... in x` loop iterates over a value
that could be 'nil'
* Return type of `next` not includes 'nil' (Fixes
https://github.com/Roblox/luau/issues/706)
* Improved type inference of `string` type
* Luau library table type names are now reported as `typeof(string)`/etc
instead of just `string` which was misleading
* Added parsing error when optional type alias type parameter wasn't
provided after `=` token
* Improved tagged union type refinement in conditional expressions, type
in `else` branch should no longer include previously handled union
options
2022-11-10 14:53:13 -08:00
Vyacheslav Egorov
2b36613293 Merge branch 'upstream' into merge 2022-11-11 00:12:57 +02:00
Vyacheslav Egorov
3289fc1b85 Merge remote-tracking branch 'upstream/master' into merge 2022-11-11 00:12:36 +02:00
Vyacheslav Egorov
3155ba0358 Sync to upstream/release/553 2022-11-11 00:04:44 +02:00
Arseny Kapoulkine
0f04d521e6
Add "Luau origins and evolution" post (#737) 2022-11-04 15:26:37 -07:00
Andy Friesen
c33700e473
Sync to upstream/release/552 (#735)
* Reduce the stack utilization of type checking.
* Improve the error message that's reported when a delimiting comma is
missing from a table literal. eg
```lua
local t = {
    first = 1
    second = 2
}```
2022-11-04 10:33:22 -07:00
Andy Friesen
8fd7d22046 Merge branch 'upstream' into merge 2022-11-04 10:04:57 -07:00
Andy Friesen
d189305fa8 Merge branch 'master' into merge 2022-11-04 10:03:15 -07:00
Andy Friesen
e3fdab3082 Sync to upstream/release/552 2022-11-04 10:02:37 -07:00
Alan Jeffrey
e43a9e927c
Added Sep-Oct 2022 Luau Recap (#726) 2022-11-01 12:02:03 -05:00
boyned//Kampfkarren
e37eb3c778
Fix { range to be within the interpolated string (#728)
Corrects `{` range to be inside the interpolated string, needed for syntax highlighting.
2022-10-28 12:22:26 -07:00
vegorov-rbx
a6b5051edc
Sync to upstream/release/551 (#727)
* https://github.com/Roblox/luau/pull/719
* Improved `Failed to unify type packs` error message to be reported as
`Type pack 'X' could not be converted into 'Y'`
* https://github.com/Roblox/luau/pull/722
* 1% reduction in executed instruction count by removing a check in fast
call dispatch
* Additional fixes to reported error location of OOM errors in VM
* Improve `math.sqrt`, `math.floor` and `math.ceil` performance on
additional compilers and platforms (1-2% geomean improvement including
8-9% on math-cordic)
* All thrown exceptions by Luau analysis are derived from
`Luau::InternalCompilerError`
* When a call site has fewer arguments than required, error now reports
the location of the function name instead of the argument to the
function
* https://github.com/Roblox/luau/pull/724
* Fixed https://github.com/Roblox/luau/issues/725

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
2022-10-28 03:37:29 -07:00
Arseny Kapoulkine
a6cbb0f65c Fix clang-14 / GNU ld interaction for target_clones 2022-10-27 17:47:14 -07:00
Vyacheslav Egorov
b4b125c727 Merge branch 'upstream' into merge 2022-10-28 01:36:59 +03:00
Vyacheslav Egorov
dbedb3c859 Merge remote-tracking branch 'upstream/master' into merge 2022-10-28 01:35:18 +03:00
Vyacheslav Egorov
99c0db3b08 Sync to upstream/release/551 2022-10-28 01:22:49 +03:00
JohnnyMorganz
ad7d006fb2
Use overloaded documentation symbol for class methods/table props (#724)
Right now `getDocumentationSymbol` on an overloaded class method or
table prop just gives the prop name, not prepended with `/overload/ +
toString(overload)`.

This causes the incorrect symbol to be used for `table.insert`,
`BrickColor.new`, `Color3.new` etc. (present in
https://raw.githubusercontent.com/MaximumADHD/Roblox-Client-Tracker/roblox/api-docs/en-us.json)

Co-authored-by: vegorov-rbx <75688451+vegorov-rbx@users.noreply.github.com>
2022-10-25 16:22:19 -07:00
Arseny Kapoulkine
e64d489e57
Update benchmark.yml
Add codegennull runs to compile time benchmarks
2022-10-24 17:24:19 -07:00
Andy Friesen
54324867df
Sync to upstream/release/550 (#723)
* Support `["prop"]` syntax on class definitions in definition files.
(#704)
* Improve type checking performance for complex overloaded functions
* Fix rare cases of incorrect stack traces for out of memory errors at
runtime
2022-10-21 10:54:01 -07:00
Andy Friesen
b9451c6f68 Merge branch 'upstream' into merge 2022-10-21 10:34:21 -07:00
Andy Friesen
72fdf6f03a Merge branch 'master' into merge 2022-10-21 10:34:17 -07:00
Andy Friesen
2eff6cfe50 Sync to upstream/release/550 2022-10-21 10:33:43 -07:00
JohnnyMorganz
12ee1407a1
Add named parameters to math lib (#722)
Name the parameters used in `math` lib

This is mainly done to highlight the particular confusion for
`math.atan2`, where `y` comes before `x`, but this might not be
immediately obvious.

And then I added the rest of the names for consistency.

Note: I didn't add names to `math.random` as it's currently typed as
`(number?, number?) -> number`. Naming it `min` and `max` is technically
incorrect for the 1 argument version.
Maybe it should be typed as an intersection instead?
2022-10-21 08:05:56 -07:00
JohnnyMorganz
c4c120513f
Fix reverse deps > 1 node away not correctly marked as dirty (#719)
`Frontend.markDirty()` does not correctly mark reverse dependencies that
are not immediate as dirty.
This is because it would keep adding the reverse deps of `name` into the
queue to mark as dirty, instead of the reverse deps of `next`
2022-10-20 09:07:00 -07:00
Alan Jeffrey
662a5532ec
Minor fixes to the semantic subtyping blog post (#720) 2022-10-19 20:32:29 -05:00
Alan Jeffrey
ae5a011465
Add a blog post about semantic subtyping (#700)
Co-authored-by: Alexander McCord <11488393+alexmccord@users.noreply.github.com>
2022-10-18 16:56:15 -05:00
JohnnyMorganz
edb4453924
Support ["prop"] syntax on class definitions (#704)
Some classes have properties which are not valid identifiers (such as
https://create.roblox.com/docs/reference/engine/classes/Studio)

This adds support for the following syntax in definition files:
```lua
declare class Foo
    ["a property"]: string
end
```

Closes #702
2022-10-18 10:15:26 -07:00
Arseny Kapoulkine
49d6bc30ad
Add bench-codegen benchmark (#713)
We will now run luau with --codegen during benchmark runs and collect
the data into separate JSON. Note that we don't yet have the historical
data for these, which will be backfilled later.
2022-10-17 12:18:34 -07:00
Arseny Kapoulkine
c6a2d75193
Improve testing coverage with codegen/O2 in mind (#709)
This change adds codegen runs to coverage config and adds O2/codegen
testing to CI.

Note that we don't run O2 combinations in coverage - it's better that we
see gaps in O2 coverage in compiler tests, as these are valuable for
validating codegen intricacies that are difficult to see from
conformance tests passing/failing.
2022-10-17 10:02:21 -07:00
vegorov-rbx
76070f8da2
Sync to upstream/release/549 (#707)
* Reoptimized math.min/max/bit32 builtins assuming at least 2 arguments are used (1-2% lift on some benchmarks)
* Type errors that mention function types no longer have redundant parenthesis around return type
* Luau REPL now supports --compile=remarks which displays the source code with optimization remarks embedded as comments
* Builtin calls are slightly faster when called with 1-2 arguments (~1% improvement in some benchmarks)
2022-10-14 12:48:41 -07:00
JohnnyMorganz
ff736fd3e4
Fix segfault in loadDefinition for unit tests (#705)
`module` can be empty if the definition file has syntax errors
2022-10-14 07:28:54 -07:00
Vyacheslav Egorov
6a98d15fe7 Responding to PR comments 2022-10-14 12:55:17 +03:00
Vyacheslav Egorov
48fd16d4a9 Fix build error 2022-10-14 03:16:47 +03:00
Vyacheslav Egorov
1dca05d09b Merge branch 'upstream' into merge 2022-10-14 02:10:48 +03:00
Vyacheslav Egorov
6aafd2b3cd Merge branch 'master' into merge 2022-10-14 02:10:39 +03:00
Vyacheslav Egorov
d82e73607c Sync to upstream/release/549 2022-10-14 01:59:53 +03:00
Arseny Kapoulkine
d6aa35583e Add new-release action to automatically build & publish release artifacts 2022-10-06 18:44:51 -07:00
Arseny Kapoulkine
d5a2a1585e
Sync to upstream/release/548 (#699)
- Fix rare type checking bugs with invalid generic types escaping the
module scope
- Fix type checking of variadic type packs in certain cases
- Implement type normalization, which resolves a large set of various
issues with unions/intersections in type checker
- Improve parse errors for trailing commas in function calls and type
lists
- Reduce profiling skew when using --profile with very high frequencies
- Improve performance of `lua_getinfo` (`debug.info`, `debug.traceback`
and profiling overhead are now 20% faster/smaller)
- Improve performance of polymorphic comparisons (1-2% lift on some
benchmarks)
- Improve performance of closure creation (1-2% lift on some benchmarks)
- Improve string comparison performance (4% lift on string sorting)
2022-10-06 17:23:29 -07:00
Arseny Kapoulkine
4a79c7ff33 Merge branch 'upstream' into merge 2022-10-06 16:56:18 -07:00
Arseny Kapoulkine
9bfe2b844b Merge branch 'master' into merge 2022-10-06 16:56:17 -07:00
Arseny Kapoulkine
91e144ac1b Sync to upstream/release/548 2022-10-06 16:55:58 -07:00
Arseny Kapoulkine
cc26ef16df
Update release.yml
Use -j2 for release builds since CMake doesn't do it for us
2022-09-29 16:19:37 -07:00
Arseny Kapoulkine
937ef2efd4
Cleanup benchmark builds a little bit (#691)
We don't need to run any cachegrind benchmarks in benchmark-dev, since
benchmark uses our new callgrind setup instead.

Also removes prototyping filters that we no longer need from all builds.
2022-09-29 15:42:23 -07:00
Arseny Kapoulkine
944e8375aa
Sync to upstream/release/547 (#690)
- Type aliases can no longer override primitive types; attempts to do
that will result in a type error
- Fix misleading type error messages for mismatches in expression list
length during assignment
- Fix incorrect type name display in certain cases
- setmetatable/getmetatable are now ~2x faster
- tools/perfstat.py can be used to display statistics about profiles
captured via --profile switch
2022-09-29 15:23:10 -07:00
Arseny Kapoulkine
ebf252f325 Merge branch 'upstream' into merge 2022-09-29 15:12:34 -07:00
Arseny Kapoulkine
123649d7b7 Merge branch 'master' into merge 2022-09-29 15:12:30 -07:00
Arseny Kapoulkine
d0989b9e15 Sync to upstream/release/547 2022-09-29 15:11:54 -07:00
Arseny Kapoulkine
1acd66c97d
Remove prototyping/ and prototyping.yml (#688)
Superseded by https://github.com/luau-lang/agda-typeck
2022-09-29 12:13:00 -07:00
Alexander McCord
5414cddb27
Add unknown and never to typecheck.md (#682)
Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
2022-09-26 14:18:04 -07:00
Arseny Kapoulkine
a7f8c1045c
Update library.md
Fix Markdown formatting
2022-09-24 09:09:02 -07:00
Arseny Kapoulkine
59fd9de485
Sync to upstream/release/546 (#681) 2022-09-23 12:17:25 -07:00
Arseny Kapoulkine
4176e1c044 Fix internals library 2022-09-23 11:43:13 -07:00
Arseny Kapoulkine
7bea908f0d Merge branch 'upstream' into merge 2022-09-23 11:34:11 -07:00
Arseny Kapoulkine
cd66ad6470 Merge branch 'master' into merge 2022-09-23 11:33:00 -07:00
Arseny Kapoulkine
48fb5a3483 Sync to upstream/release/546 2022-09-23 11:32:10 -07:00
Petri Häkkinen
fc4871989a
Add lua_cleartable (#678)
To my understanding lua_cleartable does not need GC barriers because
it's only removing elements and not modifying the stack. But I'm not a
GC expert so please correct if I'm wrong.

resolves #672

Co-authored-by: Petri Häkkinen <petrih@rmd.remedy.fi>
2022-09-22 09:54:03 -07:00
Aaron Weiss
bdf7d8c185
RFC: Expanded Subtyping for Generic Function Types (#641)
[Rendered](https://github.com/aatxe/luau/blob/generic-function-subtyping/rfcs/generic-function-subtyping.md)
2022-09-19 13:42:20 -07:00
Arseny Kapoulkine
8b36409c2c
Update build.yml (#675)
Pass codecov token explicitly, for some reason automatic configuration
stopped working
2022-09-16 11:39:30 -07:00
Arseny Kapoulkine
3d74a8f4d4
Sync to upstream/release/545 (#674)
- Improve type error messages for argument count mismatch in certain
cases
- Fix type checking variadic returns when the type is incompatible which
type checked in certain cases
- Reduce size of upvalue objects by 8 bytes on 64-bit platforms
- Reduce I$ footprint of interpreter by 1.5KB
- Reduce GC pause during atomic stage for programs with a lot of threads
- Remove support for bytecode v2
2022-09-15 15:38:17 -07:00
Arseny Kapoulkine
6e957a457a Merge branch 'upstream' into merge 2022-09-15 15:24:05 -07:00
Arseny Kapoulkine
19ac72adb1 Merge branch 'master' into merge 2022-09-15 15:14:18 -07:00
Arseny Kapoulkine
dd710f67ca Sync to upstream/release/545 2022-09-15 15:13:58 -07:00
Petri Häkkinen
6fbea7cc84
Add lua_rawsetfield (#671)
Luau currently has the following functions in the C API for dealing with
tables without invoking metamethods:

lua_rawgetfield
lua_rawget
lua_rawgeti
lua_rawset
lua_rawseti

This change adds the missing function lua_rawsetfield for consistency
and because it's more efficient to use it in place of plain lua_rawset
which requires pushing the key and value separately.

Co-authored-by: Petri Häkkinen <petrih@rmd.remedy.fi>
2022-09-15 08:26:54 -07:00
Allan N Jeremy
366df96393
Feat: Added retry functionality to benchmarks (#667)
Resolves #668 

## The problem
Benchmarks jobs run concurrently for the different operating systems.
This means that when it comes time to push the benchmark results to [the
assigned benchmark results
repo](https://github.com/luau-lang/benchmark-data), there can be two
different jobs trying to push changes at the same time. In such a case,
one of the pushes will fail and we end up missing some benchmark results
data from the workflow run.

## The solution
Whenever a push fails, we need to retry the steps leading up to the push
(checking out the benchmark results repo, storing benchmark results,
pushing the results to [a specific
repo](https://github.com/luau-lang/benchmark-data)).

### Note
There are 3 push attempts before submitting to failure.

## TL;DR
This PR retries pushing benchmark results when they fail to get pushed
(often due to pushing from multiple jobs concurrently)

Co-authored-by: Jamie Kuppens <reshurum@gmail.com>
Co-authored-by: Ignacio Falk <flakolefluk@gmail.com>
2022-09-12 08:45:55 -07:00
Arseny Kapoulkine
ce2c3b3a4e
Sync to upstream/release/544 (#669)
- Remove type definitions of
`utf8.nfcnormalize`/`nfdnormalize`/`graphemes` that aren't supported by
standalone Luau library
- Add `lua_costatus` to retrieve extended thread status (similar to
`coroutine.status`)
- Improve GC sweeping performance (2-10% improvement on allocation-heavy
benchmarks)
2022-09-08 15:14:25 -07:00
Arseny Kapoulkine
6c708975d3 Patch the test for now to work with 16K pages
This exposes a bug in CodeAllocator that needs to be fixed properly
2022-09-08 14:58:20 -07:00
Arseny Kapoulkine
71df988dec Merge branch 'upstream' into merge 2022-09-08 14:45:28 -07:00
Arseny Kapoulkine
ff18a63c1d Merge branch 'master' into merge 2022-09-08 14:45:24 -07:00
Arseny Kapoulkine
dec4b67b5a Sync to upstream/release/544 2022-09-08 14:44:50 -07:00
Alexander McCord
b2e357da30
Update title and fix a dead link. (#659)
Originally it was titled "Luau Recap: August 2022" but it got renamed to "Luau Recap: July & August 2022" and we just didn't fix the link here too. Also backports the title change to here too for consistency.
2022-09-04 13:05:25 -07:00
Arseny Kapoulkine
ae35ada579
Sync to upstream/release/543 (#657)
- Improve ComparisonPrecedence lint suggestions for three-way comparisons (X < Y < Z)
- Improve type checking stability
- Improve location information for errors when parsing invalid type annotations
- Compiler now generates bytecode version 3 in all configurations
- Improve performance of comparisons against numeric constants on AArch64
2022-09-01 16:14:03 -07:00
Arseny Kapoulkine
75b8a2a032 Merge branch 'upstream' into merge 2022-09-01 16:00:56 -07:00
Arseny Kapoulkine
c6ac06e656 Sync to upstream/release/543 2022-09-01 16:00:14 -07:00
Alexander McCord
42c24f98d9
July and August recap (#654)
Co-authored-by: vegorov-rbx <75688451+vegorov-rbx@users.noreply.github.com>
Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
2022-08-31 10:46:06 -07:00
JohnnyMorganz
e9e2cba77a
Fix overloaded metamethod in class definitions (#653)
Fixes #652
2022-08-29 08:28:04 -07:00
Arseny Kapoulkine
f2b334a4bb Merge branch 'merge' to resynchronize merge tracking history 2022-08-26 12:56:52 -07:00
Arseny Kapoulkine
e84bccc5c0
RFC: Prohibit use of interpolated strings in function calls without parentheses (#648)
We've had this restriction in the original RFC, but decided to remove it afterwards. This change puts the restriction back - we need to work through some implications of future support for string-based DSLs together with interpolated strings which may or may not change the behavior here, for example to allow something like

```
local fragment = xml `
  <img src="{self.url}" />
`
```
2022-08-25 14:54:02 -07:00
Arseny Kapoulkine
b2f9f53ae3
Sync to upstream/release/542 (#649)
- Fix DeprecatedGlobal warning text in cases when the global is deprecated without a suggested alternative
- Fix an off-by-one error in type error text for incorrect use of string.format
- Reduce stack consumption further during parsing, hopefully eliminating stack overflows during parsing/compilation for good
- Mark interpolated string support as experimental (requires --fflags=LuauInterpolatedStringBaseSupport to enable)
- Simplify garbage collection treatment of upvalues, reducing cache misses during sweeping stage and reducing the cost of upvalue assignment (SETUPVAL); supersedes #643
- Simplify garbage collection treatment of sleeping threads
- Simplify sweeping of alive threads, reducing cache misses during sweeping stage
- Simplify management of string buffers, removing redundant linked list operations
2022-08-25 14:53:50 -07:00
Arseny Kapoulkine
5eb4fb6089 Fix gcc warning 2022-08-25 14:10:12 -07:00
Arseny Kapoulkine
9aa4677829 Merge branch 'upstream' into merge 2022-08-25 13:57:52 -07:00
Arseny Kapoulkine
c8adbf2375 Merge branch 'master' into merge 2022-08-25 13:57:43 -07:00
Arseny Kapoulkine
3008da98df Sync to upstream/release/542 2022-08-25 13:55:08 -07:00
boyned//Kampfkarren
da9d8e8c60
String interpolation (#614)
Implements the string interpolation RFC (#165).

Adds the string interpolation as per the RFC.

```lua
local name = "world"
print(`Hello {name}!`) -- Hello world!
```

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Alexander McCord <11488393+alexmccord@users.noreply.github.com>
2022-08-24 12:01:00 -07:00
Alexander McCord
0ce4c45436
RFC: Explicitly support escape sequence in string interpolation literals (#642)
This was implicitly assumed to be supported, but we should really just say so explicitly. If we didn't, there'd be two ways to interpret it: string interpolations are raw strings and escape sequences don't exist for the most part (false), or string interpolations are like strings and escape sequences do exist (true).

Also talks about the ambiguity with `\u{` and that it is defined to take priority and does not terminate the interpolation chunk and starts a new expression, instead it must be a well-formed escape sequence `\u{...}`. It is very unlikely there'd be another escape sequence that also has this same ambiguity problem, so we don't need to worry about this.
2022-08-23 12:03:53 -07:00
JohnnyMorganz
a95dff3223
Pass environment scope to autocomplete typechecker (#610)
Fixes a bug where the environment scope is not passed to the autocomplete typechecker, so environment-related types are not provided.

This caused an issue where `getModuleEnvironment` returns `typeChecker.globalScope` when there is no environment. We pass this through to `typeCheckerForAutocomplete.check()` then environmentscope is no longer nullopt and it will use `typeChecker.globalScope` instead of falling back to `typeCheckerForAutocomplete.globalScope`.

This is solved by passing `forAutocomplete` to `getModuleEnvironment` so it falls back to the autocomplete global scope if none found.
2022-08-23 11:53:02 -07:00
Arseny Kapoulkine
be2769ad14
Sync to upstream/release/541 (#644)
- Fix autocomplete not suggesting globals defined after the cursor (fixes #622)
- Improve type checker stability
- Reduce parser C stack consumption which fixes some stack overflow crashes on deeply nested sources
- Improve performance of bit32.extract/replace when width is implied (~3% faster chess)
- Improve performance of bit32.extract when field/width are constants (~10% faster base64)
- Heap dump now annotates thread stacks with local variable/function names
2022-08-18 14:32:08 -07:00
Arseny Kapoulkine
fcccf1e7d1 Merge branch 'upstream' into merge 2022-08-18 14:07:38 -07:00
Arseny Kapoulkine
d614f43ebe Merge branch 'master' into merge 2022-08-18 14:06:14 -07:00
Arseny Kapoulkine
b3e6dcecfd Sync to upstream/release/541 2022-08-18 14:04:33 -07:00
Arseny Kapoulkine
0e118b54bb
Update generalized-iteration.md (#640)
Use a more precise pseudocode snippet for __iter handling which matches runtime semantics
2022-08-17 10:02:08 -07:00
XmiliaH
4ded555cc5
Prevent overflow in lua_newuserdatadtor (#639)
In case a large userdata size is passed to lua_newuserdatadtor it might overflow the size resulting in luaU_newudata actually allocating the object without a memory error. This will then result in overwriting part of the metatable pointer of the userdata.
This PR fixes this issue by checking for the overflow and in such cases pass a size value which will cause a memory error in luaU_newudata.
2022-08-16 15:32:48 -07:00
Arseny Kapoulkine
cd26f88d56
Update compatibility.md
Add NaN keys from Lua 5.2
2022-08-12 09:10:42 -07:00
Mactavsin
b7d126bf99
Fix lint.md formatting (#637)
This pull requests fixes a mistake in lint.md file that causes incorrect formatting of the text:
2022-08-12 07:14:38 -07:00
Arseny Kapoulkine
8b390a33aa
Update benchmark.yml
Switch to bench.json file so that bench-gcc is the only outlier, as everything else is built with clang.
2022-08-11 14:14:32 -07:00
Arseny Kapoulkine
f7d8ad0774
Sync to upstream/release/540 (#635)
Also adjust benchmark runs to use config=profile and run clang for all benchmarks + gcc for runtime
2022-08-11 14:01:33 -07:00
Arseny Kapoulkine
f1928ddade Adjust benchmark runs to use config=profile and new file names 2022-08-11 13:48:35 -07:00
Arseny Kapoulkine
39bcf23cd6 Merge branch 'upstream' into merge 2022-08-11 13:44:03 -07:00
Arseny Kapoulkine
f21ba5d2f2 Merge branch 'master' into merge 2022-08-11 13:43:34 -07:00
Arseny Kapoulkine
106b269885 Sync to upstream/release/540 2022-08-11 13:42:54 -07:00
Arseny Kapoulkine
2c40b7661c
Update lint.md (#634)
Add documentation for IntegerParsing and ComparisonPrecedence lints
2022-08-11 08:42:31 -07:00
JohnnyMorganz
e15b0728be
Add autocomplete context to result (#611)
Closes #599 

Not sure if these context definitions are distinct enough, but they work for my use cases.

I repurposed (a majority of) the existing unit tests for this, but if it should live in separate test cases let me know

---
* Add autocomplete context to result

* Update tests

* Remove comments

* Fix expression context issues
2022-08-10 13:04:08 -07:00
Arseny Kapoulkine
1b20fcd43c
Sync to upstream/release/539 (#625) 2022-08-04 15:35:33 -07:00
Arseny Kapoulkine
6d6fd12fee Let's try this 2022-08-04 15:07:49 -07:00
Arseny Kapoulkine
5522631be4 Merge branch 'upstream' into merge 2022-08-04 14:30:15 -07:00
Arseny Kapoulkine
33190f84ae Merge branch 'master' into merge 2022-08-04 14:27:40 -07:00
Arseny Kapoulkine
4a9cfd57a6 Sync to upstream/release/539 2022-08-04 14:27:28 -07:00
boyned//Kampfkarren
4658219df2
Add %* format specifier (#619)
RFC: https://github.com/Roblox/luau/pull/165
2022-08-04 07:22:16 -07:00
Arseny Kapoulkine
2c12badb5c
Update RFC status page 2022-08-03 15:40:57 -07:00
Arseny Kapoulkine
4692c55687
Mark never/unknown types RFC as implemented 2022-08-03 15:40:24 -07:00
Arseny Kapoulkine
b39fcc7e77
Mark __len RFC as implemented 2022-08-03 15:38:45 -07:00
Jay Kruer
b204981aca
Add Ctrl-C handling to the REPL (#537)
Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
2022-08-02 09:32:22 -07:00
Arseny Kapoulkine
d3b566c258
Sync to upstream/release/538 (#616) 2022-07-28 21:24:07 -07:00
Arseny Kapoulkine
cdcd6fcbdb Merge branch 'upstream' into merge 2022-07-28 20:46:02 -07:00
Arseny Kapoulkine
efcaf05010 Merge branch 'master' into merge 2022-07-28 20:45:57 -07:00
Arseny Kapoulkine
3202869acc Sync to upstream/release/538 2022-07-28 20:41:13 -07:00
Alexander McCord
cb16555608
RFC: Disallow name T and name(T) in future syntactic extensions for type annotations (#589) 2022-07-28 14:48:17 -07:00
Alexander McCord
185370fa1d
RFC: Update the rules on string interpolation in light of feedback (#615)
We make four adjustments in this RFC:

1. `{{` is not allowed. This is likely a valid but poor attempt at escaping coming from C#, Rust, or Python.
2. We now allow `` `this` `` with zero interpolating expressions.
3. We now allow `` f `this` `` also.
4. Explicitly say that `` `this` `` and `` `this {that}` `` are not valid type annotation syntax.
2022-07-28 14:48:05 -07:00
JohnnyMorganz
2a6d1c03ac
Store AstExprFunction in astTypes for stats (#612) 2022-07-25 10:19:21 -07:00
Arseny Kapoulkine
12c5502027
Update performance.md
Update compiler performance metrics to account for O2 and expanding internal codebase
2022-07-22 10:35:03 -07:00
Arseny Kapoulkine
9be7f85be7
Update performance.md (#608)
Mention constant folding for builtins and remove the note about possibly doing inlining in the future because we do do it now!
2022-07-22 07:53:16 -07:00
Arseny Kapoulkine
b1cfaf5305
Sync to upstream/release/537 (#607) 2022-07-21 14:16:54 -07:00
Arseny Kapoulkine
2c0be898a1 Merge branch 'upstream' into merge 2022-07-21 13:37:02 -07:00
Arseny Kapoulkine
8f7a1c701b Merge branch 'master' into merge 2022-07-21 13:36:59 -07:00
Arseny Kapoulkine
8e8ae0a01d Sync to upstream/release/537 2022-07-21 13:36:41 -07:00
Arseny Kapoulkine
a824b05c9e
Update coverage report to codecov (#606)
Coveralls has had multiple stability issues in the recent while, and codecov seems much better maintained in general.
2022-07-20 15:12:30 -07:00
Arseny Kapoulkine
ea7a6c1260
Spell out RFC considerations for library functions more explicitly (#603)
When considering new standard library functions, we essentially need to strongly justify their existence over their implementation in Luau in user library code.

This PR attempts to provide a few axes of consideration; ideally new library functions tick many of the boxes, eg "used often + is more performant + clear unambiguous interface" is an ideal consideration for a library function, whereas if it's merely accelerating a single specific use case for a single application it's unlikely to be a good justification for inclusion.
2022-07-19 08:37:56 -07:00
Matthew Emery
96316c66dc
Documentation of round tie-breaking (#602)
* Update library.md

* Tie-breaking documentation for round
2022-07-18 12:36:23 -07:00
Arseny Kapoulkine
5b2e39c922
Sync to upstream/release/536 (#592) 2022-07-14 15:52:26 -07:00
Arseny Kapoulkine
092e70115e Merge branch 'master' into merge 2022-07-14 15:40:07 -07:00
Arseny Kapoulkine
c321668dbc Merge branch 'upstream' into merge 2022-07-14 15:40:05 -07:00
Arseny Kapoulkine
4bd651292d Sync to upstream/release/536 2022-07-14 15:39:35 -07:00
Alex Orlenko
e87009f5b2
Add lua_setuserdatatag to update userdata tags (#588) 2022-07-14 12:00:37 -07:00
Andy Friesen
a934f742d8
June recap (#583) 2022-07-11 13:21:23 -07:00
Anaminus
6ad8239e32
Improve description of bit32.extract/replace. (#585)
Fix description incorrectly saying that parameter w specifies an upper range. w is actually a width. Proof:

    print(bit32.extract(2^32-1, 3, 4)) -- prints 15, not 1.

Also indicate that the position is 0-based, and that the function will error if the selected range exceeds the allowed bounds.
2022-07-08 10:06:25 -07:00
Arseny Kapoulkine
120a7fab70 Merge branch 'merge' 2022-07-07 18:22:50 -07:00
Arseny Kapoulkine
506d971421
Sync to upstream/release/535 (#584) 2022-07-07 18:22:39 -07:00
Arseny Kapoulkine
42244b14df Merge branch 'upstream' into merge 2022-07-07 18:08:06 -07:00
Arseny Kapoulkine
f9e76fc75c Merge branch 'master' into merge 2022-07-07 18:07:30 -07:00
Arseny Kapoulkine
4a95f2201e Sync to upstream/release/535 2022-07-07 18:05:31 -07:00
Allan N Jeremy
dbcd5fb28e
build: changed data output to be different for each os (#581)
- macos: data-macos-latest.json
- ubuntu: data-ubuntu-latest.json
- windows: data-windows-latest.json
2022-07-07 08:21:40 -07:00
Alan Jeffrey
a7ae439b0f
Document new table type features (#567) 2022-07-05 16:25:09 -05:00
Arseny Kapoulkine
42510bb5c8
Fix test after merging a stale PR (#577) 2022-07-05 11:36:33 -07:00
JohnnyMorganz
baa35117bd
Use syntheticName for stringified MetatableTypeVar (#563) 2022-07-05 09:19:54 -07:00
JohnnyMorganz
4d98a16ea8
Store resolved types in astResolvedTypes (#574) 2022-07-05 09:08:05 -07:00
JohnnyMorganz
6303ae30c4
Fix op used when stringifying AstExprIndexName (#572)
* Fix op used when stringifying AstExprIndexName

* Remove visualizeWithSelf
2022-07-05 08:59:09 -07:00
Arseny Kapoulkine
2460e38998
bench: Implement luau-analyze and luau --compile benchmarks (#575)
This change adds another file for benchmarking luau-analyze and sets up
benchmarks for both non-strict/strict modes for analysis and all three
optimization levels for compilation performance.

To avoid issues with race conditions on repository update we do all this
in the same job in benchmark.yml.

To be able to benchmark both modes from a single file, luau-analyze
gains --mode argument which allows to override the default typechecking
mode. Not sure if we'll want this to be a hard override on top of the
module-specified mode in the future, but this works for now.
2022-07-05 08:23:09 -07:00
Arseny Kapoulkine
48aa7a5162
bench: Implement first class support for callgrind (#570)
Since callgrind allows to control stats collection from the guest, this
allows us to reset the collection right before the benchmark starts.

This change exposes this to the benchmark runner and integrates
callgrind data parsing into bench.py, so that we can run bench.py with
--callgrind argument and, as long as the runner was built with callgrind
support, we get instruction counts from the run.

We convert instruction counts to seconds using 10G instructions/second
rate; there's no correct way to do this without simulating the full CPU
pipeline but it results in time units on a similar scale to real runs.
2022-07-04 11:13:07 -07:00
Arseny Kapoulkine
6467c855e8
Update compatibility.md (#566)
Update `__len` metamethod (pending the code change that implements this)
2022-06-30 17:07:56 -07:00
Arseny Kapoulkine
2daa6497a1
Sync to upstream/release/534 (#569) 2022-06-30 16:52:43 -07:00
Arseny Kapoulkine
065f2dac40 Merge branch 'upstream' into merge 2022-06-30 16:30:01 -07:00
Arseny Kapoulkine
3f716ea007 Merge branch 'master' into merge 2022-06-30 16:29:59 -07:00
Arseny Kapoulkine
8f040862b1 Sync to upstream/release/534 2022-06-30 16:29:02 -07:00
natteko
fc763650d3
Fix broken link in typecheck.md (#568)
Current link redirects to 
https://luau-lang.org/typecheck#Roblox-types (notice the fragment) 
which is effectively the same as https://luau-lang.org/typecheck
What the link *wants* to redirect to is 
https://luau-lang.org/typecheck#roblox-types (notice the change in fragment) 
which is the Roblox types segment of the document
2022-06-30 15:14:49 -07:00
Arseny Kapoulkine
ee82f1e997
Update sandbox.md
Since we don't have a formal proof, clarify that we don't have known bugs.
2022-06-28 23:13:13 -07:00
Arseny Kapoulkine
c29b803046
Update STATUS.md
Add __len metamethod
2022-06-28 09:08:12 -07:00
Arseny Kapoulkine
fd82e92628
RFC: Support __len metamethod for tables and rawlen function (#536) 2022-06-28 09:06:59 -07:00
Arseny Kapoulkine
13e50a9cac
Update library.md (#564)
Clarify behavior of shifts for out of range values.
2022-06-27 09:05:50 -07:00
Arseny Kapoulkine
4cd0443913
Update benchmark.yml
Cleaner names
2022-06-24 18:30:26 -07:00
Arseny Kapoulkine
9846a6c7b9
Update benchmark.yml
Remove all alert/comment functionality
2022-06-24 18:26:15 -07:00
Arseny Kapoulkine
224d35bc9e
Update benchmark.yml
Attempt to fix Windows and other builds
2022-06-24 18:16:12 -07:00
Allan N Jeremy
5e405b58b3
Added multi-os runners for benchmark & implemented luau analyze (#542) 2022-06-24 09:46:29 -07:00
Arseny Kapoulkine
e91d80ee25
Update compatibility.md (#559) 2022-06-23 18:56:19 -07:00
Arseny Kapoulkine
08ab7da4db
Sync to upstream/release/533 (#560) 2022-06-23 18:56:00 -07:00
Arseny Kapoulkine
4bf3ace2a6 Merge branch 'upstream' into merge 2022-06-23 18:45:15 -07:00
Arseny Kapoulkine
8544ca2c54 Merge branch 'master' into merge 2022-06-23 18:45:13 -07:00
Arseny Kapoulkine
6d14bdadf4 Sync to upstream/release/533 2022-06-23 18:44:07 -07:00
Arseny Kapoulkine
348ad4d417
Update STATUS.md
Add never and unknown types
2022-06-22 12:54:48 -07:00
Arseny Kapoulkine
1757234f01
Rename none-and-unknown-types.md to never-and-unknown-types.md
This makes the type names match.
2022-06-22 11:16:38 -07:00
Alan Jeffrey
778e62c8f7
RFC: never and unknown types (#434)
Co-authored-by: Alexander McCord <11488393+alexmccord@users.noreply.github.com>
2022-06-22 11:15:41 -07:00
Arseny Kapoulkine
ca32d1bf9d
Update library.md (#555)
Fix string.match and string.find type definitions
2022-06-22 09:27:05 -07:00
Qualadore
e0ac24d1ed
Correct string.find and string.match return types (#554) 2022-06-22 09:01:34 -07:00
Arseny Kapoulkine
ce9f4e23ae
Update performance.md (#553)
Document function inlining and loop unrolling.
2022-06-21 13:14:30 -07:00
Arseny Kapoulkine
f1b46f4b96
Sync to upstream/release/532 (#545) 2022-06-16 18:05:14 -07:00
Arseny Kapoulkine
fe6850556f Merge branch 'upstream' into merge 2022-06-16 17:54:55 -07:00
Arseny Kapoulkine
88b3984711 Sync to upstream/release/532 2022-06-16 17:54:42 -07:00
Arseny Kapoulkine
1b99d566a9 Merge branch 'upstream' into merge 2022-06-16 17:53:05 -07:00
Arseny Kapoulkine
404653b4ca Merge branch 'master' into merge 2022-06-16 17:53:02 -07:00
Arseny Kapoulkine
316838f253 Sync to upstream/release/531 2022-06-16 17:52:23 -07:00
Alan Jeffrey
948f678f93
Prototyping function overload resolution (#508) 2022-06-14 20:03:43 -07:00
Allan N Jeremy
da01056022
Added Luau Benchmark Workflows (#530) 2022-06-14 08:48:07 -07:00
JohnnyMorganz
c30ab0647b
Improve table stringifier when line breaks enabled (#488)
* Improve table stringifier when line breaks enabled

* Add FFlag

* Fix FFlags
2022-06-14 08:39:25 -07:00
rblanckaert
b066e4c8f8
0.531 (#532)
* Fix free Luau type being fully overwritten by 'any' and causing UAF
* Fix lua_clonefunction implementation replacing top instead of pushing
* Falsey values other than false can now narrow refinements
* Fix lua_getmetatable, lua_getfenv not waking thread up
* FIx a case where lua_objlen could push a new string without thread wakeup or GC
* Moved Luau math and bit32 definitions to definition file 
* Improve Luau parse recovery of incorrect return type token
2022-06-10 09:58:21 -07:00
Rob Blanckaert
a6322ed36a Merge branch 'upstream' into merge 2022-06-09 18:34:22 -07:00
Rodactor
7df9408851 Sync to origin/release/531 2022-06-09 18:31:12 -07:00
Rob Blanckaert
44a5cb6d52 Merge remote-tracking branch 'origin/master' into merge 2022-06-09 18:24:58 -07:00
Arseny Kapoulkine
ca5fbbfc24
Mark generalized iteration RFC as implemented 2022-06-09 10:18:29 -07:00
Arseny Kapoulkine
bcab792e0d
Update STATUS.md
Generalized iteration got implemented, safe indexing got dropped.
2022-06-09 10:18:03 -07:00
Arseny Kapoulkine
b8ef743721
RFC: Do not implement safe navigation operator (#501)
This meta-RFC proposes removing the previously accepted RFC on safe navigation operator. This is probably going to be disappointing but is the best course of action that reflects our ideals in language evolution.

See PR thread for rationale and discussion.
2022-06-09 10:03:37 -07:00
Petri Häkkinen
b44912cd20
Allow vector fastcall constructor to work with 3-4 arguments with 4-wide vectors (#511) 2022-06-09 09:41:52 -07:00
Daniel Nachun
9619f036ac
fix build with newer GCC (#522) 2022-06-06 15:52:55 -07:00
rblanckaert
55a026811a
Sync to upstream/release/530 (#517)
* Run clang-format
* Contains a preliminary implementation of deferred constraint resolution
* Reduce stack usage by some recursive functions
* Fix a bug when smartCloning a BoundTypeVar
* Remove some GC related flags from VM
2022-06-03 15:15:45 -07:00
Rob Blanckaert
a78b6d59e9 Merge branch 'upstream' into merge 2022-06-03 13:35:49 -07:00
Rob Blanckaert
188ffdfdfb Merge remote-tracking branch 'origin/master' into merge 2022-06-03 13:34:19 -07:00
Rob Blanckaert
871a3a21c5 Sync to origin/release/530 2022-06-03 13:32:20 -07:00
vegorov-rbx
edd071f99a
Luau Recap: May 2022 (#513)
* Recap is ready

* Change the intro format so it shows up in the news section snippet

* Fixed line breaks that were implied

* Update docs/_posts/2022-06-01-luau-recap-may-2022.md

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>

* Drop Autocomplete improvements and extend compiler optimization section

In the cross-post to Roblox developer forum, Autocomplete section will be restored, while compiler optimization section can be optionally removed (although it might interest some developers)

* One more optimization

* Update docs/_posts/2022-06-01-luau-recap-may-2022.md

Co-authored-by: dcope-rbx <91100513+dcope-rbx@users.noreply.github.com>

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: dcope-rbx <91100513+dcope-rbx@users.noreply.github.com>
2022-06-01 16:27:42 -07:00
Arseny Kapoulkine
10ed02470c
Update compatibility.md
Add a note on light C functions that Lua 5.2 added; suggested by #512.
2022-06-01 14:19:17 -04:00
rblanckaert
61766a692c
Sync to upstream/release/529 (#505)
* Adds a currently unused x86-64 assembler as a prerequisite for possible future JIT compilation
* Fix a bug in table iteration (closes Possible table iteration bug #504)
* Improved warning method when function is used as a type
* Fix a bug with unsandboxed iteration with pairs()
* Type of coroutine.status() is now a union of value types
* Bytecode output for tests/debugging now has labels
* Improvements to loop unrolling cost estimation
* Report errors when the key obviously doesn't exist in the table
2022-05-26 15:08:16 -07:00
Rob Blanckaert
b2f4f70274 Merge hotfix: x64 AssemblyBuilder: Don't copy data of size zero 2022-05-26 14:46:41 -07:00
Rob Blanckaert
01f36ef4e8 Merge branch 'upstream' into merge 2022-05-26 14:00:15 -07:00
Rob Blanckaert
fa2794e681 Merge remote-tracking branch 'origin/master' into merge 2022-05-26 13:58:18 -07:00
Rob Blanckaert
c4e05eb7c1 Sync to upstream/release/529 2022-05-26 13:33:48 -07:00
Austin
e13f17e225
Fix VM inconsistency caused by userdata C TM fast paths (#497)
This fixes usage of userdata C functions in xpcall handler following call stack overflow
2022-05-24 11:32:03 -07:00
T 'Filtered' C
69acf5ac07
Make coroutine.status use a string literal (#500)
Implements #495 
C++ isn't a language im very familliar with so this might be completely wrong
2022-05-24 11:29:17 -07:00
Petri Häkkinen
fb9c4311d8
Add lua_tolightuserdata, optimized lua_topointer (#496)
Co-authored-by: Petri Häkkinen <petrih@rmd.remedy.fi>
2022-05-24 08:59:12 -07:00
Arseny Kapoulkine
70ff6b4347
Update performance.md (#494)
Add a section on table length optimizations and reword the table iteration section a bit to account for generalized iteration.
2022-05-20 13:00:53 -07:00
Arseny Kapoulkine
f5923aefeb
Sync to upstream/release/527 (#491) 2022-05-19 17:02:24 -07:00
Arseny Kapoulkine
0a0012e26d Merge branch 'upstream' into merge 2022-05-19 16:48:13 -07:00
Arseny Kapoulkine
8c52852592 Merge branch 'master' into merge 2022-05-19 16:47:10 -07:00
Arseny Kapoulkine
7e9e697489 Sync to upstream/release/527 2022-05-19 16:46:52 -07:00
JohnnyMorganz
8b4c6aabc2
Fix findAstAncestry when position is at eof (#490) 2022-05-18 16:26:05 -07:00
JohnnyMorganz
f2191b9e4d
Respect useLineBreaks for union/intersect toString (#487)
* Respect useLineBreaks for union/intersect toString

* Apply suggestions from code review

Co-authored-by: Andy Friesen <andy.friesen@gmail.com>

Co-authored-by: Andy Friesen <andy.friesen@gmail.com>
2022-05-17 11:22:54 -07:00
JohnnyMorganz
ab4bb355a3
Add ToStringOptions.hideFunctionSelfArgument (#486)
Adds an option to hide the `self: type` argument as the first argument in the string representation of a named function type var if the ftv hasSelf.

Also added in a test for the original output (i.e., if the option was disabled)

I didn't apply this option in the normal `Luau::toString()` function, just the `Luau::toStringNamedFunction()` one (for my usecase, that is enough + I felt like a named function would include the method colon `:` to signify self). If this is unintuitive, I can also add it to the general `Luau::toString()` function.
2022-05-16 09:50:15 -07:00
Arseny Kapoulkine
a36b1eb29b
Sync to upstream/release/527 (#481) 2022-05-13 12:36:37 -07:00
Arseny Kapoulkine
982024bdc7 Merge branch 'upstream' into merge 2022-05-13 12:17:13 -07:00
Arseny Kapoulkine
10af54f68b Merge branch 'master' into merge 2022-05-13 12:17:11 -07:00
Arseny Kapoulkine
298b33859b Sync to upstream/release/527 2022-05-13 12:16:50 -07:00
Arseny Kapoulkine
87fe15ac51
Update STATUS.md
Mark last table subtyping RFC as implemented
2022-05-12 10:08:36 -07:00
Arseny Kapoulkine
a775e6dc8e
Mark last table subtyping RFC as implemented 2022-05-12 10:08:10 -07:00
Arseny Kapoulkine
105e74c7d9
Update STATUS.md
Both generalized iteration and LBC are implemented but not fully enabled in Roblox yet.
2022-05-11 15:14:51 -07:00
Arseny Kapoulkine
f3f231ea6b
Update compatibility.md
Update `__pairs` note with `__iter`, change `__len` to unsure as with `__iter` lack of `__len` on tables is the only issue preventing complete user created containers.
2022-05-09 18:38:10 -07:00
Arseny Kapoulkine
be0b7d07e2
Update sandbox.md
Replace debug.getinfo with debug.info
2022-05-09 18:34:31 -07:00
Arseny Kapoulkine
7935f9f8b6
Update sandbox.md
Reword the GC docs to avoid back-referencing the thread identity mechanism, since it's entirely Roblox-side and isn't fully documented here anymore.
2022-05-09 18:33:53 -07:00
Arseny Kapoulkine
72d8d44343
Add documentation for generalized iteration (#475) 2022-05-05 17:05:57 -07:00
Arseny Kapoulkine
e9cc76a3d5
Sync to upstream/release/526 (#477) 2022-05-05 17:03:43 -07:00
Arseny Kapoulkine
08ac2176c5 Merge branch 'upstream' into merge 2022-05-05 16:53:50 -07:00
Arseny Kapoulkine
a76a92f4a3 Merge branch 'master' into merge 2022-05-05 16:53:35 -07:00
Arseny Kapoulkine
bb57bf9603 Sync to upstream/release/526 2022-05-05 16:52:48 -07:00
phoebe
57016582a7
fix feed link (#476) 2022-05-05 14:37:27 -07:00
byte-chan™
9156b5ae6d
Fix non-C locale issues in REPL (#474) 2022-05-04 12:27:12 -07:00
Alexander McCord
47a8d28aa9
Fix a typo in recap. (#472) 2022-05-03 16:12:59 -07:00
Andy Friesen
9bc71c4b13
April 2022 recap (#470) 2022-05-03 15:29:01 -07:00
Andy Friesen
448f03218f
Add attribution for Result.ts (#468) 2022-04-29 09:33:30 -07:00
Arseny Kapoulkine
bd6d44f5e3
Sync to upstream/release/525 (#467) 2022-04-28 18:24:24 -07:00
Arseny Kapoulkine
51ae97c211 We also need to lower the limit 2022-04-28 18:15:04 -07:00
Arseny Kapoulkine
0d6481b9df Fix tests in debug 2022-04-28 18:10:31 -07:00
Arseny Kapoulkine
5444d06708 Merge branch 'upstream' into merge 2022-04-28 18:05:07 -07:00
Arseny Kapoulkine
9ce46fd381 Merge branch 'master' into merge 2022-04-28 18:05:04 -07:00
Arseny Kapoulkine
4d9ac7db1e Sync to upstream/release/525 2022-04-28 18:04:52 -07:00
Alan Jeffrey
74c84815a0
Prototyping type normalizaton (#466)
* Added type normalization
2022-04-28 15:00:55 -05:00
Arseny Kapoulkine
e0a6461173
Sync to upstream/release/524 (#462) 2022-04-21 14:44:27 -07:00
Arseny Kapoulkine
bb2370d5c6 Merge branch 'upstream' into merge 2022-04-21 14:04:52 -07:00
Arseny Kapoulkine
267c88fec1 Merge branch 'master' into merge 2022-04-21 14:04:48 -07:00
Arseny Kapoulkine
f2677f6975 Sync to upstream/release/524 2022-04-21 14:04:22 -07:00
Alan Jeffrey
5bb9f379b0
Unified strict and nonstrict mode in the prototype (#458) 2022-04-15 19:19:42 -05:00
Arseny Kapoulkine
8e7845076b
Sync to upstream/release/523 (#459) 2022-04-14 16:57:43 -07:00
Arseny Kapoulkine
25f90eae7d Fix test in debug 2022-04-14 16:48:36 -07:00
Arseny Kapoulkine
6d8a6ec825 Merge branch 'upstream' into merge 2022-04-14 14:58:21 -07:00
Arseny Kapoulkine
5a59a9a50f Merge branch 'master' into merge 2022-04-14 14:57:52 -07:00
Arseny Kapoulkine
02ed5373ec Sync to upstream/release/523 2022-04-14 14:57:15 -07:00
Alan Jeffrey
d37d0c857b
Prototype: Renamed any/none to unknown/never (#447)
* Renamed any/none to unknown/never
* Pin hackage version
* Update Agda version
2022-04-09 00:07:08 -05:00
Lily Brown
510aed7d3f
Fix JsonEncoder for AstExprTable (#454)
JsonEncoder wasn't producing valid JSON for `AstExprTable`s. This PR fixes it. The new output looks like
```json
{
    "type": "AstStatBlock",
    "location": "0,0 - 6,4",
    "body": [
        {
            "type": "AstStatLocal",
            "location": "1,8 - 5,9",
            "vars": [
                {
                    "name": "x",
                    "location": "1,14 - 1,15"
                }
            ],
            "values": [
                {
                    "type": "AstExprTable",
                    "location": "3,12 - 5,9",
                    "items": [
                        {
                            "kind": "record",
                            "key": {
                                "type": "AstExprConstantString",
                                "location": "4,12 - 4,15",
                                "value": "foo"
                            },
                            "value": {
                                "type": "AstExprConstantNumber",
                                "location": "4,18 - 4,21",
                                "value": 123
                            }
                        }
                    ]
                }
            ]
        }
    ]
}
```
2022-04-08 11:26:47 -07:00
Arseny Kapoulkine
de1381e3f1
Sync to upstream/release/522 (#450) 2022-04-07 14:29:01 -07:00
Arseny Kapoulkine
8f96e19b80 Merge branch 'upstream' into merge 2022-04-07 13:54:36 -07:00
Arseny Kapoulkine
d42a5dbe48 Sync to upstream/release/522 2022-04-07 13:53:47 -07:00
Alexander McCord
ffff25a9e5
Improve the UX of reading tagged unions a smidge. (#449)
The page is a little narrow, and having to scroll on this horizontally isn't too nice. This fixes the UX for this specific part.
2022-04-07 09:16:44 -07:00
Alan Jeffrey
dc32a3253e
Add short example of width subtyping (#444) 2022-03-31 18:44:41 -05:00
Alan Jeffrey
916c83fdc4
Prototype: Added a discussion of set-theoretic models of subtyping (#431)
* Added a discussion of set-theoretic models of subtyping to the prototype
2022-03-31 18:29:42 -05:00
Arseny Kapoulkine
20308bed20 Merge branch 'merge' 2022-03-31 14:04:40 -07:00
Arseny Kapoulkine
4c1f208d7a
Sync to upstream/release/521 (#443) 2022-03-31 14:01:51 -07:00
Arseny Kapoulkine
a9489893b0 Merge branch 'upstream' into merge 2022-03-31 13:39:48 -07:00
Arseny Kapoulkine
b9b0382a5a Merge branch 'master' into merge 2022-03-31 13:39:46 -07:00
Arseny Kapoulkine
83c1c48e09 Sync to upstream/release/521 2022-03-31 13:37:49 -07:00
Alexander McCord
ba60730e0f
Add documentation on singleton types and tagged unions to typecheck.md. (#440)
Update the typecheck.md page to talk about singleton types and their uses, tagged unions.

As a driveby, improve the documentation on type refinements. And delete the unknown symbols part, this is really dated.

* Update docs/_pages/typecheck.md to fix a typo

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
2022-03-31 11:54:06 -07:00
Alan Jeffrey
06bbfd90b5
Fix code sample in March 2022 Recap (#442) 2022-03-31 09:31:06 -05:00
Alan Jeffrey
f3ea2f96f7
Recap March 2022 (#439)
* March Recap

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
2022-03-30 18:38:55 -05:00
Arseny Kapoulkine
af64680a5e
Mark singleton types and unsealed table literals RFCs as implemented (#438) 2022-03-29 16:58:59 -07:00
Andy Friesen
75bccce3db
RFC: Lower Bounds Calculation (#388)
Co-authored-by: vegorov-rbx <75688451+vegorov-rbx@users.noreply.github.com>
2022-03-29 12:37:14 -07:00
Arseny Kapoulkine
2c339d52c0
Sync to upstream/release/520 (#427) 2022-03-24 15:04:14 -07:00
Arseny Kapoulkine
d13b50de16 Merge branch 'upstream' into merge 2022-03-24 14:49:30 -07:00
Arseny Kapoulkine
44405223ce Merge branch 'master' into merge 2022-03-24 14:49:29 -07:00
Arseny Kapoulkine
373da161e9 Sync to upstream/release/520 2022-03-24 14:49:08 -07:00
Arseny Kapoulkine
5e7e462104
Update STATUS.md 2022-03-24 13:10:56 -07:00
Arseny Kapoulkine
ab64a097cd
Mark sealed table subtyping RFC as implemented 2022-03-24 13:10:30 -07:00
Arseny Kapoulkine
2335b26ffc
Update library.md
Add documentation for table.clone
2022-03-24 09:31:18 -07:00
Arseny Kapoulkine
b8e025311b
Mark table.clone as implemented 2022-03-24 09:29:20 -07:00
Arseny Kapoulkine
5a21c41363
Update STATUS.md 2022-03-24 09:26:42 -07:00
Alexander McCord
7ab76e582c
RFC: Do not implement non-nil postfix ! operator. (#420)
This is a meta-RFC. I'd like to propose to remove the postfix `!` operator from the language.

I'd like to argue that this operator will eventually become useless and can only be used incorrectly at some point in the future, and that there are likely ways for them to be sound without having to reach for the `!` hammer.

With that, I present these counterarguments:

Shortcoming <span>#</span>1: Refinements does not apply to the block after `return`/`break`/`continue`.

```lua
if not x or not y then return end
-- both x and y are truthy
```

This will be solved by implementing control flow analysis where it'd apply the inverse of the condition leading up to the control transfer statement to the rest of the scope.

Shortcoming <span>#</span>2: Type checker is not aware of the actual state of various locations.

```lua
type Foo = { x: { y: number }? }?
local foo: Foo = { x = { y = 5 } }
print(foo.x.y) -- prints 5 at runtime, type checker warns on this
```

This will be solved by implementing type states where it would inspect the initialization sites as well as assignments to know their actual states. That is, rather than trusting the type annotation `Foo` as the state which gets us far enough, we'd start seeing these type annotations as a subtype constraint for the location `foo`.

---

If there are other use cases not covered in this message, we should talk about that and see if there exists an alternative direction that can solve these use cases soundly.
2022-03-24 08:38:16 -07:00
Alexander McCord
2f15079642
Fold in rationale for making else branch mandatory in if-then-else expressions. (#426)
This has been discussed but just didn't get incorporated into the RFC.
2022-03-23 16:53:52 -07:00
Alan Jeffrey
7721955ba5
Prototyping: add semantic subtyping (#424)
Adds subtyping to strict mode.
2022-03-23 15:02:57 -05:00
Arseny Kapoulkine
362428f8b4
Sync to upstream/release/519 (#422) 2022-03-17 17:46:04 -07:00
Arseny Kapoulkine
57faf7aaf2 Lower the stack limit to make tests pass in debug 2022-03-17 17:33:23 -07:00
Arseny Kapoulkine
eee60dcaf3 Merge branch 'upstream' into merge 2022-03-17 17:07:51 -07:00
Arseny Kapoulkine
305f255a1f Merge branch 'master' into merge 2022-03-17 17:07:07 -07:00
Arseny Kapoulkine
adecd84067 Sync to upstream/release/519 2022-03-17 17:06:25 -07:00
Luca Salmin
4fdead5e3c
yield from interrupt #287 (#413)
Co-authored-by: luca salmin <luca.salmin@studioevil.com>
2022-03-14 11:43:40 -07:00
Arseny Kapoulkine
fe71aff7af
Update library.md 2022-03-14 10:30:49 -07:00
Arseny Kapoulkine
a44b7906b6
Sync to upstream/release/518 (#419) 2022-03-11 08:55:02 -08:00
Arseny Kapoulkine
bf74a347c3 Merge branch 'upstream' into merge 2022-03-11 08:36:10 -08:00
Arseny Kapoulkine
6667df02df Merge branch 'master' into merge 2022-03-11 08:36:08 -08:00
Arseny Kapoulkine
feea507be3 Sync to upstream/release/518 2022-03-11 08:31:18 -08:00
Arseny Kapoulkine
dbdf91f3ca
Sync to upstream/release/517 (#408) 2022-03-04 08:36:33 -08:00
Arseny Kapoulkine
75e66f13c5 Merge branch 'upstream' into merge 2022-03-04 08:19:48 -08:00
Arseny Kapoulkine
600b8a483a Merge branch 'master' into merge 2022-03-04 08:19:44 -08:00
Arseny Kapoulkine
9bfecab5ba Sync to upstream/release/517 2022-03-04 08:19:20 -08:00
Lily Brown
6c923b8802
Prototyping: strings (#390) 2022-03-02 15:26:58 -08:00
Alan Jeffrey
c5477d522d
Prototyping strict mode (#399)
* First cut of strict mode

Co-authored-by: Lily Brown <lily@lily.fyi>
2022-03-02 16:02:51 -06:00
vegorov-rbx
d277cc2c3b
Luau Recap: February 2022 (#403)
* Luau Recap: February 2022

* Rebuild pages

* Fixed Markdown linter warnings, converted tabs to spaces and added a note on 'select' optimization

* Apply suggestions from code review

Co-authored-by: Alexander McCord <11488393+alexmccord@users.noreply.github.com>

* Added information about new lint

* Update docs/_posts/2022-02-28-luau-recap-february-2022.md

Co-authored-by: Alexander McCord <11488393+alexmccord@users.noreply.github.com>

Co-authored-by: Alexander McCord <11488393+alexmccord@users.noreply.github.com>
2022-03-02 06:27:35 -08:00
Arseny Kapoulkine
898c0501d1
Update STATUS.md
Add table.clone
2022-02-28 14:16:35 -08:00
Arseny Kapoulkine
5e8242aeda
RFC: table.clone (#362) 2022-02-28 14:15:33 -08:00
Arseny Kapoulkine
fb11ca00f4
Update README.md
linenoise -> isocline
2022-02-28 10:50:23 -08:00
Arseny Kapoulkine
db3a8a2f0f
Update prototyping.yml (#398)
This limits the scope of prototyping action to PRs to prototyping branch to minimize the GHA cost / latency, as cabal install sometimes takes forever
2022-02-24 17:08:54 -08:00
Arseny Kapoulkine
c7eca27909
Sync to upstream/release/516 (#397) 2022-02-24 15:53:37 -08:00
Arseny Kapoulkine
8984f4984c Merge branch 'upstream' into merge 2022-02-24 15:16:35 -08:00
Arseny Kapoulkine
2a549cfbad Merge branch 'master' into merge 2022-02-24 15:16:33 -08:00
Arseny Kapoulkine
a8eabedd57 Sync to upstream/release/516 2022-02-24 15:15:41 -08:00
Lily Brown
0bd21762ae
Prototype bools and relational operators (#387)
Prototypes booleans and relational operators.

As part of this I removed `FFI/Data/Bool.agda`, because it was getting in the way - we already use `Agda.Builtin.Bool` instead for other cases.
2022-02-24 11:17:46 -08:00
Petri Häkkinen
0bc7c51afc
Lua API: add return types to table getters (#389)
Co-authored-by: Petri Häkkinen <petrih@rmd.remedy.fi>
2022-02-23 10:03:58 -08:00
Lily Brown
cd18adc20e
Prototyping: binary operations (#377)
Adds support for binary operations on numbers.
2022-02-22 15:52:56 -08:00
James Napora
1334db600f
Update grammar.md (#379) 2022-02-22 11:24:15 -08:00
Arseny Kapoulkine
3c42b3a013
Revert "Mark singleton types RFC as implemented (#370)" (#378)
This reverts commit 731e197757.
2022-02-19 10:57:29 -08:00
Alan Jeffrey
fc33b0c702
Fix evaluation rule for function application (#375) 2022-02-18 16:47:23 -06:00
Alan Jeffrey
0b783d8932
Add Properties.Equality to prototyping (#376) 2022-02-18 16:47:04 -06:00
Lily Brown
7f867ac166
Prototyping: numbers (#368)
Adds number support to the prototype. Binary operators are next.
2022-02-18 11:09:00 -08:00
Arseny Kapoulkine
a9bdce6cc0
Rename tests to tests.py (#374) 2022-02-18 10:04:38 -08:00
Arseny Kapoulkine
5b78465059
Sync to upstream/release/514 (#372) 2022-02-17 17:18:01 -08:00
Lily Brown
1ac64af484
Prototyping: Revise CI (#371)
Introduces a test runner with test cases. Also significantly overhauls the GHA configuration.
2022-02-17 17:15:33 -08:00
Arseny Kapoulkine
6aaaafcc8d
Add documentation for upcoming CommentDirective lint (#361) 2022-02-17 16:58:43 -08:00
Arseny Kapoulkine
1f679b08b8 Merge branch 'upstream' into merge 2022-02-17 16:42:11 -08:00
Arseny Kapoulkine
eea0374fad Merge branch 'master' into merge 2022-02-17 16:42:08 -08:00
Arseny Kapoulkine
4930409516 Sync to upstream/release/515 2022-02-17 16:41:20 -08:00
Alexander McCord
731e197757
Mark singleton types RFC as implemented (#370) 2022-02-17 16:16:31 -08:00
Arseny Kapoulkine
e49a0fd4cd
Mark default type parameters RFC as implemented (#369) 2022-02-17 16:14:35 -08:00
Arseny Kapoulkine
e541e19f44
Create RFC status tracking document (#363)
This tracks status of all unimplemented RFCs in one central place. Hopefully we won't forget to update this when new RFCs are added!
2022-02-15 18:37:02 -08:00
Lily Brown
c8d6dc2758
Revise GHA workflows for prototyping (#367)
Changed the GHA workflows to:
- Not run `build` and `release` workflows for PRs that only affect `prototyping/`
- Run `prototyping` workflow when PRs affect `Analysis/**`, `Ast/**`, or the `luau-ast` source files
2022-02-15 14:24:51 -08:00
Lily Brown
f0c9d84461
Prototyping: Parse type annotations (#366)
Parses type annotations from the JSON output of `luau-ast`.
2022-02-15 14:10:43 -08:00
Lily Brown
4b7e06e14f
Emit more information for AstLocals in JSON encoder (#364)
We don't emit type annotations right now for `AstLocal`s in the JSON encoder. This makes it really hard to surface annotations in the Agda implementation. This PR changes it to emit location and type annotations, if present.
2022-02-14 14:14:11 -08:00
Arseny Kapoulkine
7d679b317f
RFC: Generalized iteration (#335)
Co-authored-by: dcope-rbx <91100513+dcope-rbx@users.noreply.github.com>
Co-authored-by: vegorov-rbx <75688451+vegorov-rbx@users.noreply.github.com>
2022-02-14 10:04:07 -08:00
Alan Jeffrey
e0a9bc191a
Prototype: added syntax for optional type annotations (#358) 2022-02-11 19:03:26 -06:00
Alan Jeffrey
db90c7da48
Add a typeToString function to the prototype (#354)
* Added Luau.Type.ToString
2022-02-11 14:38:35 -06:00
Arseny Kapoulkine
63d5423bbb
Sync to upstream/release/514 (#357) 2022-02-11 11:02:09 -08:00
Arseny Kapoulkine
a08f52a6d2 Merge branch 'upstream' into merge 2022-02-11 10:45:41 -08:00
Arseny Kapoulkine
46ba69555a Merge branch 'master' into merge 2022-02-11 10:43:59 -08:00
Arseny Kapoulkine
e9bf182585 Sync to upstream/release/514 2022-02-11 10:43:14 -08:00
Arseny Kapoulkine
aecd60371b
Update performance.md (#355)
Document weak table shrinking and paged sweeper
2022-02-11 09:13:27 -08:00
Alan Jeffrey
5187e64f88
Implement a prototype interpreter (#353)
* First cut interpreter
2022-02-09 17:14:29 -06:00
Arseny Kapoulkine
abe3f87b48
docs: Add documentation for upcoming MisleadingAndOr lint (#349)
This is going to be part of Luau 0.514
2022-02-09 09:27:01 -08:00
Arseny Kapoulkine
ec481695a3
Update library.md (#352)
Clarify the relationship between `typeof` and `newproxy`. As a sandboxing measure, `typeof` only uses `__type` on host-defined userdata.

Fixes #351.
2022-02-09 09:19:50 -08:00
Alan Jeffrey
041838a942
Prototyping a small subset of Luau in Agda (#350)
* First cut reading JSON into an Agda representation of Luau syntax
2022-02-08 18:26:58 -06:00
Michael Savage
324bc4b01d
Add a build option to link with the static CRT (#343)
LUAU_STATIC_CRT CMake option can be set externally (requires CMake 3.15+)
2022-02-07 13:51:57 -08:00
Lily Brown
7ffb5fc4fd
Add Luau.Ast.CLI target (#345)
Adds a `luau-ast` CLI that dumps Luau source to JSON. @asajeffrey and I are planning to use this functionality to construct an Agda model of the Luau type system/operational semantics, to allow formally proving properties of Luau's type systems.
2022-02-07 12:08:43 -08:00
Arseny Kapoulkine
e51ff38d19
Sync to upstream/release/513 (#342) 2022-02-04 12:46:08 -08:00
Arseny Kapoulkine
6d40eb4b74 Merge branch 'upstream' into merge 2022-02-04 12:34:32 -08:00
Arseny Kapoulkine
15bc249b2c Merge branch 'master' into merge 2022-02-04 12:34:28 -08:00
Arseny Kapoulkine
bbae466006 Sync to upstream/release/513
This takes the extra bug fix for generic name confusion
2022-02-04 12:31:19 -08:00
Arseny Kapoulkine
d58e70b8c1
Sync to upstream/release/513 (#340) 2022-02-04 08:45:57 -08:00
Arseny Kapoulkine
4748777ce8 Fix isocline warnings 2022-02-03 16:43:44 -08:00
Arseny Kapoulkine
4e60eec1fc Apply fix to the crash 2022-02-03 16:31:50 -08:00
Arseny Kapoulkine
7b3c24f9c8 Merge branch 'upstream' into merge 2022-02-03 15:17:04 -08:00
Arseny Kapoulkine
4b4a6a72c4 Merge branch 'master' into merge 2022-02-03 15:16:22 -08:00
Arseny Kapoulkine
f6b4cc9442 Sync to upstream/release/513 2022-02-03 15:09:37 -08:00
Andy Friesen
c572f6944f
January 2022 recap (#331) 2022-02-01 14:49:55 -08:00
Arseny Kapoulkine
2f989fc049
Sync to upstream/release/512 (#330)
- Improve refinement support for unions, in particular it's now possible to implement tagged unions as a union of tables where individual branches use a string literal type for one of the fields.
- Fix `string.split` type information
- Optimize `select(_, ...)` to run in constant time (~2.7x faster on VariadicSelect benchmark)
- Improve debug line information for multi-line assignments
- Improve compilation of table literals when table keys are constant expressions/variables
- Use forward GC barrier for `setmetatable` which slightly accelerates GC progress
2022-01-27 15:46:05 -08:00
Arseny Kapoulkine
78039f4535 Thanks gcc, we know you can't compile code. 2022-01-27 13:52:56 -08:00
Arseny Kapoulkine
86b62ac14f Merge branch 'upstream' into merge 2022-01-27 13:32:25 -08:00
Arseny Kapoulkine
b2c8c3ac11 Merge branch 'master' into merge 2022-01-27 13:31:02 -08:00
Arseny Kapoulkine
6e1e277cb8 Sync to upstream/release/512 2022-01-27 13:29:34 -08:00
Vlad Marica
4b96f7efc1
luau-analyze: Add support for reading source code from stdin (#325)
Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
2022-01-25 08:25:01 -08:00
Arseny Kapoulkine
8fe95c9963
Sync to upstream/release/511 (#324)
- TableOperations lint now includes a warning for table.create(N, {}) (which is likely a mistake since the table is shared by all entries)
- Type checker now type checks #v when v is a union
- Parser now rejects sources that consists of a single unfinished long comment
- Work around significant MSVC 2022 performance regression, bringing it more or less in line with MSVC 2019
- Compiler now predicts array size for newly allocated tables when the table is filled in a short loop
- Small improvements in compilation throughput (~2% faster)
- Implement paged sweeper for GC which improves sweep throughput 2-3x and reduces memory consumption by 8 bytes per object (once it is stabilized we will see additional 8 bytes per object of savings)
- Improve Repl Tab completion
- Repl now supports -i (interactive mode to run code in context of a script's environment) and -On (to control optimization flags)
2022-01-21 09:00:19 -08:00
Arseny Kapoulkine
9c15f6a6d7 And one more 2022-01-21 08:52:48 -08:00
Arseny Kapoulkine
0062000d46 One more 2022-01-21 08:43:41 -08:00
Arseny Kapoulkine
699660a4eb Fix MSVC warnings 2022-01-21 08:37:50 -08:00
Arseny Kapoulkine
9cfe44e5a2 Merge branch 'upstream' into merge 2022-01-21 08:24:01 -08:00
Arseny Kapoulkine
5b4af9704f Merge branch 'master' into merge 2022-01-21 08:23:25 -08:00
Arseny Kapoulkine
d70a0788c5 Sync to upstream/release/511 2022-01-21 08:23:02 -08:00
Shiro
478a3da634
Update copyright years. (#323) 2022-01-20 09:42:49 -08:00
JohnnyMorganz
4e5ff99582
Improve Grammar documentation (#315) 2022-01-20 08:27:19 -08:00
dcope-rbx
49ce5096a4
Fixed a couple spelling mistakes in markdown files. (#316) 2022-01-17 09:44:31 -08:00
Halalaluyafail3
497d625f73
Fix some mistakes in the documentation (#314) 2022-01-14 13:42:49 -08:00
Arseny Kapoulkine
32c39e2162
Sync to upstream/release/510 (#313) 2022-01-14 08:20:09 -08:00
Arseny Kapoulkine
e6dd6bd158 Merge branch 'upstream' into merge 2022-01-14 08:07:10 -08:00
Arseny Kapoulkine
e0af631169 Merge branch 'master' into merge 2022-01-14 08:07:08 -08:00
Arseny Kapoulkine
80d5c0000e Sync to upstream/release/510 2022-01-14 08:06:31 -08:00
Arseny Kapoulkine
b2af550b08
Update grammar.md
Add forgotten quotes around 'type'; reported by @Dionysusnu
2022-01-13 15:23:18 -08:00
Arseny Kapoulkine
d6ba106be6
Update compatibility.md
Add a note about function identity
2022-01-12 11:56:46 -08:00
T 'Filtered' C
abf9fc2754
Update compatibility.md to split coroutine.close from lua_resetthread (#309)
Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
2022-01-12 11:50:25 -08:00
rafa_br34
b7f78f4997
MSVC warning C4244 fixes (#308)
Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
2022-01-12 10:05:31 -08:00
Arseny Kapoulkine
b7a7b92d12
Update syntax.md
Remove confusing block because it reads as if we *do* support this syntax if you aren't reading carefully.
2022-01-11 13:24:56 -08:00
Arseny Kapoulkine
a23b467485
Add turbofish discussion to generic function RFC (#300) 2022-01-07 11:07:36 -08:00
Arseny Kapoulkine
287b46e6a7
Mark bidirectional ascription RFC as implemented (#305) 2022-01-07 11:07:22 -08:00
Arseny Kapoulkine
b5e338325b
Mark coroutine.close RFC as implemented (#304) 2022-01-07 08:52:33 -08:00
Arseny Kapoulkine
d50b079325
Sync to upstream/release/509 (#303)
- Rework transaction log used for type checking which should result in more robust type checking internals with fewer bugs
- Reduce the amount of memory consumed by type checker on large module graphs
- Type checker now errors on attempts to change the type of imported module fields
- The return type of newproxy is now any (fixes #296)
- Implement new number printing algorithm (Schubfach) which makes tostring() produce precise (round-trippable) and short decimal output up to 10x faster
- Fix lua_Debug::linedefined to point to the line with the function definition instead of the first statement (fixes #265)
- Fix minor bugs in Tab completion in Repl
- Repl now saves/restores command history in ~/.luau_history
2022-01-06 17:46:53 -08:00
Arseny Kapoulkine
d189bd9b1a Enable V2Read flag early 2022-01-06 17:37:50 -08:00
Arseny Kapoulkine
8464a78d43 Merge branch 'upstream' into merge 2022-01-06 16:08:33 -08:00
Arseny Kapoulkine
c7e1a94996 Merge branch 'master' into merge 2022-01-06 16:08:24 -08:00
Arseny Kapoulkine
d323237b6c
Sync to upstream/release/508 (#301)
This version isn't for release because we've skipped some internal
numbers due to year-end schedule changes, but it's better to merge
separately.
2022-01-06 15:26:14 -08:00
Arseny Kapoulkine
62a40d0b86 Merge commit 'a9aa4faf24e6cea1ac0e33d0054a7328a35f9d4a' into merge 2022-01-06 14:17:44 -08:00
Arseny Kapoulkine
bdf81c0ed1 Merge branch 'master' into merge 2022-01-06 14:11:50 -08:00
Arseny Kapoulkine
44ccd82822 Sync to upstream/release/509 2022-01-06 14:10:07 -08:00
Arseny Kapoulkine
a9aa4faf24 Sync to upstream/release/508
This version isn't for release because we've skipped some internal
numbers due to year-end schedule changes, but it's better to merge
separately.
2022-01-06 14:08:56 -08:00
Alan Jeffrey
82587bef29
RFC: Fix an unsoundness issue around stripping optional properties (#276)
* Fix an unsoundness issue around stripping optional properties

Co-authored-by: vegorov-rbx <75688451+vegorov-rbx@users.noreply.github.com>
2022-01-06 12:48:09 -06:00
Arseny Kapoulkine
73b7bcb2da
docs: Move Luau.Web.js fetch to the end of the page
This was moved in the CodeMirror change, but that may break Module setup in the embedded <script> block, so move it back.
2022-01-04 15:04:25 -08:00
Arseny Kapoulkine
fa35884e5b
Update library.md
A few small tweaks and fixes.
2021-12-27 13:08:56 -08:00
Arseny Kapoulkine
6203bf6ac5
Update grammar.md
Remove TOC & mark page as wide.
2021-12-27 12:51:23 -08:00
Arseny Kapoulkine
65177c425c
Update grammar.md
This changes the grammar to follow the EBNF rules more rigorously, most significantly quoting all keywords.
2021-12-27 12:48:58 -08:00
Arseny Kapoulkine
d079201a6e Fix repeated calls to print() clearing output 2021-12-20 15:44:51 -08:00
Arseny Kapoulkine
fedd9a5f78 docs: Fix label syntax 2021-12-20 15:40:38 -08:00
boyned//Kampfkarren
5e7648947b
RFC: Safe navigation operator (#142)
Co-authored-by: Alexander McCord <11488393+alexmccord@users.noreply.github.com>
2021-12-20 15:38:15 -08:00
Arseny Kapoulkine
9e7e779c02
Quality of life improvements to web demo (#297)
- Upgrade CodeMirror to 5.65
- Enable matching paren highlighting via an addon
- Remove extra buttons and replace clear output with a checkbox
- Highlight error line on parsing/execution error
- Change demo layout to wide to increase available width
2021-12-20 15:36:41 -08:00
Pelanyo Kamara
019eeeb853
Add CodeMirror Editor for Web Demo (#228)
Also remove timestamps from output
2021-12-15 09:23:26 -08:00
Arseny Kapoulkine
f2e6a8f4a5
Sync to upstream/release/507-pre (#286)
This doesn't contain all changes for 507 yet but we might want to do the
Luau 0.507 release a bit earlier to end the year sooner.

Changes:

- Type ascription (::) now permits casts between related types in both directions, allowing to refine or loosen the type (RFC #56)
- Fix type definition for tonumber to return number? since the input string isn't guaranteed to contain a valid number
- Fix type refinements for field access via []
- Many stability fixes for type checker
- Provide extra information in error messages for type mismatches in more cases
- Improve performance of type checking for large unions when union members are string literals
- Add coverage reporting support to Repl (--coverage command line argument) and lua_getcoverage C API
- Work around code signing issues during Makefile builds on macOS
- Improve performance of truthiness checks in some cases, particularly on Apple M1, resulting in 10-25% perf gains on qsort benchmark depending on the CPU/compiler
- Fix support for little-endian systems; IBM s390x here we go!
2021-12-10 14:05:05 -08:00
Arseny Kapoulkine
bcf117ba1b Merge branch 'upstream' into merge 2021-12-10 13:19:56 -08:00
Arseny Kapoulkine
891948ef30 Merge branch 'master' into merge 2021-12-10 13:19:49 -08:00
Arseny Kapoulkine
a8673f0f99 Sync to upstream/release/507-pre
This doesn't contain all changes for 507 yet but we might want to do the
Luau 0.507 release a bit earlier to end the year sooner.
2021-12-10 13:17:10 -08:00
Arseny Kapoulkine
88be067c0b
Switch coverage build to checkout@v2 (#285)
Attempt to fix coverage builds by using checkout@v2 instead of v1 which might fix the detacthed HEAD issue.

On the off chance it doesn't, add extra logging around git specifically.
2021-12-09 17:50:29 -08:00
Rerumu
12ef94df5e
Add shebang support (#149)
Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
2021-12-09 12:37:48 -08:00
JohnnyMorganz
2e6a2090c3
Add Grammar documentation (#266)
Co-authored-by: Alexander McCord <11488393+alexmccord@users.noreply.github.com>
Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
2021-12-08 09:17:28 -08:00
vegorov-rbx
71adace16e
RFC: Amend 'Default type alias type parameters' for type pack parameters (#238)
* Do not allow regular type assignment to a type pack as a default parameter

* With type pack support in type aliases, this second form with an empty list is now supported

* Update rfcs/syntax-default-type-alias-type-parameters.md

Co-authored-by: Alan Jeffrey <403333+asajeffrey@users.noreply.github.com>

* Update syntax-default-type-alias-type-parameters.md

Even more examples

Co-authored-by: Alan Jeffrey <403333+asajeffrey@users.noreply.github.com>
2021-12-08 04:21:47 -08:00
Braeden
2807dcb758
Update JsonEncoder.cpp (#279)
fix JsonEncoder so that AstArray elems are given commas seperating them
2021-12-07 08:14:35 -08:00
Slappy826
3eff1b214e
Fix spelling error of 'ROBLOX' (#277)
* Remove roblox prefix on comment
2021-12-07 08:14:24 -08:00
Arseny Kapoulkine
9488f2379d
Mark bit32.count* RFC as implemented 2021-12-03 11:10:41 -08:00
Arseny Kapoulkine
f63ddae898
Enable LuauActivateBeforeExec to activate the fix early (#275)
Fixes #259.
2021-12-03 10:40:31 -08:00
Arseny Kapoulkine
7257f34d89
Update CONTRIBUTING.md
The language around creating RFCs seems a bit redundant now.
2021-12-02 23:08:45 -08:00
Arseny Kapoulkine
32fb6d10a7
Sync to upstream/release/506 (#270)
- Fix some cases where type checking would overflow the native stack
- Improve autocomplete behavior when assigning a partially written function call (not currently exposed through command line tools)
- Improve autocomplete type inference feedback for some expressions where previously the type would not be known
- Improve quantification performance during type checking for large types
- Improve type checking for table literals when the expected type of the table is known because of a type annotation
- Fix type checking errors in cases where required module has errors in the resulting type
- Fix debug line information for multi-line chained call sequences (Add function name information for "attempt to call a nil value" #255)
- lua_newuserdata now takes 2 arguments to match Lua/LuaJIT APIs better; lua_newuserdatatagged should be used if the third argument was non-0.
- lua_ref can no longer be used with LUA_REGISTRYINDEX to prevent mistakes when migrating Lua FFI (Inconsistency with lua_ref #247)
- Fix assertions and possible crashes when executing script code indirectly via metatable dispatch from lua_equal/lua_lessthan/lua_getfield/etc. (Hitting a crash in an assert after lua_equal is called. #259)
- Fix flamegraph scripts to run under Python 2
2021-12-02 22:41:04 -08:00
Arseny Kapoulkine
f5ec6df7ba
Update build.yml
Disable continue-on-error for coverage because hiding the error actually makes it difficult to debug now :)
2021-12-02 15:55:06 -08:00
Arseny Kapoulkine
e440729e2b Fix signed/unsigned mismatch warning + lower limit to match upstream 2021-12-02 15:46:33 -08:00
Arseny Kapoulkine
a179c5248e Merge branch 'upstream' into merge 2021-12-02 15:25:23 -08:00
Arseny Kapoulkine
47f7cbe5a5 Merge branch 'master' into merge 2021-12-02 15:24:33 -08:00
Arseny Kapoulkine
eed18acec8 Sync to upstream/release/506 2021-12-02 15:20:08 -08:00
Lily Brown
864221e667
November 2021 Luau recap (#264)
Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
2021-12-02 13:22:02 -08:00
Arseny Kapoulkine
2aff9bb859
Add documentation for bit32.count* and coroutine.close (#268) 2021-12-02 11:36:40 -08:00
Petri Häkkinen
d2bf2870e8
Add lua_isvector, luaL_checkvector and luaL_optvector (#261)
Co-authored-by: Petri Häkkinen <petrih@rmd.remedy.fi>
Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
2021-12-01 11:03:08 -08:00
Petri Häkkinen
bf6cf4a69e
Fix luau_load 'env' to work with absolute stack index & add lua_absindex (#263)
Co-authored-by: Petri Häkkinen <petrih@rmd.remedy.fi>
2021-12-01 10:44:38 -08:00
kunitoki
35e497b533
Allow reconfiguring VM defaults (#260)
Co-authored-by: Lucio Asnaghi <lucio.asnaghi@king.com>
2021-11-30 17:03:18 -08:00
Arseny Kapoulkine
955f9fa754
Update index.md
Studio => luau-analyze
2021-11-30 15:26:28 -08:00
vegorov-rbx
6801c65090
Documentation for type packs (#257)
* Documentation for type packs

* Update docs/_pages/typecheck.md

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>

* Add a note about the difference between ...T and T...

Fix a typo at the start as well.

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
2021-11-30 14:00:23 -08:00
Arseny Kapoulkine
f185e9f5db
Update performance.md (#252)
Add documentation for closure allocation elision.
2021-11-30 08:21:11 -08:00
Petri Häkkinen
677994b243
Fix: luaL_sandbox leaves value on the stack (#253)
Co-authored-by: Petri Häkkinen <petrih@rmd.remedy.fi>
2021-11-30 08:14:28 -08:00
Petri Häkkinen
9aa9ff12dd
Add LUA_GCCOUNTB option for lua_gc (#254)
Co-authored-by: Petri Häkkinen <petrih@rmd.remedy.fi>
2021-11-30 08:14:12 -08:00
Lana Octavia
f86d4c6995
Removed LUALIB_API from source file method bodies (#235) 2021-11-29 08:14:06 -08:00
Baileyeatspizza
222f03bbda
Update lmathlib.cpp (#241) 2021-11-29 08:13:55 -08:00
Arseny Kapoulkine
dd02420f70
Update build.yml
Enable debug mode in coveralls action to diagnose https://github.com/lemurheavy/coveralls-public/issues/1595
2021-11-23 11:44:18 -08:00
vegorov-rbx
6b2b179aa6
Mark 'Type alias type packs' RFC as implemented (#237) 2021-11-23 10:03:20 -08:00
Arseny Kapoulkine
5740686124
Ignore errors during upload coverage (#236) 2021-11-23 08:26:28 -08:00
Alexander McCord
6958716ccd
RFC: String interpolation (#165) 2021-11-22 14:59:38 -08:00
Tiffany Bennett
a26024fb4b
Use latest release url (#227) 2021-11-22 12:54:27 -08:00
Arseny Kapoulkine
5961261a1c
Add web workflow to build Repl with Emscripten (#222)
This also separates Emscripten build into a new target / source to make
it more decoupled.
2021-11-22 09:59:15 -08:00
petrihakkinen
2740f69f32
Expand vectors to 4 components using compile time switch (#214)
Co-authored-by: Petri Häkkinen <petrih@rmd.remedy.fi>
Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
2021-11-22 07:42:33 -08:00
Pelanyo Kamara
a5bb3ee2af
Add luaL_checkboolean and luaL_optboolean (#221)
Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
2021-11-22 07:42:11 -08:00
Arseny Kapoulkine
cecd50fb06
Update navigation.yml
Remove leftover comments
2021-11-21 20:12:21 -08:00
Arseny Kapoulkine
ffed184562
Update navigation.yml
Add demo to top-level nav
2021-11-21 20:12:08 -08:00
Arseny Kapoulkine
2fa5b9c329
Update repl.html
Try using a release artifact
2021-11-21 20:07:44 -08:00
Josh Soref
ec8a5643cc
Improve readability (#206)
Co-authored-by: Josh Soref <jsoref@users.noreply.github.com>
Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
2021-11-19 10:45:53 -08:00
Arseny Kapoulkine
100710c9f6
Update README.md
Switch to luau_compile and specify env for luau_load
2021-11-19 08:15:56 -08:00
Arseny Kapoulkine
7835224606 Merge branch 'upstream' into merge 2021-11-18 14:25:34 -08:00
Arseny Kapoulkine
c5ac146580 Merge branch 'master' into merge 2021-11-18 14:25:28 -08:00
Arseny Kapoulkine
60e6e86adb Sync to upstream/release/505 2021-11-18 14:21:07 -08:00
Arseny Kapoulkine
3c3541aba8 Add a comment 2021-11-11 20:36:53 -08:00
Arseny Kapoulkine
863d3ff6ff Attempt to work around non-sensical error 2021-11-11 19:42:50 -08:00
Arseny Kapoulkine
8fe0dc0b6d Fix build 2021-11-11 18:23:34 -08:00
Arseny Kapoulkine
8db1b03acc Merge branch 'upstream' into merge 2021-11-11 18:23:03 -08:00
Arseny Kapoulkine
ce0bbdda59 Merge branch 'master' into merge 2021-11-11 18:21:14 -08:00
Arseny Kapoulkine
82d74e6f73 Sync to upstream/release/504 2021-11-11 18:12:39 -08:00
Arseny Kapoulkine
22dd6b8e48 Merge branch 'upstream' into merge 2021-11-04 19:49:07 -07:00
Arseny Kapoulkine
3c4758f7bb Merge branch 'master' into merge 2021-11-04 19:47:46 -07:00
Arseny Kapoulkine
34cf695fbc Sync to upstream/release/503
- A series of major optimizations to type checking performance on complex
programs/types (up to two orders of magnitude speedup for programs
involving huge tagged unions)
- Fix a few issues encountered by UBSAN (and maybe fix s390x builds)
- Fix gcc-11 test builds
- Fix a rare corner case where luau_load wouldn't wake inactive threads
which could result in a use-after-free due to GC
- Fix CLI crash when error object that's not a string escapes to top level
2021-11-04 19:47:13 -07:00
Arseny Kapoulkine
5f7504c040 Merge branch 'upstream' into merge 2021-11-04 19:23:29 -07:00
Arseny Kapoulkine
08c66ef2e1 Sync to upstream/release/502
Changes:
- Support for time tracing for analysis/compiler (not currently exposed
  through CLI)
- Support for type pack arguments in type aliases (#83)
- Basic support for require(path) in luau-analyze
- Add a lint warning for table.move with 0 index as part of
  TableOperation lint
- Remove last STL dependency from Luau.VM
- Minor VS2022 performance tuning

Co-authored-by: Rodactor <rodactor@roblox.com>
2021-11-04 19:12:52 -07:00
898 changed files with 247553 additions and 44419 deletions

View file

@ -16,10 +16,14 @@ SortIncludes: false
IndentWidth: 4
TabWidth: 4
ObjCBlockIndentWidth: 4
AlignAfterOpenBracket: DontAlign
UseTab: Never
PointerAlignment: Left
SpaceAfterTemplateKeyword: false
AlignEscapedNewlines: DontAlign
AlwaysBreakTemplateDeclarations: Yes
MaxEmptyLinesToKeep: 10
AllowAllParametersOfDeclarationOnNextLine: false
AlignAfterOpenBracket: BlockIndent
BinPackArguments: false
BinPackParameters: false
PenaltyReturnTypeOnItsOwnLine: 10000

View file

@ -1,5 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Questions
url: https://github.com/Roblox/luau/discussions
url: https://github.com/luau-lang/luau/discussions
about: Please use GitHub Discussions if you have questions or need help.

7
.github/codecov.yml vendored Normal file
View file

@ -0,0 +1,7 @@
comment: false
coverage:
status:
patch: false
project:
default:
informational: true

145
.github/workflows/benchmark.yml vendored Normal file
View file

@ -0,0 +1,145 @@
name: benchmark
on:
push:
branches:
- master
paths-ignore:
- "docs/**"
- "papers/**"
- "rfcs/**"
- "*.md"
jobs:
callgrind:
strategy:
matrix:
os: [ubuntu-22.04]
benchResultsRepo:
- { name: "luau-lang/benchmark-data", branch: "main" }
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Luau repository
uses: actions/checkout@v3
- name: Install valgrind
run: |
sudo apt-get update
sudo apt-get install valgrind
- name: Build Luau (gcc)
run: |
CXX=g++ make config=profile luau
cp luau luau-gcc
- name: Build Luau (codegen)
run: |
make config=profile clean
CXX=clang++ make config=profile native=1 luau
cp luau luau-codegen
- name: Build Luau (clang)
run: |
make config=profile clean
CXX=clang++ make config=profile luau luau-analyze luau-compile
- name: Run benchmark (bench-gcc)
run: |
python bench/bench.py --callgrind --vm "./luau-gcc -O2" | tee -a bench-gcc-output.txt
- name: Run benchmark (bench)
run: |
python bench/bench.py --callgrind --vm "./luau -O2" | tee -a bench-output.txt
- name: Run benchmark (bench-codegen)
run: |
python bench/bench.py --callgrind --vm "./luau-codegen --codegen -O2" | tee -a bench-codegen-output.txt
- name: Run benchmark (analyze)
run: |
filter() {
awk '/.*I\s+refs:\s+[0-9,]+/ {gsub(",", "", $4); X=$4} END {print "SUCCESS: '$1' : " X/1e7 "ms +/- 0% on luau-analyze"}'
}
valgrind --tool=callgrind ./luau-analyze --mode=nonstrict bench/other/LuauPolyfillMap.lua 2>&1 | filter map-nonstrict | tee -a analyze-output.txt
valgrind --tool=callgrind ./luau-analyze --mode=strict bench/other/LuauPolyfillMap.lua 2>&1 | filter map-strict | tee -a analyze-output.txt
valgrind --tool=callgrind ./luau-analyze --mode=strict --fflags=LuauSolverV2 bench/other/LuauPolyfillMap.lua 2>&1 | filter map-dcr | tee -a analyze-output.txt
valgrind --tool=callgrind ./luau-analyze --mode=nonstrict bench/other/regex.lua 2>&1 | filter regex-nonstrict | tee -a analyze-output.txt
valgrind --tool=callgrind ./luau-analyze --mode=strict bench/other/regex.lua 2>&1 | filter regex-strict | tee -a analyze-output.txt
valgrind --tool=callgrind ./luau-analyze --mode=strict --fflags=LuauSolverV2 bench/other/regex.lua 2>&1 | filter regex-dcr | tee -a analyze-output.txt
- name: Run benchmark (compile)
run: |
filter() {
awk '/.*I\s+refs:\s+[0-9,]+/ {gsub(",", "", $4); X=$4} END {print "SUCCESS: '$1' : " X/1e7 "ms +/- 0% on luau-compile"}'
}
valgrind --tool=callgrind ./luau-compile --null -O0 bench/other/LuauPolyfillMap.lua 2>&1 | filter map-O0 | tee -a compile-output.txt
valgrind --tool=callgrind ./luau-compile --null -O1 bench/other/LuauPolyfillMap.lua 2>&1 | filter map-O1 | tee -a compile-output.txt
valgrind --tool=callgrind ./luau-compile --null -O2 bench/other/LuauPolyfillMap.lua 2>&1 | filter map-O2 | tee -a compile-output.txt
valgrind --tool=callgrind ./luau-compile --codegennull -O2 bench/other/LuauPolyfillMap.lua 2>&1 | filter map-O2-codegen | tee -a compile-output.txt
valgrind --tool=callgrind ./luau-compile --codegennull -O2 -t1 bench/other/LuauPolyfillMap.lua 2>&1 | filter map-O2-t1-codegen | tee -a compile-output.txt
valgrind --tool=callgrind ./luau-compile --null -O0 bench/other/regex.lua 2>&1 | filter regex-O0 | tee -a compile-output.txt
valgrind --tool=callgrind ./luau-compile --null -O1 bench/other/regex.lua 2>&1 | filter regex-O1 | tee -a compile-output.txt
valgrind --tool=callgrind ./luau-compile --null -O2 bench/other/regex.lua 2>&1 | filter regex-O2 | tee -a compile-output.txt
valgrind --tool=callgrind ./luau-compile --codegennull -O2 bench/other/regex.lua 2>&1 | filter regex-O2-codegen | tee -a compile-output.txt
valgrind --tool=callgrind ./luau-compile --codegennull -O2 -t1 bench/other/regex.lua 2>&1 | filter regex-O2-t1-codegen | tee -a compile-output.txt
- name: Checkout benchmark results
uses: actions/checkout@v3
with:
repository: ${{ matrix.benchResultsRepo.name }}
ref: ${{ matrix.benchResultsRepo.branch }}
token: ${{ secrets.BENCH_GITHUB_TOKEN }}
path: "./gh-pages"
- name: Store results (bench)
uses: Roblox/rhysd-github-action-benchmark@v-luau
with:
name: callgrind clang
tool: "benchmarkluau"
output-file-path: ./bench-output.txt
external-data-json-path: ./gh-pages/bench.json
- name: Store results (bench-codegen)
uses: Roblox/rhysd-github-action-benchmark@v-luau
with:
name: callgrind codegen
tool: "benchmarkluau"
output-file-path: ./bench-codegen-output.txt
external-data-json-path: ./gh-pages/bench-codegen.json
- name: Store results (bench-gcc)
uses: Roblox/rhysd-github-action-benchmark@v-luau
with:
name: callgrind gcc
tool: "benchmarkluau"
output-file-path: ./bench-gcc-output.txt
external-data-json-path: ./gh-pages/bench-gcc.json
- name: Store results (analyze)
uses: Roblox/rhysd-github-action-benchmark@v-luau
with:
name: luau-analyze
tool: "benchmarkluau"
output-file-path: ./analyze-output.txt
external-data-json-path: ./gh-pages/analyze.json
- name: Store results (compile)
uses: Roblox/rhysd-github-action-benchmark@v-luau
with:
name: luau-compile
tool: "benchmarkluau"
output-file-path: ./compile-output.txt
external-data-json-path: ./gh-pages/compile.json
- name: Push benchmark results
if: github.event_name == 'push'
run: |
echo "Pushing benchmark results..."
cd gh-pages
git config user.name github-actions
git config user.email github@users.noreply.github.com
git add *.json
git commit -m "Add benchmarks results for ${{ github.sha }}"
git push
cd ..

View file

@ -20,22 +20,35 @@ jobs:
unix:
strategy:
matrix:
os: [ubuntu, macos]
name: ${{matrix.os}}
runs-on: ${{matrix.os}}-latest
os: [{name: ubuntu, version: ubuntu-latest}, {name: macos, version: macos-latest}, {name: macos-arm, version: macos-14}]
name: ${{matrix.os.name}}
runs-on: ${{matrix.os.version}}
steps:
- uses: actions/checkout@v1
- name: make test
- name: work around ASLR+ASAN compatibility
run: sudo sysctl -w vm.mmap_rnd_bits=28
if: matrix.os.name == 'ubuntu'
- name: make tests
run: |
make -j2 config=sanitize werror=1 test
- name: make test w/flags
make -j2 config=sanitize werror=1 native=1 luau-tests
- name: run tests
run: |
make -j2 config=sanitize werror=1 flags=true test
./luau-tests
./luau-tests --fflags=true
- name: run extra conformance tests
run: |
./luau-tests -ts=Conformance -O2
./luau-tests -ts=Conformance -O2 --fflags=true
./luau-tests -ts=Conformance --codegen
./luau-tests -ts=Conformance --codegen --fflags=true
./luau-tests -ts=Conformance --codegen -O2
./luau-tests -ts=Conformance --codegen -O2 --fflags=true
- name: make cli
run: |
make -j2 config=sanitize werror=1 luau luau-analyze # match config with tests to improve build time
./luau tests/conformance/assert.lua
./luau-analyze tests/conformance/assert.lua
make -j2 config=sanitize werror=1 luau luau-analyze luau-compile # match config with tests to improve build time
./luau tests/conformance/assert.luau
./luau-analyze tests/conformance/assert.luau
./luau-compile tests/conformance/assert.luau
windows:
runs-on: windows-latest
@ -45,41 +58,64 @@ jobs:
steps:
- uses: actions/checkout@v1
- name: cmake configure
run: cmake . -A ${{matrix.arch}} -DLUAU_WERROR=ON
- name: cmake test
run: cmake . -A ${{matrix.arch}} -DLUAU_WERROR=ON -DLUAU_NATIVE=ON
- name: cmake build
run: cmake --build . --target Luau.UnitTest Luau.Conformance --config Debug
- name: run tests
shell: bash # necessary for fail-fast
run: |
cmake --build . --target Luau.UnitTest Luau.Conformance --config Debug
Debug/Luau.UnitTest.exe
Debug/Luau.Conformance.exe
- name: cmake test w/flags
shell: bash # necessary for fail-fast
run: |
Debug/Luau.UnitTest.exe --fflags=true
Debug/Luau.Conformance.exe --fflags=true
- name: run extra conformance tests
shell: bash # necessary for fail-fast
run: |
Debug/Luau.Conformance.exe -O2
Debug/Luau.Conformance.exe -O2 --fflags=true
Debug/Luau.Conformance.exe --codegen
Debug/Luau.Conformance.exe --codegen --fflags=true
Debug/Luau.Conformance.exe --codegen -O2
Debug/Luau.Conformance.exe --codegen -O2 --fflags=true
- name: cmake cli
shell: bash # necessary for fail-fast
run: |
cmake --build . --target Luau.Repl.CLI Luau.Analyze.CLI --config Debug # match config with tests to improve build time
Debug/luau tests/conformance/assert.lua
Debug/luau-analyze tests/conformance/assert.lua
cmake --build . --target Luau.Repl.CLI Luau.Analyze.CLI Luau.Compile.CLI --config Debug # match config with tests to improve build time
Debug/luau tests/conformance/assert.luau
Debug/luau-analyze tests/conformance/assert.luau
Debug/luau-compile tests/conformance/assert.luau
coverage:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- name: install
run: |
sudo apt install llvm
- name: make coverage
run: |
CXX=clang++-10 make -j2 config=coverage coverage
CXX=clang++ make -j2 config=coverage native=1 coverage
- name: upload coverage
uses: coverallsapp/github-action@master
uses: codecov/codecov-action@v3
with:
path-to-lcov: ./coverage.info
github-token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/upload-artifact@v2
files: ./coverage.info
token: ${{ secrets.CODECOV_TOKEN }}
web:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
with:
name: coverage
path: coverage
repository: emscripten-core/emsdk
path: emsdk
- name: emsdk install
run: |
cd emsdk
./emsdk install latest
./emsdk activate latest
- name: make
run: |
source emsdk/emsdk_env.sh
emcmake cmake . -DLUAU_BUILD_WEB=ON -DCMAKE_BUILD_TYPE=Release
make -j2 Luau.Web

83
.github/workflows/new-release.yml vendored Normal file
View file

@ -0,0 +1,83 @@
name: new-release
on:
workflow_dispatch:
inputs:
version:
required: true
description: Release version including 0.
permissions:
contents: write
jobs:
create-release:
runs-on: ubuntu-latest
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
steps:
- name: create release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.event.inputs.version }}
release_name: ${{ github.event.inputs.version }}
draft: true
build:
needs: ["create-release"]
strategy:
matrix: # not using ubuntu-latest to improve compatibility
os: [{name: ubuntu, version: ubuntu-22.04}, {name: macos, version: macos-latest}, {name: windows, version: windows-latest}]
name: ${{matrix.os.name}}
runs-on: ${{matrix.os.version}}
steps:
- uses: actions/checkout@v1
- name: configure
run: cmake . -DCMAKE_BUILD_TYPE=Release
- name: build
run: cmake --build . --target Luau.Repl.CLI Luau.Analyze.CLI Luau.Compile.CLI Luau.Ast.CLI --config Release -j 2
- name: pack
if: matrix.os.name != 'windows'
run: zip luau-${{matrix.os.name}}.zip luau*
- name: pack
if: matrix.os.name == 'windows'
run: 7z a luau-${{matrix.os.name}}.zip .\Release\luau*.exe
- uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: luau-${{matrix.os.name}}.zip
asset_name: luau-${{matrix.os.name}}.zip
asset_content_type: application/octet-stream
web:
needs: ["create-release"]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
with:
repository: emscripten-core/emsdk
path: emsdk
- name: emsdk install
run: |
cd emsdk
./emsdk install latest
./emsdk activate latest
- name: make
run: |
source emsdk/emsdk_env.sh
emcmake cmake . -DLUAU_BUILD_WEB=ON -DCMAKE_BUILD_TYPE=Release
make -j2 Luau.Web
- uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: Luau.Web.js
asset_name: Luau.Web.js
asset_content_type: application/octet-stream

View file

@ -13,23 +13,48 @@ on:
jobs:
build:
strategy:
matrix:
os: [ubuntu, macos, windows]
name: ${{matrix.os}}
runs-on: ${{matrix.os}}-latest
matrix: # not using ubuntu-latest to improve compatibility
os: [{name: ubuntu, version: ubuntu-22.04}, {name: macos, version: macos-latest}, {name: windows, version: windows-latest}]
name: ${{matrix.os.name}}
runs-on: ${{matrix.os.version}}
steps:
- uses: actions/checkout@v1
- name: configure
run: cmake . -DCMAKE_BUILD_TYPE=Release
- name: build
run: cmake --build . --target Luau.Repl.CLI Luau.Analyze.CLI --config Release
- uses: actions/upload-artifact@v2
if: matrix.os != 'windows'
run: cmake --build . --target Luau.Repl.CLI Luau.Analyze.CLI Luau.Compile.CLI --config Release -j 2
- uses: actions/upload-artifact@v4
if: matrix.os.name != 'windows'
with:
name: luau-${{matrix.os}}
name: luau-${{matrix.os.name}}
path: luau*
- uses: actions/upload-artifact@v2
if: matrix.os == 'windows'
overwrite: true
- uses: actions/upload-artifact@v4
if: matrix.os.name == 'windows'
with:
name: luau-${{matrix.os}}
name: luau-${{matrix.os.name}}
path: Release\luau*.exe
overwrite: true
web:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
with:
repository: emscripten-core/emsdk
path: emsdk
- name: emsdk install
run: |
cd emsdk
./emsdk install latest
./emsdk activate latest
- name: make
run: |
source emsdk/emsdk_env.sh
emcmake cmake . -DLUAU_BUILD_WEB=ON -DCMAKE_BUILD_TYPE=Release
make -j2 Luau.Web
- uses: actions/upload-artifact@v4
with:
name: Luau.Web.js
path: Luau.Web.js
overwrite: true

28
.gitignore vendored
View file

@ -1,8 +1,20 @@
^build/
^coverage/
^fuzz/luau.pb.*
^crash-*
^default.prof*
^fuzz-*
^luau$
/.vs
/build/
/build[.-]*/
/out
/cmake/
/cmake[.-]*/
/coverage/
/.vs/
/.vscode/
/fuzz/luau.pb.*
/crash-*
/default.prof*
/fuzz-*
/luau
/luau-tests
/luau-analyze
/luau-bytecode
/luau-compile
__pycache__
.cache
.clangd

View file

@ -0,0 +1,54 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/NotNull.h"
#include "Luau/Substitution.h"
#include "Luau/TypeFwd.h"
#include <memory>
namespace Luau
{
struct TypeArena;
struct Scope;
struct InternalErrorReporter;
using ScopePtr = std::shared_ptr<Scope>;
// A substitution which replaces free types by any
struct Anyification : Substitution
{
Anyification(
TypeArena* arena,
NotNull<Scope> scope,
NotNull<BuiltinTypes> builtinTypes,
InternalErrorReporter* iceHandler,
TypeId anyType,
TypePackId anyTypePack
);
Anyification(
TypeArena* arena,
const ScopePtr& scope,
NotNull<BuiltinTypes> builtinTypes,
InternalErrorReporter* iceHandler,
TypeId anyType,
TypePackId anyTypePack
);
NotNull<Scope> scope;
NotNull<BuiltinTypes> builtinTypes;
InternalErrorReporter* iceHandler;
TypeId anyType;
TypePackId anyTypePack;
bool normalizationTooComplex = false;
bool isDirty(TypeId ty) override;
bool isDirty(TypePackId tp) override;
TypeId clean(TypeId ty) override;
TypePackId clean(TypePackId tp) override;
bool ignoreChildren(TypeId ty) override;
bool ignoreChildren(TypePackId ty) override;
};
} // namespace Luau

View file

@ -0,0 +1,32 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Substitution.h"
#include "Luau/TxnLog.h"
#include "Luau/TypeFwd.h"
namespace Luau
{
// A substitution which replaces the type parameters of a type function by arguments
struct ApplyTypeFunction : Substitution
{
ApplyTypeFunction(TypeArena* arena)
: Substitution(TxnLog::empty(), arena)
, encounteredForwardedType(false)
{
}
// Never set under deferred constraint resolution.
bool encounteredForwardedType;
std::unordered_map<TypeId, TypeId> typeArguments;
std::unordered_map<TypePackId, TypePackId> typePackArguments;
bool ignoreChildren(TypeId ty) override;
bool ignoreChildren(TypePackId tp) override;
bool isDirty(TypeId ty) override;
bool isDirty(TypePackId tp) override;
TypeId clean(TypeId ty) override;
TypePackId clean(TypePackId tp) override;
};
} // namespace Luau

View file

@ -2,12 +2,15 @@
#pragma once
#include <string>
#include <vector>
namespace Luau
{
class AstNode;
struct Comment;
std::string toJson(AstNode* node);
std::string toJson(AstNode* node, const std::vector<Comment>& commentLocations);
} // namespace Luau

View file

@ -3,6 +3,7 @@
#include "Luau/Ast.h"
#include "Luau/Documentation.h"
#include "Luau/TypeFwd.h"
#include <memory>
@ -13,9 +14,6 @@ struct Binding;
struct SourceModule;
struct Module;
struct TypeVar;
using TypeId = const TypeVar*;
using ScopePtr = std::shared_ptr<struct Scope>;
struct ExprOrLocal
@ -42,14 +40,49 @@ struct ExprOrLocal
{
return expr ? expr->location : (local ? local->location : std::optional<Location>{});
}
std::optional<AstName> getName()
{
if (expr)
{
if (AstName name = getIdentifier(expr); name.value)
{
return name;
}
}
else if (local)
{
return local->name;
}
return std::nullopt;
}
private:
AstExpr* expr = nullptr;
AstLocal* local = nullptr;
};
std::vector<AstNode*> findAstAncestryOfPosition(const SourceModule& source, Position pos);
struct FindFullAncestry final : public AstVisitor
{
std::vector<AstNode*> nodes;
Position pos;
Position documentEnd;
bool includeTypes = false;
explicit FindFullAncestry(Position pos, Position documentEnd, bool includeTypes = false);
bool visit(AstType* type) override;
bool visit(AstStatFunction* node) override;
bool visit(AstNode* node) override;
};
std::vector<AstNode*> findAncestryAtPositionForAutocomplete(const SourceModule& source, Position pos);
std::vector<AstNode*> findAncestryAtPositionForAutocomplete(AstStatBlock* root, Position pos);
std::vector<AstNode*> findAstAncestryOfPosition(const SourceModule& source, Position pos, bool includeTypes = false);
std::vector<AstNode*> findAstAncestryOfPosition(AstStatBlock* root, Position pos, bool includeTypes = false);
AstNode* findNodeAtPosition(const SourceModule& source, Position pos);
AstNode* findNodeAtPosition(AstStatBlock* root, Position pos);
AstExpr* findExprAtPosition(const SourceModule& source, Position pos);
ScopePtr findScopeAtPosition(const Module& module, Position pos);
std::optional<Binding> findBindingAtPosition(const Module& module, const SourceModule& source, Position pos);

View file

@ -1,10 +1,10 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/AutocompleteTypes.h"
#include "Luau/Location.h"
#include "Luau/TypeVar.h"
#include "Luau/Type.h"
#include <unordered_map>
#include <string>
#include <memory>
#include <optional>
@ -16,76 +16,8 @@ struct Frontend;
struct SourceModule;
struct Module;
struct TypeChecker;
using ModulePtr = std::shared_ptr<Module>;
enum class AutocompleteEntryKind
{
Property,
Binding,
Keyword,
String,
Type,
Module,
};
enum class ParenthesesRecommendation
{
None,
CursorAfter,
CursorInside,
};
enum class TypeCorrectKind
{
None,
Correct,
CorrectFunctionResult,
};
struct AutocompleteEntry
{
AutocompleteEntryKind kind = AutocompleteEntryKind::Property;
// Nullopt if kind is Keyword
std::optional<TypeId> type = std::nullopt;
bool deprecated = false;
// Only meaningful if kind is Property.
bool wrongIndexType = false;
// Set if this suggestion matches the type expected in the context
TypeCorrectKind typeCorrect = TypeCorrectKind::None;
std::optional<const ClassTypeVar*> containingClass = std::nullopt;
std::optional<const Property*> prop = std::nullopt;
std::optional<std::string> documentationSymbol = std::nullopt;
Tags tags;
ParenthesesRecommendation parens = ParenthesesRecommendation::None;
};
using AutocompleteEntryMap = std::unordered_map<std::string, AutocompleteEntry>;
struct AutocompleteResult
{
AutocompleteEntryMap entryMap;
std::vector<AstNode*> ancestry;
AutocompleteResult() = default;
AutocompleteResult(AutocompleteEntryMap entryMap, std::vector<AstNode*> ancestry)
: entryMap(std::move(entryMap))
, ancestry(std::move(ancestry))
{
}
};
using ModuleName = std::string;
using StringCompletionCallback = std::function<std::optional<AutocompleteEntryMap>(std::string tag, std::optional<const ClassTypeVar*> ctx)>;
struct OwningAutocompleteResult
{
AutocompleteResult result;
ModulePtr module;
std::unique_ptr<SourceModule> sourceModule;
};
struct FileResolver;
AutocompleteResult autocomplete(Frontend& frontend, const ModuleName& moduleName, Position position, StringCompletionCallback callback);
OwningAutocompleteResult autocompleteSource(Frontend& frontend, std::string_view source, Position position, StringCompletionCallback callback);
} // namespace Luau

View file

@ -0,0 +1,92 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Ast.h"
#include "Luau/Type.h"
#include <unordered_map>
namespace Luau
{
enum class AutocompleteContext
{
Unknown,
Expression,
Statement,
Property,
Type,
Keyword,
String,
};
enum class AutocompleteEntryKind
{
Property,
Binding,
Keyword,
String,
Type,
Module,
GeneratedFunction,
RequirePath,
};
enum class ParenthesesRecommendation
{
None,
CursorAfter,
CursorInside,
};
enum class TypeCorrectKind
{
None,
Correct,
CorrectFunctionResult,
};
struct AutocompleteEntry
{
AutocompleteEntryKind kind = AutocompleteEntryKind::Property;
// Nullopt if kind is Keyword
std::optional<TypeId> type = std::nullopt;
bool deprecated = false;
// Only meaningful if kind is Property.
bool wrongIndexType = false;
// Set if this suggestion matches the type expected in the context
TypeCorrectKind typeCorrect = TypeCorrectKind::None;
std::optional<const ExternType*> containingExternType = std::nullopt;
std::optional<const Property*> prop = std::nullopt;
std::optional<std::string> documentationSymbol = std::nullopt;
Tags tags;
ParenthesesRecommendation parens = ParenthesesRecommendation::None;
std::optional<std::string> insertText;
// Only meaningful if kind is Property.
bool indexedWithSelf = false;
};
using AutocompleteEntryMap = std::unordered_map<std::string, AutocompleteEntry>;
struct AutocompleteResult
{
AutocompleteEntryMap entryMap;
std::vector<AstNode*> ancestry;
AutocompleteContext context = AutocompleteContext::Unknown;
AutocompleteResult() = default;
AutocompleteResult(AutocompleteEntryMap entryMap, std::vector<AstNode*> ancestry, AutocompleteContext context)
: entryMap(std::move(entryMap))
, ancestry(std::move(ancestry))
, context(context)
{
}
};
using StringCompletionCallback =
std::function<std::optional<AutocompleteEntryMap>(std::string tag, std::optional<const ExternType*> ctx, std::optional<std::string> contents)>;
constexpr char kGeneratedAnonymousFunctionEntryName[] = "function (anonymous autofilled)";
} // namespace Luau

View file

@ -2,50 +2,94 @@
#pragma once
#include "Luau/Scope.h"
#include "Luau/TypeInfer.h"
#include "Luau/Type.h"
#include <optional>
namespace Luau
{
void registerBuiltinTypes(TypeChecker& typeChecker);
static constexpr char kRequireTagName[] = "require";
struct Frontend;
struct GlobalTypes;
struct TypeChecker;
struct TypeArena;
struct Subtyping;
void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeCheckForAutocomplete = false);
TypeId makeUnion(TypeArena& arena, std::vector<TypeId>&& types);
TypeId makeIntersection(TypeArena& arena, std::vector<TypeId>&& types);
/** Build an optional 't'
*/
TypeId makeOption(TypeChecker& typeChecker, TypeArena& arena, TypeId t);
TypeId makeOption(NotNull<BuiltinTypes> builtinTypes, TypeArena& arena, TypeId t);
/** Small utility function for building up type definitions from C++.
*/
TypeId makeFunction( // Monomorphic
TypeArena& arena, std::optional<TypeId> selfType, std::initializer_list<TypeId> paramTypes, std::initializer_list<TypeId> retTypes);
TypeArena& arena,
std::optional<TypeId> selfType,
std::initializer_list<TypeId> paramTypes,
std::initializer_list<TypeId> retTypes,
bool checked = false
);
TypeId makeFunction( // Polymorphic
TypeArena& arena, std::optional<TypeId> selfType, std::initializer_list<TypeId> generics, std::initializer_list<TypePackId> genericPacks,
std::initializer_list<TypeId> paramTypes, std::initializer_list<TypeId> retTypes);
TypeArena& arena,
std::optional<TypeId> selfType,
std::initializer_list<TypeId> generics,
std::initializer_list<TypePackId> genericPacks,
std::initializer_list<TypeId> paramTypes,
std::initializer_list<TypeId> retTypes,
bool checked = false
);
TypeId makeFunction( // Monomorphic
TypeArena& arena, std::optional<TypeId> selfType, std::initializer_list<TypeId> paramTypes, std::initializer_list<std::string> paramNames,
std::initializer_list<TypeId> retTypes);
TypeArena& arena,
std::optional<TypeId> selfType,
std::initializer_list<TypeId> paramTypes,
std::initializer_list<std::string> paramNames,
std::initializer_list<TypeId> retTypes,
bool checked = false
);
TypeId makeFunction( // Polymorphic
TypeArena& arena, std::optional<TypeId> selfType, std::initializer_list<TypeId> generics, std::initializer_list<TypePackId> genericPacks,
std::initializer_list<TypeId> paramTypes, std::initializer_list<std::string> paramNames, std::initializer_list<TypeId> retTypes);
void attachMagicFunction(TypeId ty, MagicFunction fn);
TypeArena& arena,
std::optional<TypeId> selfType,
std::initializer_list<TypeId> generics,
std::initializer_list<TypePackId> genericPacks,
std::initializer_list<TypeId> paramTypes,
std::initializer_list<std::string> paramNames,
std::initializer_list<TypeId> retTypes,
bool checked = false
);
void attachMagicFunction(TypeId ty, std::shared_ptr<MagicFunction> fn);
Property makeProperty(TypeId ty, std::optional<std::string> documentationSymbol = std::nullopt);
void assignPropDocumentationSymbols(TableTypeVar::Props& props, const std::string& baseName);
void assignPropDocumentationSymbols(TableType::Props& props, const std::string& baseName);
std::string getBuiltinDefinitionSource();
std::string getTypeFunctionDefinitionSource();
void addGlobalBinding(TypeChecker& typeChecker, const std::string& name, TypeId ty, const std::string& packageName);
void addGlobalBinding(TypeChecker& typeChecker, const std::string& name, Binding binding);
void addGlobalBinding(TypeChecker& typeChecker, const ScopePtr& scope, const std::string& name, TypeId ty, const std::string& packageName);
void addGlobalBinding(TypeChecker& typeChecker, const ScopePtr& scope, const std::string& name, Binding binding);
std::optional<Binding> tryGetGlobalBinding(TypeChecker& typeChecker, const std::string& name);
Binding* tryGetGlobalBindingRef(TypeChecker& typeChecker, const std::string& name);
TypeId getGlobalBinding(TypeChecker& typeChecker, const std::string& name);
void addGlobalBinding(GlobalTypes& globals, const std::string& name, TypeId ty, const std::string& packageName);
void addGlobalBinding(GlobalTypes& globals, const std::string& name, Binding binding);
void addGlobalBinding(GlobalTypes& globals, const ScopePtr& scope, const std::string& name, TypeId ty, const std::string& packageName);
void addGlobalBinding(GlobalTypes& globals, const ScopePtr& scope, const std::string& name, Binding binding);
std::optional<Binding> tryGetGlobalBinding(GlobalTypes& globals, const std::string& name);
Binding* tryGetGlobalBindingRef(GlobalTypes& globals, const std::string& name);
TypeId getGlobalBinding(GlobalTypes& globals, const std::string& name);
/** A number of built-in functions are magical enough that we need to match on them specifically by
* name when they are called. These are listed here to be used whenever necessary, instead of duplicating this logic repeatedly.
*/
bool matchSetMetatable(const AstExprCall& call);
bool matchTableFreeze(const AstExprCall& call);
bool matchAssert(const AstExprCall& call);
// Returns `true` if the function should introduce typestate for its first argument.
bool shouldTypestateForFirstArgument(const AstExprCall& call);
} // namespace Luau

View file

@ -0,0 +1,24 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include <atomic>
namespace Luau
{
struct FrontendCancellationToken
{
void cancel()
{
cancelled.store(true);
}
bool requested()
{
return cancelled.load();
}
std::atomic<bool> cancelled;
};
} // namespace Luau

View file

@ -0,0 +1,48 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include <Luau/NotNull.h>
#include "Luau/TypeArena.h"
#include "Luau/Type.h"
#include "Luau/Scope.h"
#include <unordered_map>
namespace Luau
{
// Only exposed so they can be unit tested.
using SeenTypes = std::unordered_map<TypeId, TypeId>;
using SeenTypePacks = std::unordered_map<TypePackId, TypePackId>;
struct CloneState
{
NotNull<BuiltinTypes> builtinTypes;
SeenTypes seenTypes;
SeenTypePacks seenTypePacks;
};
/** `shallowClone` will make a copy of only the _top level_ constructor of the type,
* while `clone` will make a deep copy of the entire type and its every component.
*
* Be mindful about which behavior you actually _want_.
*
* Persistent types are not cloned as an optimization.
* If a type is cloned in order to mutate it, 'ignorePersistent' has to be set
*/
TypePackId shallowClone(TypePackId tp, TypeArena& dest, CloneState& cloneState, bool ignorePersistent = false);
TypeId shallowClone(TypeId typeId, TypeArena& dest, CloneState& cloneState, bool ignorePersistent = false);
TypePackId clone(TypePackId tp, TypeArena& dest, CloneState& cloneState);
TypeId clone(TypeId tp, TypeArena& dest, CloneState& cloneState);
TypeFun clone(const TypeFun& typeFun, TypeArena& dest, CloneState& cloneState);
Binding clone(const Binding& binding, TypeArena& dest, CloneState& cloneState);
TypePackId cloneIncremental(TypePackId tp, TypeArena& dest, CloneState& cloneState, Scope* freshScopeForFreeTypes);
TypeId cloneIncremental(TypeId typeId, TypeArena& dest, CloneState& cloneState, Scope* freshScopeForFreeTypes);
TypeFun cloneIncremental(const TypeFun& typeFun, TypeArena& dest, CloneState& cloneState, Scope* freshScopeForFreeTypes);
Binding cloneIncremental(const Binding& binding, TypeArena& dest, CloneState& cloneState, Scope* freshScopeForFreeTypes);
} // namespace Luau

View file

@ -1,58 +0,0 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Linter.h"
#include "Luau/ParseOptions.h"
#include <string>
#include <optional>
#include <vector>
namespace Luau
{
using ModuleName = std::string;
constexpr const char* kConfigName = ".luaurc";
struct Config
{
Config()
{
enabledLint.setDefaults();
}
Mode mode = Mode::NoCheck;
ParseOptions parseOptions;
LintOptions enabledLint;
LintOptions fatalLint;
bool lintErrors = false;
bool typeErrors = true;
std::vector<std::string> globals;
};
struct ConfigResolver
{
virtual ~ConfigResolver() {}
virtual const Config& getConfig(const ModuleName& name) const = 0;
};
struct NullConfigResolver : ConfigResolver
{
Config defaultConfig;
virtual const Config& getConfig(const ModuleName& name) const override;
};
std::optional<std::string> parseModeString(Mode& mode, const std::string& modeString, bool compat = false);
std::optional<std::string> parseLintRuleString(
LintOptions& enabledLints, LintOptions& fatalLints, const std::string& warningName, const std::string& value, bool compat = false);
std::optional<std::string> parseConfig(const std::string& contents, Config& config, bool compat = false);
} // namespace Luau

View file

@ -0,0 +1,332 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Ast.h" // Used for some of the enumerations
#include "Luau/DenseHash.h"
#include "Luau/NotNull.h"
#include "Luau/Variant.h"
#include "Luau/TypeFwd.h"
#include <string>
#include <memory>
#include <vector>
namespace Luau
{
enum class ValueContext;
struct Scope;
// if resultType is a freeType, assignmentType <: freeType <: resultType bounds
struct EqualityConstraint
{
TypeId resultType;
TypeId assignmentType;
};
// subType <: superType
struct SubtypeConstraint
{
TypeId subType;
TypeId superType;
};
// subPack <: superPack
struct PackSubtypeConstraint
{
TypePackId subPack;
TypePackId superPack;
// HACK!! TODO clip.
// We need to know which of `PackSubtypeConstraint` are emitted from `AstStatReturn` vs any others.
// Then we force these specific `PackSubtypeConstraint` to only dispatch in the order of the `return`s.
bool returns = false;
};
// generalizedType ~ gen sourceType
struct GeneralizationConstraint
{
TypeId generalizedType;
TypeId sourceType;
std::vector<TypeId> interiorTypes;
bool hasDeprecatedAttribute = false;
};
// variables ~ iterate iterator
// Unpack the iterator, figure out what types it iterates over, and bind those types to variables.
struct IterableConstraint
{
TypePackId iterator;
std::vector<TypeId> variables;
const AstNode* nextAstFragment;
DenseHashMap<const AstNode*, TypeId>* astForInNextTypes;
};
// name(namedType) = name
struct NameConstraint
{
TypeId namedType;
std::string name;
bool synthetic = false;
std::vector<TypeId> typeParameters;
std::vector<TypePackId> typePackParameters;
};
// target ~ inst target
struct TypeAliasExpansionConstraint
{
// Must be a PendingExpansionType.
TypeId target;
};
struct FunctionCallConstraint
{
TypeId fn;
TypePackId argsPack;
TypePackId result;
class AstExprCall* callSite = nullptr;
std::vector<std::optional<TypeId>> discriminantTypes;
// When we dispatch this constraint, we update the key at this map to record
// the overload that we selected.
DenseHashMap<const AstNode*, TypeId>* astOverloadResolvedTypes = nullptr;
};
// function_check fn argsPack
//
// If fn is a function type and argsPack is a partially solved
// pack of arguments to be supplied to the function, propagate the argument
// types of fn into the types of argsPack. This is used to implement
// bidirectional inference of lambda arguments.
struct FunctionCheckConstraint
{
TypeId fn;
TypePackId argsPack;
class AstExprCall* callSite = nullptr;
NotNull<DenseHashMap<const AstExpr*, TypeId>> astTypes;
NotNull<DenseHashMap<const AstExpr*, TypeId>> astExpectedTypes;
};
// table_check expectedType exprType
//
// If `expectedType` is a table type and `exprType` is _also_ a table type,
// propogate the member types of `expectedType` into the types of `exprType`.
// This is used to implement bidirectional inference on table assignment.
// Also see: FunctionCheckConstraint.
struct TableCheckConstraint
{
TypeId expectedType;
TypeId exprType;
AstExprTable* table = nullptr;
NotNull<DenseHashMap<const AstExpr*, TypeId>> astTypes;
NotNull<DenseHashMap<const AstExpr*, TypeId>> astExpectedTypes;
};
// prim FreeType ExpectedType PrimitiveType
//
// FreeType is bounded below by the singleton type and above by PrimitiveType
// initially. When this constraint is resolved, it will check that the bounds
// of the free type are well-formed by subtyping.
//
// If they are not well-formed, then FreeType is replaced by its lower bound
//
// If they are well-formed and ExpectedType is potentially a singleton (an
// actual singleton or a union that contains a singleton),
// then FreeType is replaced by its lower bound
//
// else FreeType is replaced by PrimitiveType
struct PrimitiveTypeConstraint
{
TypeId freeType;
// potentially gets used to force the lower bound?
std::optional<TypeId> expectedType;
// the primitive type to check against
TypeId primitiveType;
};
// result ~ hasProp type "prop_name"
//
// If the subject is a table, bind the result to the named prop. If the table
// has an indexer, bind it to the index result type. If the subject is a union,
// bind the result to the union of its constituents' properties.
//
// It would be nice to get rid of this constraint and someday replace it with
//
// T <: {p: X}
//
// Where {} describes an inexact shape type.
struct HasPropConstraint
{
TypeId resultType;
TypeId subjectType;
std::string prop;
ValueContext context;
// We want to track if this `HasPropConstraint` comes from a conditional.
// If it does, we're going to change the behavior of property look-up a bit.
// In particular, we're going to return `unknownType` for property lookups
// on `table` or inexact table types where the property is not present.
//
// This allows us to refine table types to have additional properties
// without reporting errors in typechecking on the property tests.
bool inConditional = false;
// HACK: We presently need types like true|false or string|"hello" when
// deciding whether a particular literal expression should have a singleton
// type. This boolean is set to true when extracting the property type of a
// value that may be a union of tables.
//
// For example, in the following code fragment, we want the lookup of the
// success property to yield true|false when extracting an expectedType in
// this expression:
//
// type Result<T, E> = {success:true, result: T} | {success:false, error: E}
//
// local r: Result<number, string> = {success=true, result=9}
//
// If we naively simplify the expectedType to boolean, we will erroneously
// compute the type boolean for the success property of the table literal.
// This causes type checking to fail.
bool suppressSimplification = false;
};
// resultType ~ hasIndexer subjectType indexType
//
// If the subject type is a table or table-like thing that supports indexing,
// populate the type result with the result type of such an index operation.
//
// If the subject is not indexable, resultType is bound to errorType.
struct HasIndexerConstraint
{
TypeId resultType;
TypeId subjectType;
TypeId indexType;
};
// assignProp lhsType propName rhsType
//
// Assign a value of type rhsType into the named property of lhsType.
struct AssignPropConstraint
{
TypeId lhsType;
std::string propName;
TypeId rhsType;
/// If a new property is to be inserted into a table type, it will be
/// ascribed this location.
std::optional<Location> propLocation;
/// The canonical write type of the property. It is _solely_ used to
/// populate astTypes during constraint resolution. Nothing should ever
/// block on it.
TypeId propType;
// When we generate constraints, we increment the remaining prop count on
// the table if we are able. This flag informs the solver as to whether or
// not it should in turn decrement the prop count when this constraint is
// dispatched.
bool decrementPropCount = false;
};
struct AssignIndexConstraint
{
TypeId lhsType;
TypeId indexType;
TypeId rhsType;
/// The canonical write type of the property. It is _solely_ used to
/// populate astTypes during constraint resolution. Nothing should ever
/// block on it.
TypeId propType;
};
// resultTypes ~ unpack sourceTypePack
//
// Similar to PackSubtypeConstraint, but with one important difference: If the
// sourcePack is blocked, this constraint blocks.
struct UnpackConstraint
{
std::vector<TypeId> resultPack;
TypePackId sourcePack;
};
// ty ~ reduce ty
//
// Try to reduce ty, if it is a TypeFunctionInstanceType. Otherwise, do nothing.
struct ReduceConstraint
{
TypeId ty;
};
// tp ~ reduce tp
//
// Analogous to ReduceConstraint, but for type packs.
struct ReducePackConstraint
{
TypePackId tp;
};
using ConstraintV = Variant<
SubtypeConstraint,
PackSubtypeConstraint,
GeneralizationConstraint,
IterableConstraint,
NameConstraint,
TypeAliasExpansionConstraint,
FunctionCallConstraint,
FunctionCheckConstraint,
PrimitiveTypeConstraint,
HasPropConstraint,
HasIndexerConstraint,
AssignPropConstraint,
AssignIndexConstraint,
UnpackConstraint,
ReduceConstraint,
ReducePackConstraint,
EqualityConstraint,
TableCheckConstraint>;
struct Constraint
{
Constraint(NotNull<Scope> scope, const Location& location, ConstraintV&& c);
Constraint(const Constraint&) = delete;
Constraint& operator=(const Constraint&) = delete;
NotNull<Scope> scope;
Location location;
ConstraintV c;
std::vector<NotNull<Constraint>> dependencies;
DenseHashSet<TypeId> getMaybeMutatedFreeTypes() const;
};
using ConstraintPtr = std::unique_ptr<Constraint>;
bool isReferenceCountedType(const TypeId typ);
inline Constraint& asMutable(const Constraint& c)
{
return const_cast<Constraint&>(c);
}
template<typename T>
T* getMutable(Constraint& c)
{
return ::Luau::get_if<T>(&c.c);
}
template<typename T>
const T* get(const Constraint& c)
{
return getMutable<T>(asMutable(c));
}
} // namespace Luau

View file

@ -0,0 +1,487 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Ast.h"
#include "Luau/Constraint.h"
#include "Luau/ConstraintSet.h"
#include "Luau/ControlFlow.h"
#include "Luau/DataFlowGraph.h"
#include "Luau/EqSatSimplification.h"
#include "Luau/InsertionOrderedMap.h"
#include "Luau/Module.h"
#include "Luau/ModuleResolver.h"
#include "Luau/Normalize.h"
#include "Luau/NotNull.h"
#include "Luau/Polarity.h"
#include "Luau/Refinement.h"
#include "Luau/Symbol.h"
#include "Luau/TypeFwd.h"
#include "Luau/TypeUtils.h"
#include <memory>
#include <vector>
namespace Luau
{
struct Scope;
using ScopePtr = std::shared_ptr<Scope>;
struct DcrLogger;
struct TypeFunctionRuntime;
struct Inference
{
TypeId ty = nullptr;
RefinementId refinement = nullptr;
Inference() = default;
explicit Inference(TypeId ty, RefinementId refinement = nullptr)
: ty(ty)
, refinement(refinement)
{
}
};
struct InferencePack
{
TypePackId tp = nullptr;
std::vector<RefinementId> refinements;
InferencePack() = default;
explicit InferencePack(TypePackId tp, const std::vector<RefinementId>& refinements = {})
: tp(tp)
, refinements(refinements)
{
}
};
struct ConstraintGenerator
{
// A list of all the scopes in the module. This vector holds ownership of the
// scope pointers; the scopes themselves borrow pointers to other scopes to
// define the scope hierarchy.
std::vector<std::pair<Location, ScopePtr>> scopes;
ModulePtr module;
NotNull<BuiltinTypes> builtinTypes;
const NotNull<TypeArena> arena;
// The root scope of the module we're generating constraints for.
// This is null when the CG is initially constructed.
Scope* rootScope;
TypeContext typeContext = TypeContext::Default;
struct InferredBinding
{
Scope* scope;
Location location;
TypeIds types;
};
// Some locals have multiple type states. We wish for Scope::bindings to
// map each local name onto the union of every type that the local can have
// over its lifetime, so we use this map to accumulate the set of types it
// might have.
//
// See the functions recordInferredBinding and fillInInferredBindings.
DenseHashMap<Symbol, InferredBinding> inferredBindings{{}};
// Constraints that go straight to the solver.
std::vector<ConstraintPtr> constraints;
// The set of all free types introduced during constraint generation.
DenseHashSet<TypeId> freeTypes{nullptr};
// Map a function's signature scope back to its signature type.
DenseHashMap<Scope*, TypeId> scopeToFunction{nullptr};
// The private scope of type aliases for which the type parameters belong to.
DenseHashMap<const AstStatTypeAlias*, ScopePtr> astTypeAliasDefiningScopes{nullptr};
NotNull<const DataFlowGraph> dfg;
RefinementArena refinementArena;
int recursionCount = 0;
// It is pretty uncommon for constraint generation to itself produce errors, but it can happen.
std::vector<TypeError> errors;
// Needed to be able to enable error-suppression preservation for immediate refinements.
NotNull<Normalizer> normalizer;
NotNull<Simplifier> simplifier;
// Needed to register all available type functions for execution at later stages.
NotNull<TypeFunctionRuntime> typeFunctionRuntime;
DenseHashMap<const AstStatTypeFunction*, ScopePtr> astTypeFunctionEnvironmentScopes{nullptr};
// Needed to resolve modules to make 'require' import types properly.
NotNull<ModuleResolver> moduleResolver;
// Occasionally constraint generation needs to produce an ICE.
const NotNull<InternalErrorReporter> ice;
ScopePtr globalScope;
ScopePtr typeFunctionScope;
std::function<void(const ModuleName&, const ScopePtr&)> prepareModuleScope;
std::vector<RequireCycle> requireCycles;
DenseHashMap<TypeId, TypeIds> localTypes{nullptr};
DenseHashMap<AstExpr*, Inference> inferredExprCache{nullptr};
DcrLogger* logger;
ConstraintGenerator(
ModulePtr module,
NotNull<Normalizer> normalizer,
NotNull<Simplifier> simplifier,
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
NotNull<ModuleResolver> moduleResolver,
NotNull<BuiltinTypes> builtinTypes,
NotNull<InternalErrorReporter> ice,
const ScopePtr& globalScope,
const ScopePtr& typeFunctionScope,
std::function<void(const ModuleName&, const ScopePtr&)> prepareModuleScope,
DcrLogger* logger,
NotNull<DataFlowGraph> dfg,
std::vector<RequireCycle> requireCycles
);
ConstraintSet run(AstStatBlock* block);
ConstraintSet runOnFragment(const ScopePtr& resumeScope, AstStatBlock* block);
/**
* The entry point to the ConstraintGenerator. This will construct a set
* of scopes, constraints, and free types that can be solved later.
* @param block the root block to generate constraints for.
*/
void visitModuleRoot(AstStatBlock* block);
void visitFragmentRoot(const ScopePtr& resumeScope, AstStatBlock* block);
private:
struct InteriorFreeTypes
{
std::vector<TypeId> types;
std::vector<TypePackId> typePacks;
};
std::vector<std::vector<TypeId>> DEPRECATED_interiorTypes;
std::vector<InteriorFreeTypes> interiorFreeTypes;
/**
* Fabricates a new free type belonging to a given scope.
* @param scope the scope the free type belongs to.
*/
TypeId freshType(const ScopePtr& scope, Polarity polarity = Polarity::Unknown);
/**
* Fabricates a new free type pack belonging to a given scope.
* @param scope the scope the free type pack belongs to.
*/
TypePackId freshTypePack(const ScopePtr& scope, Polarity polarity = Polarity::Unknown);
/**
* Allocate a new TypePack with the given head and tail.
*
* Avoids allocating 0-length type packs:
*
* If the head is non-empty, allocate and return a type pack with the given
* head and tail.
* If the head is empty and tail is non-empty, return *tail.
* If both the head and tail are empty, return an empty type pack.
*/
TypePackId addTypePack(std::vector<TypeId> head, std::optional<TypePackId> tail);
/**
* Fabricates a scope that is a child of another scope.
* @param node the lexical node that the scope belongs to.
* @param parent the parent scope of the new scope. Must not be null.
*/
ScopePtr childScope(AstNode* node, const ScopePtr& parent);
std::optional<TypeId> lookup(const ScopePtr& scope, Location location, DefId def, bool prototype = true);
/**
* Adds a new constraint with no dependencies to a given scope.
* @param scope the scope to add the constraint to.
* @param cv the constraint variant to add.
* @return the pointer to the inserted constraint
*/
NotNull<Constraint> addConstraint(const ScopePtr& scope, const Location& location, ConstraintV cv);
/**
* Adds a constraint to a given scope.
* @param scope the scope to add the constraint to. Must not be null.
* @param c the constraint to add.
* @return the pointer to the inserted constraint
*/
NotNull<Constraint> addConstraint(const ScopePtr& scope, std::unique_ptr<Constraint> c);
struct RefinementPartition
{
// Types that we want to intersect against the type of the expression.
std::vector<TypeId> discriminantTypes;
// Sometimes the type we're discriminating against is implicitly nil.
bool shouldAppendNilType = false;
};
using RefinementContext = InsertionOrderedMap<DefId, RefinementPartition>;
void unionRefinements(
const ScopePtr& scope,
Location location,
const RefinementContext& lhs,
const RefinementContext& rhs,
RefinementContext& dest,
std::vector<ConstraintV>* constraints
);
void computeRefinement(
const ScopePtr& scope,
Location location,
RefinementId refinement,
RefinementContext* refis,
bool sense,
bool eq,
std::vector<ConstraintV>* constraints
);
void applyRefinements(const ScopePtr& scope, Location location, RefinementId refinement);
LUAU_NOINLINE void checkAliases(const ScopePtr& scope, AstStatBlock* block);
ControlFlow visitBlockWithoutChildScope(const ScopePtr& scope, AstStatBlock* block);
ControlFlow visitBlockWithoutChildScope_DEPRECATED(const ScopePtr& scope, AstStatBlock* block);
ControlFlow visit(const ScopePtr& scope, AstStat* stat);
ControlFlow visit(const ScopePtr& scope, AstStatBlock* block);
ControlFlow visit(const ScopePtr& scope, AstStatLocal* local);
ControlFlow visit(const ScopePtr& scope, AstStatFor* for_);
ControlFlow visit(const ScopePtr& scope, AstStatForIn* forIn);
ControlFlow visit(const ScopePtr& scope, AstStatWhile* while_);
ControlFlow visit(const ScopePtr& scope, AstStatRepeat* repeat);
ControlFlow visit(const ScopePtr& scope, AstStatLocalFunction* function);
ControlFlow visit(const ScopePtr& scope, AstStatFunction* function);
ControlFlow visit(const ScopePtr& scope, AstStatReturn* ret);
ControlFlow visit(const ScopePtr& scope, AstStatAssign* assign);
ControlFlow visit(const ScopePtr& scope, AstStatCompoundAssign* assign);
ControlFlow visit(const ScopePtr& scope, AstStatIf* ifStatement);
ControlFlow visit(const ScopePtr& scope, AstStatTypeAlias* alias);
ControlFlow visit(const ScopePtr& scope, AstStatTypeFunction* function);
ControlFlow visit(const ScopePtr& scope, AstStatDeclareGlobal* declareGlobal);
ControlFlow visit(const ScopePtr& scope, AstStatDeclareExternType* declareExternType);
ControlFlow visit(const ScopePtr& scope, AstStatDeclareFunction* declareFunction);
ControlFlow visit(const ScopePtr& scope, AstStatError* error);
InferencePack checkPack(const ScopePtr& scope, AstArray<AstExpr*> exprs, const std::vector<std::optional<TypeId>>& expectedTypes = {});
InferencePack checkPack(
const ScopePtr& scope,
AstExpr* expr,
const std::vector<std::optional<TypeId>>& expectedTypes = {},
bool generalize = true
);
InferencePack checkPack(const ScopePtr& scope, AstExprCall* call);
/**
* Checks an expression that is expected to evaluate to one type.
* @param scope the scope the expression is contained within.
* @param expr the expression to check.
* @param expectedType the type of the expression that is expected from its
* surrounding context. Used to implement bidirectional type checking.
* @param generalize If true, generalize any lambdas that are encountered.
* @return the type of the expression.
*/
Inference check(
const ScopePtr& scope,
AstExpr* expr,
std::optional<TypeId> expectedType = {},
bool forceSingleton = false,
bool generalize = true
);
Inference check(const ScopePtr& scope, AstExprConstantString* string, std::optional<TypeId> expectedType, bool forceSingleton);
Inference check(const ScopePtr& scope, AstExprConstantBool* boolExpr, std::optional<TypeId> expectedType, bool forceSingleton);
Inference check(const ScopePtr& scope, AstExprLocal* local);
Inference check(const ScopePtr& scope, AstExprGlobal* global);
Inference checkIndexName(const ScopePtr& scope, const RefinementKey* key, AstExpr* indexee, const std::string& index, Location indexLocation);
Inference check(const ScopePtr& scope, AstExprIndexName* indexName);
Inference check(const ScopePtr& scope, AstExprIndexExpr* indexExpr);
Inference check(const ScopePtr& scope, AstExprFunction* func, std::optional<TypeId> expectedType, bool generalize);
Inference check(const ScopePtr& scope, AstExprUnary* unary);
Inference check(const ScopePtr& scope, AstExprBinary* binary, std::optional<TypeId> expectedType);
Inference checkAstExprBinary(
const ScopePtr& scope,
const Location& location,
AstExprBinary::Op op,
AstExpr* left,
AstExpr* right,
std::optional<TypeId> expectedType
);
Inference check(const ScopePtr& scope, AstExprIfElse* ifElse, std::optional<TypeId> expectedType);
Inference check(const ScopePtr& scope, AstExprTypeAssertion* typeAssert);
Inference check(const ScopePtr& scope, AstExprInterpString* interpString);
Inference check(const ScopePtr& scope, AstExprTable* expr, std::optional<TypeId> expectedType);
std::tuple<TypeId, TypeId, RefinementId> checkBinary(
const ScopePtr& scope,
AstExprBinary::Op op,
AstExpr* left,
AstExpr* right,
std::optional<TypeId> expectedType
);
void visitLValue(const ScopePtr& scope, AstExpr* expr, TypeId rhsType);
void visitLValue(const ScopePtr& scope, AstExprLocal* local, TypeId rhsType);
void visitLValue(const ScopePtr& scope, AstExprGlobal* global, TypeId rhsType);
void visitLValue(const ScopePtr& scope, AstExprIndexName* indexName, TypeId rhsType);
void visitLValue(const ScopePtr& scope, AstExprIndexExpr* indexExpr, TypeId rhsType);
struct FunctionSignature
{
// The type of the function.
TypeId signature;
// The scope that encompasses the function's signature. May be nullptr
// if there was no need for a signature scope (the function has no
// generics).
ScopePtr signatureScope;
// The scope that encompasses the function's body. Is a child scope of
// signatureScope, if present.
ScopePtr bodyScope;
};
FunctionSignature checkFunctionSignature(
const ScopePtr& parent,
AstExprFunction* fn,
std::optional<TypeId> expectedType = {},
std::optional<Location> originalName = {}
);
/**
* Checks the body of a function expression.
* @param scope the interior scope of the body of the function.
* @param fn the function expression to check.
*/
void checkFunctionBody(const ScopePtr& scope, AstExprFunction* fn);
// Specializations of 'resolveType' below
TypeId resolveReferenceType(const ScopePtr& scope, AstType* ty, AstTypeReference* ref, bool inTypeArguments, bool replaceErrorWithFresh);
TypeId resolveTableType(const ScopePtr& scope, AstType* ty, AstTypeTable* tab, bool inTypeArguments, bool replaceErrorWithFresh);
TypeId resolveFunctionType(const ScopePtr& scope, AstType* ty, AstTypeFunction* fn, bool inTypeArguments, bool replaceErrorWithFresh);
/**
* Resolves a type from its AST annotation.
* @param scope the scope that the type annotation appears within.
* @param ty the AST annotation to resolve.
* @param inTypeArguments whether we are resolving a type that's contained within type arguments, `<...>`.
* @return the type of the AST annotation.
**/
TypeId resolveType(const ScopePtr& scope, AstType* ty, bool inTypeArguments, bool replaceErrorWithFresh = false);
// resolveType() is recursive, but we only want to invoke
// inferGenericPolarities() once at the very end. We thus isolate the
// recursive part of the algorithm to this internal helper.
TypeId resolveType_(const ScopePtr& scope, AstType* ty, bool inTypeArguments, bool replaceErrorWithFresh = false);
/**
* Resolves a type pack from its AST annotation.
* @param scope the scope that the type annotation appears within.
* @param tp the AST annotation to resolve.
* @param inTypeArguments whether we are resolving a type that's contained within type arguments, `<...>`.
* @return the type pack of the AST annotation.
**/
TypePackId resolveTypePack(const ScopePtr& scope, AstTypePack* tp, bool inTypeArguments, bool replaceErrorWithFresh = false);
// Inner hepler for resolveTypePack
TypePackId resolveTypePack_(const ScopePtr& scope, AstTypePack* tp, bool inTypeArguments, bool replaceErrorWithFresh = false);
/**
* Resolves a type pack from its AST annotation.
* @param scope the scope that the type annotation appears within.
* @param list the AST annotation to resolve.
* @param inTypeArguments whether we are resolving a type that's contained within type arguments, `<...>`.
* @return the type pack of the AST annotation.
**/
TypePackId resolveTypePack(const ScopePtr& scope, const AstTypeList& list, bool inTypeArguments, bool replaceErrorWithFresh = false);
/**
* Creates generic types given a list of AST definitions, resolving default
* types as required.
* @param scope the scope that the generics should belong to.
* @param generics the AST generics to create types for.
* @param useCache whether to use the generic type cache for the given
* scope.
* @param addTypes whether to add the types to the scope's
* privateTypeBindings map.
**/
std::vector<std::pair<Name, GenericTypeDefinition>> createGenerics(
const ScopePtr& scope,
AstArray<AstGenericType*> generics,
bool useCache = false,
bool addTypes = true
);
/**
* Creates generic type packs given a list of AST definitions, resolving
* default type packs as required.
* @param scope the scope that the generic packs should belong to.
* @param generics the AST generics to create type packs for.
* @param useCache whether to use the generic type pack cache for the given
* scope.
* @param addTypes whether to add the types to the scope's
* privateTypePackBindings map.
**/
std::vector<std::pair<Name, GenericTypePackDefinition>> createGenericPacks(
const ScopePtr& scope,
AstArray<AstGenericTypePack*> generics,
bool useCache = false,
bool addTypes = true
);
Inference flattenPack(const ScopePtr& scope, Location location, InferencePack pack);
void reportError(Location location, TypeErrorData err);
void reportCodeTooComplex(Location location);
// make a union type function of these two types
TypeId makeUnion(const ScopePtr& scope, Location location, TypeId lhs, TypeId rhs);
// make an intersect type function of these two types
TypeId makeIntersect(const ScopePtr& scope, Location location, TypeId lhs, TypeId rhs);
void prepopulateGlobalScopeForFragmentTypecheck(const ScopePtr& globalScope, const ScopePtr& resumeScope, AstStatBlock* program);
/** Scan the program for global definitions.
*
* ConstraintGenerator needs to differentiate between globals and accesses to undefined symbols. Doing this "for
* real" in a general way is going to be pretty hard, so we are choosing not to tackle that yet. For now, we do an
* initial scan of the AST and note what globals are defined.
*/
void prepopulateGlobalScope(const ScopePtr& globalScope, AstStatBlock* program);
bool recordPropertyAssignment(TypeId ty);
// Record the fact that a particular local has a particular type in at least
// one of its states.
void recordInferredBinding(AstLocal* local, TypeId ty);
void fillInInferredBindings(const ScopePtr& globalScope, AstStatBlock* block);
/** Given a function type annotation, return a vector describing the expected types of the calls to the function
* For example, calling a function with annotation ((number) -> string & ((string) -> number))
* yields a vector of size 1, with value: [number | string]
*/
std::vector<std::optional<TypeId>> getExpectedCallTypesForFunctionOverloads(const TypeId fnType);
TypeId createTypeFunctionInstance(
const TypeFunction& function,
std::vector<TypeId> typeArguments,
std::vector<TypePackId> packArguments,
const ScopePtr& scope,
Location location
);
TypeId simplifyUnion(const ScopePtr& scope, Location location, TypeId left, TypeId right);
};
} // namespace Luau

View file

@ -0,0 +1,32 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Constraint.h"
#include "Luau/DenseHash.h"
#include "Luau/Error.h"
#include <vector>
namespace Luau
{
struct ConstraintSet
{
NotNull<Scope> rootScope;
std::vector<ConstraintPtr> constraints;
// The set of all free types created during constraint generation
DenseHashSet<TypeId> freeTypes{nullptr};
// Map a function's signature scope back to its signature type. Once we've
// dispatched all of the constraints pertaining to a particular free type,
// we use this mapping to generalize that free type.
DenseHashMap<Scope*, TypeId> scopeToFunction{nullptr};
// It is pretty uncommon for constraint generation to itself produce errors, but it can happen.
std::vector<TypeError> errors;
};
}

View file

@ -0,0 +1,459 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Constraint.h"
#include "Luau/ConstraintSet.h"
#include "Luau/DataFlowGraph.h"
#include "Luau/DenseHash.h"
#include "Luau/EqSatSimplification.h"
#include "Luau/Error.h"
#include "Luau/Location.h"
#include "Luau/Module.h"
#include "Luau/Normalize.h"
#include "Luau/Substitution.h"
#include "Luau/ToString.h"
#include "Luau/Type.h"
#include "Luau/TypeCheckLimits.h"
#include "Luau/TypeFunction.h"
#include "Luau/TypeFwd.h"
#include "Luau/Variant.h"
#include <utility>
#include <vector>
namespace Luau
{
enum class ValueContext;
struct DcrLogger;
class AstExpr;
// TypeId, TypePackId, or Constraint*. It is impossible to know which, but we
// never dereference this pointer.
using BlockedConstraintId = Variant<TypeId, TypePackId, const Constraint*>;
struct HashBlockedConstraintId
{
size_t operator()(const BlockedConstraintId& bci) const;
};
struct ModuleResolver;
struct InstantiationSignature
{
TypeFun fn;
std::vector<TypeId> arguments;
std::vector<TypePackId> packArguments;
bool operator==(const InstantiationSignature& rhs) const;
bool operator!=(const InstantiationSignature& rhs) const
{
return !((*this) == rhs);
}
};
struct HashInstantiationSignature
{
size_t operator()(const InstantiationSignature& signature) const;
};
struct TablePropLookupResult
{
// What types are we blocked on for determining this type?
std::vector<TypeId> blockedTypes;
// The type of the property (if we were able to determine it).
std::optional<TypeId> propType;
// Whether or not this is _definitely_ derived as the result of an indexer.
// We use this to determine whether or not code like:
//
// t.lol = nil;
//
// ... is legal. If `t: { [string]: ~nil }` then this is legal as
// there's no guarantee on whether "lol" specifically exists.
// However, if `t: { lol: ~nil }`, then we cannot allow assignment as
// that would remove "lol" from the table entirely.
bool isIndex = false;
};
struct ConstraintSolver
{
NotNull<TypeArena> arena;
NotNull<BuiltinTypes> builtinTypes;
InternalErrorReporter iceReporter;
NotNull<Normalizer> normalizer;
NotNull<Simplifier> simplifier;
NotNull<TypeFunctionRuntime> typeFunctionRuntime;
// The entire set of constraints that the solver is trying to resolve.
ConstraintSet constraintSet;
std::vector<NotNull<Constraint>> constraints;
NotNull<DenseHashMap<Scope*, TypeId>> scopeToFunction;
NotNull<Scope> rootScope;
ModuleName currentModuleName;
// The dataflow graph of the program, used in constraint generation and for magic functions.
NotNull<const DataFlowGraph> dfg;
// Constraints that the solver has generated, rather than sourcing from the
// scope tree.
std::vector<std::unique_ptr<Constraint>> solverConstraints;
// This includes every constraint that has not been fully solved.
// A constraint can be both blocked and unsolved, for instance.
std::vector<NotNull<const Constraint>> unsolvedConstraints;
// A mapping of constraint pointer to how many things the constraint is
// blocked on. Can be empty or 0 for constraints that are not blocked on
// anything.
std::unordered_map<NotNull<const Constraint>, size_t> blockedConstraints;
// A mapping of type/pack pointers to the constraints they block.
std::unordered_map<BlockedConstraintId, DenseHashSet<const Constraint*>, HashBlockedConstraintId> blocked;
// Memoized instantiations of type aliases.
DenseHashMap<InstantiationSignature, TypeId, HashInstantiationSignature> instantiatedAliases{{}};
// Breadcrumbs for where a free type's upper bound was expanded. We use
// these to provide more helpful error messages when a free type is solved
// as never unexpectedly.
DenseHashMap<TypeId, std::vector<std::pair<Location, TypeId>>> upperBoundContributors{nullptr};
// A mapping from free types to the number of unresolved constraints that mention them.
DenseHashMap<TypeId, size_t> unresolvedConstraints{{}};
std::unordered_map<NotNull<const Constraint>, DenseHashSet<TypeId>> maybeMutatedFreeTypes;
std::unordered_map<TypeId, DenseHashSet<const Constraint*>> mutatedFreeTypeToConstraint;
// Irreducible/uninhabited type functions or type pack functions.
DenseHashSet<const void*> uninhabitedTypeFunctions{{}};
// The set of types that will definitely be unchanged by generalization.
DenseHashSet<TypeId> generalizedTypes_{nullptr};
const NotNull<DenseHashSet<TypeId>> generalizedTypes{&generalizedTypes_};
// Recorded errors that take place within the solver.
ErrorVec errors;
NotNull<ModuleResolver> moduleResolver;
std::vector<RequireCycle> requireCycles;
DcrLogger* logger;
TypeCheckLimits limits;
DenseHashMap<TypeId, const Constraint*> typeFunctionsToFinalize{nullptr};
explicit ConstraintSolver(
NotNull<Normalizer> normalizer,
NotNull<Simplifier> simplifier,
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
ModuleName moduleName,
NotNull<ModuleResolver> moduleResolver,
std::vector<RequireCycle> requireCycles,
DcrLogger* logger,
NotNull<const DataFlowGraph> dfg,
TypeCheckLimits limits,
ConstraintSet constraintSet
);
explicit ConstraintSolver(
NotNull<Normalizer> normalizer,
NotNull<Simplifier> simplifier,
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
NotNull<Scope> rootScope,
std::vector<NotNull<Constraint>> constraints,
NotNull<DenseHashMap<Scope*, TypeId>> scopeToFunction,
ModuleName moduleName,
NotNull<ModuleResolver> moduleResolver,
std::vector<RequireCycle> requireCycles,
DcrLogger* logger,
NotNull<const DataFlowGraph> dfg,
TypeCheckLimits limits
);
// Randomize the order in which to dispatch constraints
void randomize(unsigned seed);
/**
* Attempts to dispatch all pending constraints and reach a type solution
* that satisfies all of the constraints.
**/
void run();
/**
* Attempts to perform one final reduction on type functions after every constraint has been completed
*
**/
void finalizeTypeFunctions();
bool isDone() const;
private:
/// A helper that does most of the setup work that is shared between the two constructors.
void initFreeTypeTracking();
void generalizeOneType(TypeId ty);
/**
* Bind a type variable to another type.
*
* A constraint is required and will validate that blockedTy is owned by this
* constraint. This prevents one constraint from interfering with another's
* blocked types.
*
* Bind will also unblock the type variable for you.
*/
void bind(NotNull<const Constraint> constraint, TypeId ty, TypeId boundTo);
void bind(NotNull<const Constraint> constraint, TypePackId tp, TypePackId boundTo);
template<typename T, typename... Args>
void emplace(NotNull<const Constraint> constraint, TypeId ty, Args&&... args);
template<typename T, typename... Args>
void emplace(NotNull<const Constraint> constraint, TypePackId tp, Args&&... args);
public:
/** Attempt to dispatch a constraint. Returns true if it was successful. If
* tryDispatch() returns false, the constraint remains in the unsolved set
* and will be retried later.
*/
bool tryDispatch(NotNull<const Constraint> c, bool force);
bool tryDispatch(const SubtypeConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const PackSubtypeConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const GeneralizationConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const IterableConstraint& c, NotNull<const Constraint> constraint, bool force);
bool tryDispatch(const NameConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const TypeAliasExpansionConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const FunctionCallConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const TableCheckConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const FunctionCheckConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const PrimitiveTypeConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const HasPropConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatchHasIndexer(
int& recursionDepth,
NotNull<const Constraint> constraint,
TypeId subjectType,
TypeId indexType,
TypeId resultType,
Set<TypeId>& seen
);
bool tryDispatch(const HasIndexerConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const AssignPropConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const AssignIndexConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const UnpackConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const ReduceConstraint& c, NotNull<const Constraint> constraint, bool force);
bool tryDispatch(const ReducePackConstraint& c, NotNull<const Constraint> constraint, bool force);
bool tryDispatch(const EqualityConstraint& c, NotNull<const Constraint> constraint);
// for a, ... in some_table do
// also handles __iter metamethod
bool tryDispatchIterableTable(TypeId iteratorTy, const IterableConstraint& c, NotNull<const Constraint> constraint, bool force);
// for a, ... in next_function, t, ... do
bool tryDispatchIterableFunction(TypeId nextTy, TypeId tableTy, const IterableConstraint& c, NotNull<const Constraint> constraint);
TablePropLookupResult lookupTableProp(
NotNull<const Constraint> constraint,
TypeId subjectType,
const std::string& propName,
ValueContext context,
bool inConditional = false,
bool suppressSimplification = false
);
TablePropLookupResult lookupTableProp(
NotNull<const Constraint> constraint,
TypeId subjectType,
const std::string& propName,
ValueContext context,
bool inConditional,
bool suppressSimplification,
DenseHashSet<TypeId>& seen
);
/**
* Generate constraints to unpack the types of srcTypes and assign each
* value to the corresponding BlockedType in destTypes.
*
* This function also overwrites the owners of each BlockedType. This is
* okay because this function is only used to decompose IterableConstraint
* into an UnpackConstraint.
*
* @param destTypes A vector of types comprised of BlockedTypes.
* @param srcTypes A TypePack that represents rvalues to be assigned.
* @returns The underlying UnpackConstraint. There's a bit of code in
* iteration that needs to pass blocks on to this constraint.
*/
NotNull<const Constraint> unpackAndAssign(const std::vector<TypeId> destTypes, TypePackId srcTypes, NotNull<const Constraint> constraint);
void block(NotNull<const Constraint> target, NotNull<const Constraint> constraint);
/**
* Block a constraint on the resolution of a Type.
* @returns false always. This is just to allow tryDispatch to return the result of block()
*/
bool block(TypeId target, NotNull<const Constraint> constraint);
bool block(TypePackId target, NotNull<const Constraint> constraint);
// Block on every target
template<typename T>
bool block(const T& targets, NotNull<const Constraint> constraint)
{
for (TypeId target : targets)
block(target, constraint);
return false;
}
/**
* For all constraints that are blocked on one constraint, make them block
* on a new constraint.
* @param source the constraint to copy blocks from.
* @param addition the constraint that other constraints should now block on.
*/
void inheritBlocks(NotNull<const Constraint> source, NotNull<const Constraint> addition);
// Traverse the type. If any pending types are found, block the constraint
// on them.
//
// Returns false if a type blocks the constraint.
//
// FIXME: This use of a boolean for the return result is an appalling
// interface.
bool blockOnPendingTypes(TypeId target, NotNull<const Constraint> constraint);
bool blockOnPendingTypes(TypePackId targetPack, NotNull<const Constraint> constraint);
void unblock(NotNull<const Constraint> progressed);
void unblock(TypeId ty, Location location);
void unblock(TypePackId progressed, Location location);
void unblock(const std::vector<TypeId>& types, Location location);
void unblock(const std::vector<TypePackId>& packs, Location location);
/**
* @returns true if the TypeId is in a blocked state.
*/
bool isBlocked(TypeId ty) const;
/**
* @returns true if the TypePackId is in a blocked state.
*/
bool isBlocked(TypePackId tp) const;
/**
* Returns whether the constraint is blocked on anything.
* @param constraint the constraint to check.
*/
bool isBlocked(NotNull<const Constraint> constraint) const;
/** Pushes a new solver constraint to the solver.
* @param cv the body of the constraint.
**/
NotNull<Constraint> pushConstraint(NotNull<Scope> scope, const Location& location, ConstraintV cv);
/**
* Attempts to resolve a module from its module information. Returns the
* module-level return type of the module, or the error type if one cannot
* be found. Reports errors to the solver if the module cannot be found or
* the require is illegal.
* @param module the module information to look up.
* @param location the location where the require is taking place; used for
* error locations.
**/
TypeId resolveModule(const ModuleInfo& info, const Location& location);
void reportError(TypeErrorData&& data, const Location& location);
void reportError(TypeError e);
/**
* Shifts the count of references from `source` to `target`. This should be paired
* with any instance of binding a free type in order to maintain accurate refcounts.
* If `target` is not a free type, this is a noop.
* @param source the free type which is being bound
* @param target the type which the free type is being bound to
*/
void shiftReferences(TypeId source, TypeId target);
/**
* Generalizes the given free type if the reference counting allows it.
* @param the scope to generalize in
* @param type the free type we want to generalize
* @returns a non-free type that generalizes the argument, or `std::nullopt` if one
* does not exist
*/
std::optional<TypeId> generalizeFreeType(NotNull<Scope> scope, TypeId type);
/**
* Checks the existing set of constraints to see if there exist any that contain
* the provided free type, indicating that it is not yet ready to be replaced by
* one of its bounds.
* @param ty the free type that to check for related constraints
* @returns whether or not it is unsafe to replace the free type by one of its bounds
*/
bool hasUnresolvedConstraints(TypeId ty);
/** Attempts to unify subTy with superTy. If doing so would require unifying
* BlockedTypes, fail and block the constraint on those BlockedTypes.
*
* Note: TID can only be TypeId or TypePackId.
*
* If unification fails, replace all free types with errorType.
*
* If unification succeeds, unblock every type changed by the unification.
*
* @returns true if the unification succeeded. False if the unification was
* too complex.
*/
template<typename TID>
bool unify(NotNull<const Constraint> constraint, TID subTy, TID superTy);
/**
* Marks a constraint as being blocked on a type or type pack. The constraint
* solver will not attempt to dispatch blocked constraints until their
* dependencies have made progress.
* @param target the type or type pack pointer that the constraint is blocked on.
* @param constraint the constraint to block.
**/
bool block_(BlockedConstraintId target, NotNull<const Constraint> constraint);
/**
* Informs the solver that progress has been made on a type or type pack. The
* solver will wake up all constraints that are blocked on the type or type pack,
* and will resume attempting to dispatch them.
* @param progressed the type or type pack pointer that has progressed.
**/
void unblock_(BlockedConstraintId progressed);
/**
* Reproduces any constraints necessary for new types that are copied when applying a substitution.
* At the time of writing, this pertains only to type functions.
* @param subst the substitution that was applied
**/
void reproduceConstraints(NotNull<Scope> scope, const Location& location, const Substitution& subst);
TypeId simplifyIntersection(NotNull<Scope> scope, Location location, TypeId left, TypeId right);
TypeId simplifyIntersection(NotNull<Scope> scope, Location location, std::set<TypeId> parts);
TypeId simplifyUnion(NotNull<Scope> scope, Location location, TypeId left, TypeId right);
TypeId errorRecoveryType() const;
TypePackId errorRecoveryTypePack() const;
TypePackId anyifyModuleReturnTypePackGenerics(TypePackId tp);
void throwTimeLimitError() const;
void throwUserCancelError() const;
ToStringOptions opts;
void fillInDiscriminantTypes(NotNull<const Constraint> constraint, const std::vector<std::optional<TypeId>>& discriminantTypes);
};
/** Borrow a vector of pointers from a vector of owning pointers to constraints.
*/
std::vector<NotNull<Constraint>> borrowConstraints(const std::vector<ConstraintPtr>& constraints);
void dump(NotNull<Scope> rootScope, struct ToStringOptions& opts);
} // namespace Luau

View file

@ -0,0 +1,36 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include <memory>
namespace Luau
{
struct Scope;
using ScopePtr = std::shared_ptr<Scope>;
enum class ControlFlow
{
None = 0b00001,
Returns = 0b00010,
Throws = 0b00100,
Breaks = 0b01000,
Continues = 0b10000,
};
inline ControlFlow operator&(ControlFlow a, ControlFlow b)
{
return ControlFlow(int(a) & int(b));
}
inline ControlFlow operator|(ControlFlow a, ControlFlow b)
{
return ControlFlow(int(a) | int(b));
}
inline bool matches(ControlFlow a, ControlFlow b)
{
return (a & b) != ControlFlow(0);
}
} // namespace Luau

View file

@ -0,0 +1,222 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
// Do not include LValue. It should never be used here.
#include "Luau/Ast.h"
#include "Luau/ControlFlow.h"
#include "Luau/DenseHash.h"
#include "Luau/Def.h"
#include "Luau/NotNull.h"
#include "Luau/Symbol.h"
#include "Luau/TypedAllocator.h"
#include <unordered_map>
namespace Luau
{
struct RefinementKey
{
const RefinementKey* parent = nullptr;
DefId def;
std::optional<std::string> propName;
};
struct RefinementKeyArena
{
TypedAllocator<RefinementKey> allocator;
const RefinementKey* leaf(DefId def);
const RefinementKey* node(const RefinementKey* parent, DefId def, const std::string& propName);
};
struct DataFlowGraph
{
DataFlowGraph(DataFlowGraph&&) = default;
DataFlowGraph& operator=(DataFlowGraph&&) = default;
DefId getDef(const AstExpr* expr) const;
// Look up the definition optionally, knowing it may not be present.
std::optional<DefId> getDefOptional(const AstExpr* expr) const;
DefId getDef(const AstLocal* local) const;
DefId getDef(const AstStatDeclareGlobal* global) const;
DefId getDef(const AstStatDeclareFunction* func) const;
const RefinementKey* getRefinementKey(const AstExpr* expr) const;
private:
DataFlowGraph(NotNull<DefArena> defArena, NotNull<RefinementKeyArena> keyArena);
DataFlowGraph(const DataFlowGraph&) = delete;
DataFlowGraph& operator=(const DataFlowGraph&) = delete;
NotNull<DefArena> defArena;
NotNull<RefinementKeyArena> keyArena;
DenseHashMap<const AstExpr*, const Def*> astDefs{nullptr};
// Sometimes we don't have the AstExprLocal* but we have AstLocal*, and sometimes we need to extract that DefId.
DenseHashMap<const AstLocal*, const Def*> localDefs{nullptr};
// There's no AstStatDeclaration, and it feels useless to introduce it just to enforce an invariant in one place.
// All keys in this maps are really only statements that ambiently declares a symbol.
DenseHashMap<const AstStat*, const Def*> declaredDefs{nullptr};
DenseHashMap<const AstExpr*, const RefinementKey*> astRefinementKeys{nullptr};
friend struct DataFlowGraphBuilder;
};
struct DfgScope
{
enum ScopeType
{
Linear,
Loop,
Function,
};
DfgScope* parent;
ScopeType scopeType;
using Bindings = DenseHashMap<Symbol, const Def*>;
using Props = DenseHashMap<const Def*, std::unordered_map<std::string, const Def*>>;
Bindings bindings{Symbol{}};
Props props{nullptr};
std::optional<DefId> lookup(Symbol symbol) const;
std::optional<DefId> lookup(DefId def, const std::string& key) const;
void inherit(const DfgScope* childScope);
bool canUpdateDefinition(Symbol symbol) const;
bool canUpdateDefinition(DefId def, const std::string& key) const;
};
struct DataFlowResult
{
DefId def;
const RefinementKey* parent = nullptr;
};
using ScopeStack = std::vector<DfgScope*>;
struct DataFlowGraphBuilder
{
static DataFlowGraph build(
AstStatBlock* block,
NotNull<DefArena> defArena,
NotNull<RefinementKeyArena> keyArena,
NotNull<struct InternalErrorReporter> handle
);
private:
DataFlowGraphBuilder(NotNull<DefArena> defArena, NotNull<RefinementKeyArena> keyArena);
DataFlowGraphBuilder(const DataFlowGraphBuilder&) = delete;
DataFlowGraphBuilder& operator=(const DataFlowGraphBuilder&) = delete;
DataFlowGraph graph;
NotNull<DefArena> defArena;
NotNull<RefinementKeyArena> keyArena;
struct InternalErrorReporter* handle = nullptr;
/// The arena owning all of the scope allocations for the dataflow graph being built.
std::vector<std::unique_ptr<DfgScope>> scopes;
/// A stack of scopes used by the visitor to see where we are.
ScopeStack scopeStack;
NotNull<DfgScope> currentScope();
DfgScope* currentScope_DEPRECATED();
struct FunctionCapture
{
std::vector<DefId> captureDefs;
std::vector<DefId> allVersions;
size_t versionOffset = 0;
};
DenseHashMap<Symbol, FunctionCapture> captures{Symbol{}};
void resolveCaptures();
DfgScope* makeChildScope(DfgScope::ScopeType scopeType = DfgScope::Linear);
void join(DfgScope* p, DfgScope* a, DfgScope* b);
void joinBindings(DfgScope* p, const DfgScope& a, const DfgScope& b);
void joinProps(DfgScope* p, const DfgScope& a, const DfgScope& b);
DefId lookup(Symbol symbol, Location location);
DefId lookup(DefId def, const std::string& key, Location location);
ControlFlow visit(AstStatBlock* b);
ControlFlow visitBlockWithoutChildScope(AstStatBlock* b);
ControlFlow visit(AstStat* s);
ControlFlow visit(AstStatIf* i);
ControlFlow visit(AstStatWhile* w);
ControlFlow visit(AstStatRepeat* r);
ControlFlow visit(AstStatBreak* b);
ControlFlow visit(AstStatContinue* c);
ControlFlow visit(AstStatReturn* r);
ControlFlow visit(AstStatExpr* e);
ControlFlow visit(AstStatLocal* l);
ControlFlow visit(AstStatFor* f);
ControlFlow visit(AstStatForIn* f);
ControlFlow visit(AstStatAssign* a);
ControlFlow visit(AstStatCompoundAssign* c);
ControlFlow visit(AstStatFunction* f);
ControlFlow visit(AstStatLocalFunction* l);
ControlFlow visit(AstStatTypeAlias* t);
ControlFlow visit(AstStatTypeFunction* f);
ControlFlow visit(AstStatDeclareGlobal* d);
ControlFlow visit(AstStatDeclareFunction* d);
ControlFlow visit(AstStatDeclareExternType* d);
ControlFlow visit(AstStatError* error);
DataFlowResult visitExpr(AstExpr* e);
DataFlowResult visitExpr(AstExprGroup* group);
DataFlowResult visitExpr(AstExprLocal* l);
DataFlowResult visitExpr(AstExprGlobal* g);
DataFlowResult visitExpr(AstExprCall* c);
DataFlowResult visitExpr(AstExprIndexName* i);
DataFlowResult visitExpr(AstExprIndexExpr* i);
DataFlowResult visitExpr(AstExprFunction* f);
DataFlowResult visitExpr(AstExprTable* t);
DataFlowResult visitExpr(AstExprUnary* u);
DataFlowResult visitExpr(AstExprBinary* b);
DataFlowResult visitExpr(AstExprTypeAssertion* t);
DataFlowResult visitExpr(AstExprIfElse* i);
DataFlowResult visitExpr(AstExprInterpString* i);
DataFlowResult visitExpr(AstExprError* error);
void visitLValue(AstExpr* e, DefId incomingDef);
DefId visitLValue(AstExprLocal* l, DefId incomingDef);
DefId visitLValue(AstExprGlobal* g, DefId incomingDef);
DefId visitLValue(AstExprIndexName* i, DefId incomingDef);
DefId visitLValue(AstExprIndexExpr* i, DefId incomingDef);
DefId visitLValue(AstExprError* e, DefId incomingDef);
void visitType(AstType* t);
void visitType(AstTypeReference* r);
void visitType(AstTypeTable* t);
void visitType(AstTypeFunction* f);
void visitType(AstTypeTypeof* t);
void visitType(AstTypeUnion* u);
void visitType(AstTypeIntersection* i);
void visitType(AstTypeError* error);
void visitTypePack(AstTypePack* p);
void visitTypePack(AstTypePackExplicit* e);
void visitTypePack(AstTypePackVariadic* v);
void visitTypePack(AstTypePackGeneric* g);
void visitTypeList(AstTypeList l);
void visitGenerics(AstArray<AstGenericType*> g);
void visitGenericPacks(AstArray<AstGenericTypePack*> g);
};
} // namespace Luau

View file

@ -0,0 +1,151 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Constraint.h"
#include "Luau/NotNull.h"
#include "Luau/Scope.h"
#include "Luau/Module.h"
#include "Luau/ToString.h"
#include "Luau/Error.h"
#include "Luau/Variant.h"
#include <optional>
#include <string>
#include <vector>
namespace Luau
{
struct ErrorSnapshot
{
std::string message;
Location location;
};
struct BindingSnapshot
{
std::string typeId;
std::string typeString;
Location location;
};
struct TypeBindingSnapshot
{
std::string typeId;
std::string typeString;
};
struct ExprTypesAtLocation
{
Location location;
TypeId ty;
std::optional<TypeId> expectedTy;
};
struct AnnotationTypesAtLocation
{
Location location;
TypeId resolvedTy;
};
struct ConstraintGenerationLog
{
std::string source;
std::vector<ErrorSnapshot> errors;
std::vector<ExprTypesAtLocation> exprTypeLocations;
std::vector<AnnotationTypesAtLocation> annotationTypeLocations;
};
struct ScopeSnapshot
{
std::unordered_map<Name, BindingSnapshot> bindings;
std::unordered_map<Name, TypeBindingSnapshot> typeBindings;
std::unordered_map<Name, TypeBindingSnapshot> typePackBindings;
std::vector<ScopeSnapshot> children;
};
using ConstraintBlockTarget = Variant<TypeId, TypePackId, NotNull<const Constraint>>;
struct ConstraintBlock
{
ConstraintBlockTarget target;
std::string stringification;
};
struct ConstraintSnapshot
{
std::string stringification;
Location location;
std::vector<ConstraintBlock> blocks;
};
struct BoundarySnapshot
{
DenseHashMap<const Constraint*, ConstraintSnapshot> unsolvedConstraints{nullptr};
ScopeSnapshot rootScope;
DenseHashMap<const void*, std::string> typeStrings{nullptr};
};
struct StepSnapshot
{
const Constraint* currentConstraint;
bool forced;
DenseHashMap<const Constraint*, ConstraintSnapshot> unsolvedConstraints{nullptr};
ScopeSnapshot rootScope;
DenseHashMap<const void*, std::string> typeStrings{nullptr};
};
struct TypeSolveLog
{
BoundarySnapshot initialState;
std::vector<StepSnapshot> stepStates;
BoundarySnapshot finalState;
};
struct TypeCheckLog
{
std::vector<ErrorSnapshot> errors;
};
struct DcrLogger
{
std::string compileOutput();
void captureSource(std::string source);
void captureGenerationError(const TypeError& error);
void captureConstraintLocation(NotNull<const Constraint> constraint, Location location);
void captureGenerationModule(const ModulePtr& module);
void pushBlock(NotNull<const Constraint> constraint, TypeId block);
void pushBlock(NotNull<const Constraint> constraint, TypePackId block);
void pushBlock(NotNull<const Constraint> constraint, NotNull<const Constraint> block);
void popBlock(TypeId block);
void popBlock(TypePackId block);
void popBlock(NotNull<const Constraint> block);
void captureInitialSolverState(const Scope* rootScope, const std::vector<NotNull<const Constraint>>& unsolvedConstraints);
StepSnapshot prepareStepSnapshot(
const Scope* rootScope,
NotNull<const Constraint> current,
bool force,
const std::vector<NotNull<const Constraint>>& unsolvedConstraints
);
void commitStepSnapshot(StepSnapshot snapshot);
void captureFinalSolverState(const Scope* rootScope, const std::vector<NotNull<const Constraint>>& unsolvedConstraints);
void captureTypeCheckError(const TypeError& error);
private:
ConstraintGenerationLog generationLog;
std::unordered_map<NotNull<const Constraint>, std::vector<ConstraintBlockTarget>> constraintBlocks;
TypeSolveLog solveLog;
TypeCheckLog checkLog;
ToStringOptions opts{true};
std::vector<ConstraintBlock> snapshotBlocks(NotNull<const Constraint> constraint);
void captureBoundaryState(BoundarySnapshot& target, const Scope* rootScope, const std::vector<NotNull<const Constraint>>& unsolvedConstraints);
};
} // namespace Luau

View file

@ -0,0 +1,91 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/NotNull.h"
#include "Luau/TypedAllocator.h"
#include "Luau/Variant.h"
#include "Luau/Location.h"
#include "Luau/Symbol.h"
#include <string>
#include <optional>
namespace Luau
{
struct Def;
using DefId = NotNull<const Def>;
struct AstLocal;
/**
* A cell is a "single-object" value.
*
* Leaky implementation note: sometimes "multiple-object" values, but none of which were interesting enough to warrant creating a phi node instead.
* That can happen because there's no point in creating a phi node that points to either resultant in `if math.random() > 0.5 then 5 else "hello"`.
* This might become of utmost importance if we wanted to do some backward reasoning, e.g. if `5` is taken, then `cond` must be `truthy`.
*/
struct Cell
{
bool subscripted = false;
};
/**
* A phi node is a union of cells.
*
* We need this because we're statically evaluating a program, and sometimes a place may be assigned with
* different cells, and when that happens, we need a special data type that merges in all the cells
* that will flow into that specific place. For example, consider this simple program:
*
* ```
* x-1
* if cond() then
* x-2 = 5
* else
* x-3 = "hello"
* end
* x-4 : {x-2, x-3}
* ```
*
* At x-4, we know for a fact statically that either `5` or `"hello"` can flow into the variable `x` after the branch, but
* we cannot make any definitive decisions about which one, so we just take in both.
*/
struct Phi
{
std::vector<DefId> operands;
};
/**
* We statically approximate a value at runtime using a symbolic value, which we call a Def.
*
* DataFlowGraphBuilder will allocate these defs as a stand-in for some Luau values, and bind them to places that
* can hold a Luau value, and then observes how those defs will commute as it statically evaluate the program.
*
* It must also be noted that defs are a cyclic graph, so it is not safe to recursively traverse into it expecting it to terminate.
*/
struct Def
{
using V = Variant<struct Cell, struct Phi>;
V v;
Symbol name;
Location location;
};
template<typename T>
const T* get(DefId def)
{
return get_if<T>(&def->v);
}
bool containsSubscriptedDefinition(DefId def);
void collectOperands(DefId def, std::vector<DefId>* operands);
struct DefArena
{
TypedAllocator<Def> allocator;
DefId freshCell(Symbol sym, Location location, bool subscripted = false);
DefId phi(DefId a, DefId b);
DefId phi(const std::vector<DefId>& defs);
};
} // namespace Luau

View file

@ -0,0 +1,208 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/DenseHash.h"
#include "Luau/TypeFwd.h"
#include "Luau/UnifierSharedState.h"
#include <optional>
#include <string>
#include <unordered_set>
namespace Luau
{
struct DiffPathNode
{
// TODO: consider using Variants to simplify toString implementation
enum Kind
{
TableProperty,
FunctionArgument,
FunctionReturn,
Union,
Intersection,
Negation,
};
Kind kind;
// non-null when TableProperty
std::optional<Name> tableProperty;
// non-null when FunctionArgument (unless variadic arg), FunctionReturn (unless variadic arg), Union, or Intersection (i.e. anonymous fields)
std::optional<size_t> index;
/**
* Do not use for leaf nodes
*/
DiffPathNode(Kind kind)
: kind(kind)
{
}
DiffPathNode(Kind kind, std::optional<Name> tableProperty, std::optional<size_t> index)
: kind(kind)
, tableProperty(tableProperty)
, index(index)
{
}
std::string toString() const;
static DiffPathNode constructWithTableProperty(Name tableProperty);
static DiffPathNode constructWithKindAndIndex(Kind kind, size_t index);
static DiffPathNode constructWithKind(Kind kind);
};
struct DiffPathNodeLeaf
{
std::optional<TypeId> ty;
std::optional<Name> tableProperty;
std::optional<int> minLength;
bool isVariadic;
// TODO: Rename to anonymousIndex, for both union and Intersection
std::optional<size_t> unionIndex;
DiffPathNodeLeaf(
std::optional<TypeId> ty,
std::optional<Name> tableProperty,
std::optional<int> minLength,
bool isVariadic,
std::optional<size_t> unionIndex
)
: ty(ty)
, tableProperty(tableProperty)
, minLength(minLength)
, isVariadic(isVariadic)
, unionIndex(unionIndex)
{
}
static DiffPathNodeLeaf detailsNormal(TypeId ty);
static DiffPathNodeLeaf detailsTableProperty(TypeId ty, Name tableProperty);
static DiffPathNodeLeaf detailsUnionIndex(TypeId ty, size_t index);
static DiffPathNodeLeaf detailsLength(int minLength, bool isVariadic);
static DiffPathNodeLeaf nullopts();
};
struct DiffPath
{
std::vector<DiffPathNode> path;
std::string toString(bool prependDot) const;
};
struct DiffError
{
enum Kind
{
Normal,
MissingTableProperty,
MissingUnionMember,
MissingIntersectionMember,
IncompatibleGeneric,
LengthMismatchInFnArgs,
LengthMismatchInFnRets,
};
Kind kind;
DiffPath diffPath;
DiffPathNodeLeaf left;
DiffPathNodeLeaf right;
std::string leftRootName;
std::string rightRootName;
DiffError(Kind kind, DiffPathNodeLeaf left, DiffPathNodeLeaf right, std::string leftRootName, std::string rightRootName)
: kind(kind)
, left(left)
, right(right)
, leftRootName(leftRootName)
, rightRootName(rightRootName)
{
checkValidInitialization(left, right);
}
DiffError(Kind kind, DiffPath diffPath, DiffPathNodeLeaf left, DiffPathNodeLeaf right, std::string leftRootName, std::string rightRootName)
: kind(kind)
, diffPath(diffPath)
, left(left)
, right(right)
, leftRootName(leftRootName)
, rightRootName(rightRootName)
{
checkValidInitialization(left, right);
}
std::string toString(bool multiLine = false) const;
private:
std::string toStringALeaf(std::string rootName, const DiffPathNodeLeaf& leaf, const DiffPathNodeLeaf& otherLeaf, bool multiLine) const;
void checkValidInitialization(const DiffPathNodeLeaf& left, const DiffPathNodeLeaf& right);
void checkNonMissingPropertyLeavesHaveNulloptTableProperty() const;
};
struct DifferResult
{
std::optional<DiffError> diffError;
DifferResult() {}
DifferResult(DiffError diffError)
: diffError(diffError)
{
}
void wrapDiffPath(DiffPathNode node);
};
struct DifferEnvironment
{
TypeId rootLeft;
TypeId rootRight;
std::optional<std::string> externalSymbolLeft;
std::optional<std::string> externalSymbolRight;
DenseHashMap<TypeId, TypeId> genericMatchedPairs;
DenseHashMap<TypePackId, TypePackId> genericTpMatchedPairs;
DifferEnvironment(
TypeId rootLeft,
TypeId rootRight,
std::optional<std::string> externalSymbolLeft,
std::optional<std::string> externalSymbolRight
)
: rootLeft(rootLeft)
, rootRight(rootRight)
, externalSymbolLeft(externalSymbolLeft)
, externalSymbolRight(externalSymbolRight)
, genericMatchedPairs(nullptr)
, genericTpMatchedPairs(nullptr)
{
}
bool isProvenEqual(TypeId left, TypeId right) const;
bool isAssumedEqual(TypeId left, TypeId right) const;
void recordProvenEqual(TypeId left, TypeId right);
void pushVisiting(TypeId left, TypeId right);
void popVisiting();
std::vector<std::pair<TypeId, TypeId>>::const_reverse_iterator visitingBegin() const;
std::vector<std::pair<TypeId, TypeId>>::const_reverse_iterator visitingEnd() const;
std::string getDevFixFriendlyNameLeft() const;
std::string getDevFixFriendlyNameRight() const;
private:
// TODO: consider using DenseHashSet
std::unordered_set<std::pair<TypeId, TypeId>, TypeIdPairHash> provenEqual;
// Ancestors of current types
std::unordered_set<std::pair<TypeId, TypeId>, TypeIdPairHash> visiting;
std::vector<std::pair<TypeId, TypeId>> visitingStack;
};
DifferResult diff(TypeId ty1, TypeId ty2);
DifferResult diffWithSymbols(TypeId ty1, TypeId ty2, std::optional<std::string> symbol1, std::optional<std::string> symbol2);
/**
* True if ty is a "simple" type, i.e. cannot contain types.
* string, number, boolean are simple types.
* function and table are not simple types.
*/
bool isSimple(TypeId ty);
} // namespace Luau

View file

@ -1,3 +1,4 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/DenseHash.h"
@ -21,6 +22,7 @@ struct BasicDocumentation
{
std::string documentation;
std::string learnMoreLink;
std::string codeSample;
};
struct FunctionParameterDocumentation
@ -37,6 +39,7 @@ struct FunctionDocumentation
std::vector<FunctionParameterDocumentation> parameters;
std::vector<DocumentationSymbol> returns;
std::string learnMoreLink;
std::string codeSample;
};
struct OverloadedFunctionDocumentation
@ -52,6 +55,7 @@ struct TableDocumentation
std::string documentation;
Luau::DenseHashMap<std::string, DocumentationSymbol> keys;
std::string learnMoreLink;
std::string codeSample;
};
using DocumentationDatabase = Luau::DenseHashMap<DocumentationSymbol, Documentation>;

View file

@ -0,0 +1,50 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/TypeFwd.h"
#include "Luau/NotNull.h"
#include "Luau/DenseHash.h"
#include <memory>
#include <optional>
#include <vector>
namespace Luau
{
struct TypeArena;
}
// The EqSat stuff is pretty template heavy, so we go to some lengths to prevent
// the complexity from leaking outside its implementation sources.
namespace Luau::EqSatSimplification
{
struct Simplifier;
using SimplifierPtr = std::unique_ptr<Simplifier, void (*)(Simplifier*)>;
SimplifierPtr newSimplifier(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes);
} // namespace Luau::EqSatSimplification
namespace Luau
{
struct EqSatSimplificationResult
{
TypeId result;
// New type function applications that were created by the reduction phase.
// We return these so that the ConstraintSolver can know to try to reduce
// them.
std::vector<TypeId> newTypeFunctions;
};
using EqSatSimplification::newSimplifier; // NOLINT: clang-tidy thinks these are unused. It is incorrect.
using Luau::EqSatSimplification::Simplifier; // NOLINT
using Luau::EqSatSimplification::SimplifierPtr;
std::optional<EqSatSimplificationResult> eqSatSimplify(NotNull<Simplifier> simplifier, TypeId ty);
} // namespace Luau

View file

@ -0,0 +1,376 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/EGraph.h"
#include "Luau/Id.h"
#include "Luau/Language.h"
#include "Luau/Lexer.h" // For Allocator
#include "Luau/NotNull.h"
#include "Luau/TypeArena.h"
#include "Luau/TypeFwd.h"
namespace Luau
{
struct TypeFunction;
}
namespace Luau::EqSatSimplification
{
using StringId = uint32_t;
using Id = Luau::EqSat::Id;
LUAU_EQSAT_UNIT(TNil);
LUAU_EQSAT_UNIT(TBoolean);
LUAU_EQSAT_UNIT(TNumber);
LUAU_EQSAT_UNIT(TString);
LUAU_EQSAT_UNIT(TThread);
LUAU_EQSAT_UNIT(TTopFunction);
LUAU_EQSAT_UNIT(TTopTable);
LUAU_EQSAT_UNIT(TTopClass);
LUAU_EQSAT_UNIT(TBuffer);
// Used for any type that eqsat can't do anything interesting with.
LUAU_EQSAT_ATOM(TOpaque, TypeId);
LUAU_EQSAT_ATOM(SBoolean, bool);
LUAU_EQSAT_ATOM(SString, StringId);
LUAU_EQSAT_ATOM(TFunction, TypeId);
LUAU_EQSAT_ATOM(TImportedTable, TypeId);
LUAU_EQSAT_ATOM(TClass, TypeId);
LUAU_EQSAT_UNIT(TAny);
LUAU_EQSAT_UNIT(TError);
LUAU_EQSAT_UNIT(TUnknown);
LUAU_EQSAT_UNIT(TNever);
LUAU_EQSAT_NODE_SET(Union);
LUAU_EQSAT_NODE_SET(Intersection);
LUAU_EQSAT_NODE_ARRAY(Negation, 1);
LUAU_EQSAT_NODE_ATOM_WITH_VECTOR(TTypeFun, std::shared_ptr<const TypeFunctionInstanceType>);
LUAU_EQSAT_UNIT(TNoRefine);
LUAU_EQSAT_UNIT(Invalid);
// enodes are immutable, but types are cyclic. We need a way to tie the knot.
// We handle this by generating TBound nodes at points where we encounter cycles.
// Each TBound has an ordinal that we later map onto the type.
// We use a substitution rule to replace all TBound nodes with their referrent.
LUAU_EQSAT_ATOM(TBound, size_t);
// Tables are sufficiently unlike other enodes that the Language.h macros won't cut it.
struct TTable
{
explicit TTable(Id basis);
TTable(Id basis, std::vector<StringId> propNames_, std::vector<Id> propTypes_);
// All TTables extend some other table. This may be TTopTable.
//
// It will frequently be a TImportedTable, in which case we can reuse things
// like source location and documentation info.
Id getBasis() const;
EqSat::Slice<const Id> propTypes() const;
// TODO: Also support read-only table props
// TODO: Indexer type, index result type.
std::vector<StringId> propNames;
// The enode interface
EqSat::Slice<Id> mutableOperands();
EqSat::Slice<const Id> operands() const;
bool operator==(const TTable& rhs) const;
bool operator!=(const TTable& rhs) const
{
return !(*this == rhs);
}
struct Hash
{
size_t operator()(const TTable& value) const;
};
private:
// The first element of this vector is the basis. Subsequent elements are
// property types. As we add other things like read-only properties and
// indexers, the structure of this array is likely to change.
//
// We encode our data in this way so that the operands() method can properly
// return a Slice<Id>.
std::vector<Id> storage;
};
template<typename L>
using Node = EqSat::Node<L>;
using EType = EqSat::Language<
TNil,
TBoolean,
TNumber,
TString,
TThread,
TTopFunction,
TTopTable,
TTopClass,
TBuffer,
TOpaque,
SBoolean,
SString,
TFunction,
TTable,
TImportedTable,
TClass,
TAny,
TError,
TUnknown,
TNever,
Union,
Intersection,
Negation,
TTypeFun,
Invalid,
TNoRefine,
TBound>;
struct StringCache
{
Allocator allocator;
DenseHashMap<std::string_view, StringId> strings{{}};
std::vector<std::string_view> views;
StringId add(std::string_view s);
std::string_view asStringView(StringId id) const;
std::string asString(StringId id) const;
};
using EGraph = Luau::EqSat::EGraph<EType, struct Simplify>;
struct Simplify
{
using Data = bool;
template<typename T>
Data make(const EGraph&, const T&) const;
void join(Data& left, const Data& right) const;
};
struct Subst
{
Id eclass;
Id newClass;
// The node into eclass which is boring, if any
std::optional<size_t> boringIndex;
std::string desc;
Subst(Id eclass, Id newClass, std::string desc = "");
};
struct Simplifier
{
NotNull<TypeArena> arena;
NotNull<BuiltinTypes> builtinTypes;
EGraph egraph;
StringCache stringCache;
// enodes are immutable but types can be cyclic, so we need some way to
// encode the cycle. This map is used to connect TBound nodes to the right
// eclass.
//
// The cyclicIntersection rewrite rule uses this to sense when a cycle can
// be deleted from an intersection or union.
std::unordered_map<size_t, Id> mappingIdToClass;
std::vector<Subst> substs;
using RewriteRuleFn = void (Simplifier::*)(Id id);
Simplifier(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes);
// Utilities
const EqSat::EClass<EType, Simplify::Data>& get(Id id) const;
Id find(Id id) const;
Id add(EType enode);
template<typename Tag>
const Tag* isTag(Id id) const;
template<typename Tag>
const Tag* isTag(const EType& enode) const;
void subst(Id from, Id to);
void subst(Id from, Id to, const std::string& ruleName);
void subst(Id from, Id to, const std::string& ruleName, const std::unordered_map<Id, size_t>& forceNodes);
void subst(Id from, size_t boringIndex, Id to, const std::string& ruleName, const std::unordered_map<Id, size_t>& forceNodes);
void unionClasses(std::vector<Id>& hereParts, Id there);
// Rewrite rules
void simplifyUnion(Id id);
void uninhabitedIntersection(Id id);
void intersectWithNegatedClass(Id id);
void intersectWithNegatedAtom(Id id);
void intersectWithNoRefine(Id id);
void cyclicIntersectionOfUnion(Id id);
void cyclicUnionOfIntersection(Id id);
void expandNegation(Id id);
void intersectionOfUnion(Id id);
void intersectTableProperty(Id id);
void uninhabitedTable(Id id);
void unneededTableModification(Id id);
void builtinTypeFunctions(Id id);
void iffyTypeFunctions(Id id);
void strictMetamethods(Id id);
};
template<typename Tag>
struct QueryIterator
{
QueryIterator();
QueryIterator(EGraph* egraph, Id eclass);
bool operator==(const QueryIterator& other) const;
bool operator!=(const QueryIterator& other) const;
std::pair<const Tag*, size_t> operator*() const;
QueryIterator& operator++();
QueryIterator& operator++(int);
private:
EGraph* egraph = nullptr;
Id eclass;
size_t index = 0;
};
template<typename Tag>
struct Query
{
EGraph* egraph;
Id eclass;
Query(EGraph* egraph, Id eclass)
: egraph(egraph)
, eclass(eclass)
{
}
QueryIterator<Tag> begin()
{
return QueryIterator<Tag>{egraph, eclass};
}
QueryIterator<Tag> end()
{
return QueryIterator<Tag>{};
}
};
template<typename Tag>
QueryIterator<Tag>::QueryIterator()
: egraph(nullptr)
, eclass(Id{0})
, index(0)
{
}
template<typename Tag>
QueryIterator<Tag>::QueryIterator(EGraph* egraph_, Id eclass)
: egraph(egraph_)
, eclass(eclass)
, index(0)
{
const auto& ecl = (*egraph)[eclass];
static constexpr const int idx = EType::VariantTy::getTypeId<Tag>();
for (const auto& enode : ecl.nodes)
{
if (enode.node.index() < idx)
++index;
else
break;
}
if (index >= ecl.nodes.size() || ecl.nodes[index].node.index() != idx)
{
egraph = nullptr;
index = 0;
}
}
template<typename Tag>
bool QueryIterator<Tag>::operator==(const QueryIterator<Tag>& rhs) const
{
if (egraph == nullptr && rhs.egraph == nullptr)
return true;
return egraph == rhs.egraph && eclass == rhs.eclass && index == rhs.index;
}
template<typename Tag>
bool QueryIterator<Tag>::operator!=(const QueryIterator<Tag>& rhs) const
{
return !(*this == rhs);
}
template<typename Tag>
std::pair<const Tag*, size_t> QueryIterator<Tag>::operator*() const
{
LUAU_ASSERT(egraph != nullptr);
EGraph::EClassT& ecl = (*egraph)[eclass];
LUAU_ASSERT(index < ecl.nodes.size());
auto& enode = ecl.nodes[index].node;
Tag* result = enode.template get<Tag>();
LUAU_ASSERT(result);
return {result, index};
}
// pre-increment
template<typename Tag>
QueryIterator<Tag>& QueryIterator<Tag>::operator++()
{
const auto& ecl = (*egraph)[eclass];
do
{
++index;
if (index >= ecl.nodes.size() || ecl.nodes[index].node.index() != EType::VariantTy::getTypeId<Tag>())
{
egraph = nullptr;
index = 0;
break;
}
} while (ecl.nodes[index].boring);
return *this;
}
// post-increment
template<typename Tag>
QueryIterator<Tag>& QueryIterator<Tag>::operator++(int)
{
QueryIterator<Tag> res = *this;
++res;
return res;
}
} // namespace Luau::EqSatSimplification

View file

@ -1,24 +1,41 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/FileResolver.h"
#include "Luau/Location.h"
#include "Luau/TypeVar.h"
#include "Luau/NotNull.h"
#include "Luau/Type.h"
#include "Luau/Variant.h"
#include "Luau/Ast.h"
#include <set>
namespace Luau
{
struct FileResolver;
struct TypeArena;
struct TypeError;
struct TypeMismatch
{
enum Context
{
CovariantContext,
InvariantContext
};
TypeMismatch() = default;
TypeMismatch(TypeId wantedType, TypeId givenType);
TypeMismatch(TypeId wantedType, TypeId givenType, std::string reason);
TypeMismatch(TypeId wantedType, TypeId givenType, std::string reason, TypeError error);
TypeMismatch(TypeId wantedType, TypeId givenType, std::string reason, std::optional<TypeError> error);
TypeMismatch(TypeId wantedType, TypeId givenType, Context context);
TypeMismatch(TypeId wantedType, TypeId givenType, std::string reason, Context context);
TypeMismatch(TypeId wantedType, TypeId givenType, std::string reason, std::optional<TypeError> error, Context context);
TypeId wantedType = nullptr;
TypeId givenType = nullptr;
Context context = CovariantContext;
std::string reason;
std::shared_ptr<TypeError> error;
@ -32,7 +49,6 @@ struct UnknownSymbol
{
Binding,
Type,
Generic
};
Name name;
Context context;
@ -80,7 +96,7 @@ struct OnlyTablesCanHaveMethods
struct DuplicateTypeDefinition
{
Name name;
Location previousLocation;
std::optional<Location> previousLocation;
bool operator==(const DuplicateTypeDefinition& rhs) const;
};
@ -90,12 +106,16 @@ struct CountMismatch
enum Context
{
Arg,
Result,
FunctionResult,
ExprListResult,
Return,
};
size_t expected;
std::optional<size_t> maximum;
size_t actual;
Context context = Arg;
bool isVariadic = false;
std::string function;
bool operator==(const CountMismatch& rhs) const;
};
@ -107,8 +127,6 @@ struct FunctionDoesNotTakeSelf
struct FunctionRequiresSelf
{
int requiredExtraNils = 0;
bool operator==(const FunctionRequiresSelf& rhs) const;
};
@ -169,6 +187,18 @@ struct GenericError
bool operator==(const GenericError& rhs) const;
};
struct InternalError
{
std::string message;
bool operator==(const InternalError& rhs) const;
};
struct ConstraintSolvingIncompleteError
{
bool operator==(const ConstraintSolvingIncompleteError& rhs) const;
};
struct CannotCallNonFunction
{
TypeId ty;
@ -277,11 +307,226 @@ struct MissingUnionProperty
bool operator==(const MissingUnionProperty& rhs) const;
};
using TypeErrorData = Variant<TypeMismatch, UnknownSymbol, UnknownProperty, NotATable, CannotExtendTable, OnlyTablesCanHaveMethods,
DuplicateTypeDefinition, CountMismatch, FunctionDoesNotTakeSelf, FunctionRequiresSelf, OccursCheckFailed, UnknownRequire,
IncorrectGenericParameterCount, SyntaxError, CodeTooComplex, UnificationTooComplex, UnknownPropButFoundLikeProp, GenericError,
CannotCallNonFunction, ExtraInformation, DeprecatedApiUsed, ModuleHasCyclicDependency, IllegalRequire, FunctionExitsWithoutReturning,
DuplicateGenericParameter, CannotInferBinaryOperation, MissingProperties, SwappedGenericTypeParameter, OptionalValueAccess, MissingUnionProperty>;
struct TypesAreUnrelated
{
TypeId left;
TypeId right;
bool operator==(const TypesAreUnrelated& rhs) const;
};
struct NormalizationTooComplex
{
bool operator==(const NormalizationTooComplex&) const
{
return true;
}
};
struct TypePackMismatch
{
TypePackId wantedTp;
TypePackId givenTp;
std::string reason;
bool operator==(const TypePackMismatch& rhs) const;
};
struct DynamicPropertyLookupOnExternTypesUnsafe
{
TypeId ty;
bool operator==(const DynamicPropertyLookupOnExternTypesUnsafe& rhs) const;
};
struct UninhabitedTypeFunction
{
TypeId ty;
bool operator==(const UninhabitedTypeFunction& rhs) const;
};
struct ExplicitFunctionAnnotationRecommended
{
std::vector<std::pair<std::string, TypeId>> recommendedArgs;
TypeId recommendedReturn;
bool operator==(const ExplicitFunctionAnnotationRecommended& rhs) const;
};
struct UninhabitedTypePackFunction
{
TypePackId tp;
bool operator==(const UninhabitedTypePackFunction& rhs) const;
};
struct WhereClauseNeeded
{
TypeId ty;
bool operator==(const WhereClauseNeeded& rhs) const;
};
struct PackWhereClauseNeeded
{
TypePackId tp;
bool operator==(const PackWhereClauseNeeded& rhs) const;
};
struct CheckedFunctionCallError
{
TypeId expected;
TypeId passed;
std::string checkedFunctionName;
// TODO: make this a vector<argumentIndices>
size_t argumentIndex;
bool operator==(const CheckedFunctionCallError& rhs) const;
};
struct NonStrictFunctionDefinitionError
{
std::string functionName;
std::string argument;
TypeId argumentType;
bool operator==(const NonStrictFunctionDefinitionError& rhs) const;
};
struct PropertyAccessViolation
{
TypeId table;
Name key;
enum
{
CannotRead,
CannotWrite
} context;
bool operator==(const PropertyAccessViolation& rhs) const;
};
struct CheckedFunctionIncorrectArgs
{
std::string functionName;
size_t expected;
size_t actual;
bool operator==(const CheckedFunctionIncorrectArgs& rhs) const;
};
struct CannotAssignToNever
{
// type of the rvalue being assigned
TypeId rhsType;
// Originating type.
std::vector<TypeId> cause;
enum class Reason
{
// when assigning to a property in a union of tables, the properties type
// is narrowed to the intersection of its type in each variant.
PropertyNarrowed,
};
Reason reason;
bool operator==(const CannotAssignToNever& rhs) const;
};
struct UnexpectedTypeInSubtyping
{
TypeId ty;
bool operator==(const UnexpectedTypeInSubtyping& rhs) const;
};
struct UnexpectedTypePackInSubtyping
{
TypePackId tp;
bool operator==(const UnexpectedTypePackInSubtyping& rhs) const;
};
struct UserDefinedTypeFunctionError
{
std::string message;
bool operator==(const UserDefinedTypeFunctionError& rhs) const;
};
struct ReservedIdentifier
{
std::string name;
bool operator==(const ReservedIdentifier& rhs) const;
};
using TypeErrorData = Variant<
TypeMismatch,
UnknownSymbol,
UnknownProperty,
NotATable,
CannotExtendTable,
OnlyTablesCanHaveMethods,
DuplicateTypeDefinition,
CountMismatch,
FunctionDoesNotTakeSelf,
FunctionRequiresSelf,
OccursCheckFailed,
UnknownRequire,
IncorrectGenericParameterCount,
SyntaxError,
CodeTooComplex,
UnificationTooComplex,
UnknownPropButFoundLikeProp,
GenericError,
InternalError,
ConstraintSolvingIncompleteError,
CannotCallNonFunction,
ExtraInformation,
DeprecatedApiUsed,
ModuleHasCyclicDependency,
IllegalRequire,
FunctionExitsWithoutReturning,
DuplicateGenericParameter,
CannotAssignToNever,
CannotInferBinaryOperation,
MissingProperties,
SwappedGenericTypeParameter,
OptionalValueAccess,
MissingUnionProperty,
TypesAreUnrelated,
NormalizationTooComplex,
TypePackMismatch,
DynamicPropertyLookupOnExternTypesUnsafe,
UninhabitedTypeFunction,
UninhabitedTypePackFunction,
WhereClauseNeeded,
PackWhereClauseNeeded,
CheckedFunctionCallError,
NonStrictFunctionDefinitionError,
PropertyAccessViolation,
CheckedFunctionIncorrectArgs,
UnexpectedTypeInSubtyping,
UnexpectedTypePackInSubtyping,
ExplicitFunctionAnnotationRecommended,
UserDefinedTypeFunctionError,
ReservedIdentifier>;
struct TypeErrorSummary
{
Location location;
ModuleName moduleName;
int code;
TypeErrorSummary(const Location& location, const ModuleName& moduleName, int code)
: location(location)
, moduleName(moduleName)
, code(code)
{
}
};
struct TypeError
{
@ -289,6 +534,7 @@ struct TypeError
ModuleName moduleName;
TypeErrorData data;
static int minCode();
int code() const;
TypeError() = default;
@ -306,6 +552,8 @@ struct TypeError
}
bool operator==(const TypeError& rhs) const;
TypeErrorSummary summary() const;
};
template<typename T>
@ -322,12 +570,18 @@ T* get(TypeError& e)
using ErrorVec = std::vector<TypeError>;
struct TypeErrorToStringOptions
{
FileResolver* fileResolver = nullptr;
};
std::string toString(const TypeError& error);
std::string toString(const TypeError& error, TypeErrorToStringOptions options);
bool containsParseErrorName(const TypeError& error);
// Copy any types named in the error into destArena.
void copyErrors(ErrorVec& errors, struct TypeArena& destArena);
void copyErrors(ErrorVec& errors, struct TypeArena& destArena, NotNull<BuiltinTypes> builtinTypes);
// Internal Compiler Error
struct InternalErrorReporter
@ -335,8 +589,33 @@ struct InternalErrorReporter
std::function<void(const char*)> onInternalError;
std::string moduleName;
[[noreturn]] void ice(const std::string& message, const Location& location);
[[noreturn]] void ice(const std::string& message);
[[noreturn]] void ice(const std::string& message, const Location& location) const;
[[noreturn]] void ice(const std::string& message) const;
};
class InternalCompilerError : public std::exception
{
public:
explicit InternalCompilerError(const std::string& message)
: message(message)
{
}
explicit InternalCompilerError(const std::string& message, const std::string& moduleName)
: message(message)
, moduleName(moduleName)
{
}
explicit InternalCompilerError(const std::string& message, const std::string& moduleName, const Location& location)
: message(message)
, moduleName(moduleName)
, location(location)
{
}
virtual const char* what() const throw();
const std::string message;
const std::optional<std::string> moduleName;
const std::optional<Location> location;
};
} // namespace Luau

View file

@ -1,8 +1,10 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include <string>
#include <memory>
#include <optional>
#include <string>
#include <vector>
namespace Luau
{
@ -18,7 +20,7 @@ struct SourceCode
None,
Module,
Script,
Local
Local_DEPRECATED
};
std::string source;
@ -31,8 +33,71 @@ struct ModuleInfo
bool optional = false;
};
struct RequireAlias
{
std::string alias; // Unprefixed alias name (no leading `@`).
std::vector<std::string> tags = {};
};
struct RequireNode
{
virtual ~RequireNode() {}
// Get the path component representing this node.
virtual std::string getPathComponent() const = 0;
// Get the displayed user-facing label for this node, defaults to getPathComponent()
virtual std::string getLabel() const
{
return getPathComponent();
}
// Get tags to attach to this node's RequireSuggestion (defaults to none).
virtual std::vector<std::string> getTags() const
{
return {};
}
// TODO: resolvePathToNode() can ultimately be replaced with a call into
// require-by-string's path resolution algorithm. This will first require
// generalizing that algorithm to work with a virtual file system.
virtual std::unique_ptr<RequireNode> resolvePathToNode(const std::string& path) const = 0;
// Get children of this node, if any (if this node represents a directory).
virtual std::vector<std::unique_ptr<RequireNode>> getChildren() const = 0;
// A list of the aliases available to this node.
virtual std::vector<RequireAlias> getAvailableAliases() const = 0;
};
struct RequireSuggestion
{
std::string label;
std::string fullPath;
std::vector<std::string> tags;
};
using RequireSuggestions = std::vector<RequireSuggestion>;
struct RequireSuggester
{
virtual ~RequireSuggester() {}
std::optional<RequireSuggestions> getRequireSuggestions(const ModuleName& requirer, const std::optional<std::string>& pathString) const;
protected:
virtual std::unique_ptr<RequireNode> getNode(const ModuleName& name) const = 0;
private:
std::optional<RequireSuggestions> getRequireSuggestionsImpl(const ModuleName& requirer, const std::optional<std::string>& path) const;
};
struct FileResolver
{
FileResolver() = default;
FileResolver(std::shared_ptr<RequireSuggester> requireSuggester)
: requireSuggester(std::move(requireSuggester))
{
}
virtual ~FileResolver() {}
virtual std::optional<SourceCode> readSource(const ModuleName& name) = 0;
@ -52,12 +117,9 @@ struct FileResolver
return std::nullopt;
}
// DEPRECATED APIS
// These are going to be removed with LuauNewRequireTrace2
virtual bool moduleExists(const ModuleName& name) const = 0;
virtual std::optional<ModuleName> fromAstFragment(AstExpr* expr) const = 0;
virtual ModuleName concat(const ModuleName& lhs, std::string_view rhs) const = 0;
virtual std::optional<ModuleName> getParentModuleName(const ModuleName& name) const = 0;
std::optional<RequireSuggestions> getRequireSuggestions(const ModuleName& requirer, const std::optional<std::string>& pathString) const;
std::shared_ptr<RequireSuggester> requireSuggester;
};
struct NullFileResolver : FileResolver
@ -66,22 +128,6 @@ struct NullFileResolver : FileResolver
{
return std::nullopt;
}
bool moduleExists(const ModuleName& name) const override
{
return false;
}
std::optional<ModuleName> fromAstFragment(AstExpr* expr) const override
{
return std::nullopt;
}
ModuleName concat(const ModuleName& lhs, std::string_view rhs) const override
{
return lhs;
}
std::optional<ModuleName> getParentModuleName(const ModuleName& name) const override
{
return std::nullopt;
}
};
} // namespace Luau

View file

@ -0,0 +1,196 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Ast.h"
#include "Luau/Parser.h"
#include "Luau/AutocompleteTypes.h"
#include "Luau/DenseHash.h"
#include "Luau/Module.h"
#include "Luau/Frontend.h"
#include <memory>
#include <vector>
namespace Luau
{
struct FrontendOptions;
enum class FragmentAutocompleteWaypoint
{
ParseFragmentEnd,
CloneModuleStart,
CloneModuleEnd,
DfgBuildEnd,
CloneAndSquashScopeStart,
CloneAndSquashScopeEnd,
ConstraintSolverStart,
ConstraintSolverEnd,
TypecheckFragmentEnd,
AutocompleteEnd,
COUNT,
};
class IFragmentAutocompleteReporter
{
public:
virtual void reportWaypoint(FragmentAutocompleteWaypoint) = 0;
virtual void reportFragmentString(std::string_view) = 0;
};
enum class FragmentTypeCheckStatus
{
SkipAutocomplete,
Success,
};
struct FragmentAutocompleteAncestryResult
{
DenseHashMap<AstName, AstLocal*> localMap{AstName()};
std::vector<AstLocal*> localStack;
std::vector<AstNode*> ancestry;
AstStat* nearestStatement = nullptr;
AstStatBlock* parentBlock = nullptr;
Location fragmentSelectionRegion;
};
struct FragmentParseResult
{
std::string fragmentToParse;
AstStatBlock* root = nullptr;
std::vector<AstNode*> ancestry;
AstStat* nearestStatement = nullptr;
std::vector<Comment> commentLocations;
std::unique_ptr<Allocator> alloc = std::make_unique<Allocator>();
Position scopePos{0, 0};
};
struct FragmentTypeCheckResult
{
ModulePtr incrementalModule = nullptr;
ScopePtr freshScope;
std::vector<AstNode*> ancestry;
};
struct FragmentAutocompleteResult
{
ModulePtr incrementalModule;
Scope* freshScope;
TypeArena arenaForAutocomplete_DEPRECATED;
AutocompleteResult acResults;
};
struct FragmentRegion
{
Location fragmentLocation;
AstStat* nearestStatement = nullptr; // used for tests
AstStatBlock* parentBlock = nullptr; // used for scope detection
};
std::optional<Position> blockDiffStart(AstStatBlock* blockOld, AstStatBlock* blockNew, AstStat* nearestStatementNewAst);
FragmentRegion getFragmentRegion(AstStatBlock* root, const Position& cursorPosition);
FragmentAutocompleteAncestryResult findAncestryForFragmentParse(AstStatBlock* stale, const Position& cursorPos, AstStatBlock* lastGoodParse);
FragmentAutocompleteAncestryResult findAncestryForFragmentParse_DEPRECATED(AstStatBlock* root, const Position& cursorPos);
std::optional<FragmentParseResult> parseFragment_DEPRECATED(
AstStatBlock* root,
AstNameTable* names,
std::string_view src,
const Position& cursorPos,
std::optional<Position> fragmentEndPosition
);
std::optional<FragmentParseResult> parseFragment(
AstStatBlock* stale,
AstStatBlock* mostRecentParse,
AstNameTable* names,
std::string_view src,
const Position& cursorPos,
std::optional<Position> fragmentEndPosition
);
std::pair<FragmentTypeCheckStatus, FragmentTypeCheckResult> typecheckFragment(
Frontend& frontend,
const ModuleName& moduleName,
const Position& cursorPos,
std::optional<FrontendOptions> opts,
std::string_view src,
std::optional<Position> fragmentEndPosition,
AstStatBlock* recentParse = nullptr,
IFragmentAutocompleteReporter* reporter = nullptr
);
FragmentAutocompleteResult fragmentAutocomplete(
Frontend& frontend,
std::string_view src,
const ModuleName& moduleName,
Position cursorPosition,
std::optional<FrontendOptions> opts,
StringCompletionCallback callback,
std::optional<Position> fragmentEndPosition = std::nullopt,
AstStatBlock* recentParse = nullptr,
IFragmentAutocompleteReporter* reporter = nullptr
);
enum class FragmentAutocompleteStatus
{
Success,
FragmentTypeCheckFail,
InternalIce
};
struct FragmentAutocompleteStatusResult
{
FragmentAutocompleteStatus status;
std::optional<FragmentAutocompleteResult> result;
};
struct FragmentContext
{
std::string_view newSrc;
const ParseResult& freshParse;
std::optional<FrontendOptions> opts;
std::optional<Position> DEPRECATED_fragmentEndPosition;
IFragmentAutocompleteReporter* reporter = nullptr;
};
/**
* @brief Attempts to compute autocomplete suggestions from the fragment context.
*
* This function computes autocomplete suggestions using outdated frontend typechecking data
* by patching the fragment context of the new script source content.
*
* @param frontend The Luau Frontend data structure, which may contain outdated typechecking data.
*
* @param moduleName The name of the target module, specifying which script the caller wants to request autocomplete for.
*
* @param cursorPosition The position in the script where the caller wants to trigger autocomplete.
*
* @param context The fragment context that this API will use to patch the outdated typechecking data.
*
* @param stringCompletionCB A callback function that provides autocomplete suggestions for string contexts.
*
* @return
* The status indicating whether `fragmentAutocomplete` ran successfully or failed, along with the reason for failure.
* Also includes autocomplete suggestions if the status is successful.
*
* @usage
* FragmentAutocompleteStatusResult acStatusResult;
* if (shouldFragmentAC)
* acStatusResult = Luau::tryFragmentAutocomplete(...);
*
* if (acStatusResult.status != Successful)
* {
* frontend.check(moduleName, options);
* acStatusResult.acResult = Luau::autocomplete(...);
* }
* return convertResultWithContext(acStatusResult.acResult);
*/
FragmentAutocompleteStatusResult tryFragmentAutocomplete(
Frontend& frontend,
const ModuleName& moduleName,
Position cursorPosition,
FragmentContext context,
StringCompletionCallback stringCompletionCB
);
} // namespace Luau

View file

@ -2,12 +2,16 @@
#pragma once
#include "Luau/Config.h"
#include "Luau/GlobalTypes.h"
#include "Luau/Module.h"
#include "Luau/ModuleResolver.h"
#include "Luau/RequireTracer.h"
#include "Luau/TypeInfer.h"
#include "Luau/Scope.h"
#include "Luau/Set.h"
#include "Luau/TypeCheckLimits.h"
#include "Luau/Variant.h"
#include <mutex>
#include <string>
#include <vector>
#include <optional>
@ -20,44 +24,65 @@ class ParseError;
struct Frontend;
struct TypeError;
struct LintWarning;
struct GlobalTypes;
struct TypeChecker;
struct FileResolver;
struct ModuleResolver;
struct ParseResult;
struct HotComment;
struct BuildQueueItem;
struct BuildQueueWorkState;
struct FrontendCancellationToken;
struct LoadDefinitionFileResult
{
bool success;
ParseResult parseResult;
SourceModule sourceModule;
ModulePtr module;
};
LoadDefinitionFileResult loadDefinitionFile(
TypeChecker& typeChecker, ScopePtr targetScope, std::string_view definition, const std::string& packageName);
std::optional<Mode> parseMode(const std::vector<std::string>& hotcomments);
std::vector<std::string_view> parsePathExpr(const AstExpr& pathExpr);
// Exported only for convenient testing.
std::optional<ModuleName> pathExprToModuleName(const ModuleName& currentModuleName, const std::vector<std::string_view>& expr);
/** Try to convert an AST fragment into a ModuleName.
* Returns std::nullopt if the expression cannot be resolved. This will most likely happen in cases where
* the import path involves some dynamic computation that we cannot see into at typechecking time.
*
* Unintuitively, weirdly-formulated modules (like game.Parent.Parent.Parent.Foo) will successfully produce a ModuleName
* as long as it falls within the permitted syntax. This is ok because we will fail to find the module and produce an
* error when we try during typechecking.
*/
std::optional<ModuleName> pathExprToModuleName(const ModuleName& currentModuleName, const AstExpr& expr);
std::optional<Mode> parseMode(const std::vector<HotComment>& hotcomments);
struct SourceNode
{
bool hasDirtySourceModule() const
{
return dirtySourceModule;
}
bool hasDirtyModule(bool forAutocomplete) const
{
return forAutocomplete ? dirtyModuleForAutocomplete : dirtyModule;
}
bool hasInvalidModuleDependency(bool forAutocomplete) const
{
return forAutocomplete ? invalidModuleDependencyForAutocomplete : invalidModuleDependency;
}
void setInvalidModuleDependency(bool value, bool forAutocomplete)
{
if (forAutocomplete)
invalidModuleDependencyForAutocomplete = value;
else
invalidModuleDependency = value;
}
ModuleName name;
std::unordered_set<ModuleName> requires;
std::string humanReadableName;
DenseHashSet<ModuleName> requireSet{{}};
std::vector<std::pair<ModuleName, Location>> requireLocations;
bool dirty = true;
Set<ModuleName> dependents{{}};
bool dirtySourceModule = true;
bool dirtyModule = true;
bool dirtyModuleForAutocomplete = true;
bool invalidModuleDependency = true;
bool invalidModuleDependencyForAutocomplete = true;
double autocompleteLimitsMult = 1.0;
};
struct FrontendOptions
@ -68,14 +93,38 @@ struct FrontendOptions
// is complete.
bool retainFullTypeGraphs = false;
// When true, we run typechecking twice, one in the regular mode, ond once in strict mode
// in order to get more precise type information (e.g. for autocomplete).
bool typecheckTwice = false;
// Run typechecking only in mode required for autocomplete (strict mode in
// order to get more precise type information)
bool forAutocomplete = false;
bool runLintChecks = false;
// If not empty, randomly shuffle the constraint set before attempting to
// solve. Use this value to seed the random number generator.
std::optional<unsigned> randomizeConstraintResolutionSeed;
std::optional<LintOptions> enabledLintWarnings;
std::shared_ptr<FrontendCancellationToken> cancellationToken;
// Time limit for typechecking a single module
std::optional<double> moduleTimeLimitSec;
// When true, some internal complexity limits will be scaled down for modules that miss the limit set by moduleTimeLimitSec
bool applyInternalLimitScaling = false;
// An optional callback which is called for every *dirty* module was checked
// Is multi-threaded typechecking is used, this callback might be called from multiple threads and has to be thread-safe
std::function<void(const SourceModule& sourceModule, const Luau::Module& module)> customModuleCheck;
};
struct CheckResult
{
std::vector<TypeError> errors;
LintResult lintResult;
std::vector<ModuleName> timeoutHits;
};
struct FrontendModuleResolver : ModuleResolver
@ -87,7 +136,13 @@ struct FrontendModuleResolver : ModuleResolver
std::optional<ModuleInfo> resolveModuleInfo(const ModuleName& currentModuleName, const AstExpr& pathExpr) override;
std::string getHumanReadableModuleName(const ModuleName& moduleName) const override;
bool setModule(const ModuleName& moduleName, ModulePtr module);
void clearModules();
private:
Frontend* frontend;
mutable std::mutex moduleMutex;
std::unordered_map<ModuleName, ModulePtr> modules;
};
@ -109,22 +164,19 @@ struct Frontend
Frontend(FileResolver* fileResolver, ConfigResolver* configResolver, const FrontendOptions& options = {});
CheckResult check(const ModuleName& name); // new shininess
LintResult lint(const ModuleName& name, std::optional<Luau::LintOptions> enabledLintWarnings = {});
// Parse module graph and prepare SourceNode/SourceModule data, including required dependencies without running typechecking
void parse(const ModuleName& name);
/** Lint some code that has no associated DataModel object
*
* Since this source fragment has no name, we cannot cache its AST. Instead,
* we return it to the caller to use as they wish.
*/
std::pair<SourceModule, LintResult> lintFragment(std::string_view source, std::optional<Luau::LintOptions> enabledLintWarnings = {});
// Parse and typecheck module graph
CheckResult check(const ModuleName& name, std::optional<FrontendOptions> optionOverride = {}); // new shininess
CheckResult check(const SourceModule& module); // OLD. TODO KILL
LintResult lint(const SourceModule& module, std::optional<Luau::LintOptions> enabledLintWarnings = {});
bool allModuleDependenciesValid(const ModuleName& name, bool forAutocomplete = false) const;
bool isDirty(const ModuleName& name) const;
bool isDirty(const ModuleName& name, bool forAutocomplete = false) const;
void markDirty(const ModuleName& name, std::vector<ModuleName>* markedDirty = nullptr);
void traverseDependents(const ModuleName& name, std::function<bool(SourceNode&)> processSubtree);
/** Borrow a pointer into the SourceModule cache.
*
* Returns nullptr if we don't have it. This could mean that the script
@ -140,40 +192,133 @@ struct Frontend
void clear();
ScopePtr addEnvironment(const std::string& environmentName);
ScopePtr getEnvironmentScope(const std::string& environmentName);
ScopePtr getEnvironmentScope(const std::string& environmentName) const;
void registerBuiltinDefinition(const std::string& name, std::function<void(TypeChecker&, ScopePtr)>);
void registerBuiltinDefinition(const std::string& name, std::function<void(Frontend&, GlobalTypes&, ScopePtr)>);
void applyBuiltinDefinitionToEnvironment(const std::string& environmentName, const std::string& definitionName);
LoadDefinitionFileResult loadDefinitionFile(
GlobalTypes& globals,
ScopePtr targetScope,
std::string_view source,
const std::string& packageName,
bool captureComments,
bool typeCheckForAutocomplete = false
);
// Batch module checking. Queue modules and check them together, retrieve results with 'getCheckResult'
// If provided, 'executeTask' function is allowed to call the 'task' function on any thread and return without waiting for 'task' to complete
void queueModuleCheck(const std::vector<ModuleName>& names);
void queueModuleCheck(const ModuleName& name);
std::vector<ModuleName> checkQueuedModules(
std::optional<FrontendOptions> optionOverride = {},
std::function<void(std::function<void()> task)> executeTask = {},
std::function<bool(size_t done, size_t total)> progress = {}
);
std::optional<CheckResult> getCheckResult(const ModuleName& name, bool accumulateNested, bool forAutocomplete = false);
std::vector<ModuleName> getRequiredScripts(const ModuleName& name);
private:
std::pair<SourceNode*, SourceModule*> getSourceNode(CheckResult& checkResult, const ModuleName& name);
ModulePtr check(
const SourceModule& sourceModule,
Mode mode,
std::vector<RequireCycle> requireCycles,
std::optional<ScopePtr> environmentScope,
bool forAutocomplete,
bool recordJsonLog,
TypeCheckLimits typeCheckLimits
);
std::pair<SourceNode*, SourceModule*> getSourceNode(const ModuleName& name);
SourceModule parse(const ModuleName& name, std::string_view src, const ParseOptions& parseOptions);
bool parseGraph(std::vector<ModuleName>& buildQueue, CheckResult& checkResult, const ModuleName& root);
bool parseGraph(
std::vector<ModuleName>& buildQueue,
const ModuleName& root,
bool forAutocomplete,
std::function<bool(const ModuleName&)> canSkip = {}
);
void addBuildQueueItems(
std::vector<BuildQueueItem>& items,
std::vector<ModuleName>& buildQueue,
bool cycleDetected,
DenseHashSet<Luau::ModuleName>& seen,
const FrontendOptions& frontendOptions
);
void checkBuildQueueItem(BuildQueueItem& item);
void checkBuildQueueItems(std::vector<BuildQueueItem>& items);
void recordItemResult(const BuildQueueItem& item);
void performQueueItemTask(std::shared_ptr<BuildQueueWorkState> state, size_t itemPos);
void sendQueueItemTask(std::shared_ptr<BuildQueueWorkState> state, size_t itemPos);
void sendQueueCycleItemTask(std::shared_ptr<BuildQueueWorkState> state);
static LintResult classifyLints(const std::vector<LintWarning>& warnings, const Config& config);
ScopePtr getModuleEnvironment(const SourceModule& module, const Config& config);
ScopePtr getModuleEnvironment(const SourceModule& module, const Config& config, bool forAutocomplete) const;
std::unordered_map<std::string, ScopePtr> environments;
std::unordered_map<std::string, std::function<void(TypeChecker&, ScopePtr)>> builtinDefinitions;
std::unordered_map<std::string, std::function<void(Frontend&, GlobalTypes&, ScopePtr)>> builtinDefinitions;
BuiltinTypes builtinTypes_;
public:
const NotNull<BuiltinTypes> builtinTypes;
FileResolver* fileResolver;
FrontendModuleResolver moduleResolver;
FrontendModuleResolver moduleResolverForAutocomplete;
TypeChecker typeChecker;
TypeChecker typeCheckerForAutocomplete;
GlobalTypes globals;
GlobalTypes globalsForAutocomplete;
ConfigResolver* configResolver;
FrontendOptions options;
InternalErrorReporter iceHandler;
TypeArena arenaForAutocomplete;
std::function<void(const ModuleName& name, const ScopePtr& scope, bool forAutocomplete)> prepareModuleScope;
std::function<void(const ModuleName& name, std::string log)> writeJsonLog = {};
std::unordered_map<ModuleName, SourceNode> sourceNodes;
std::unordered_map<ModuleName, SourceModule> sourceModules;
std::unordered_map<ModuleName, RequireTraceResult> requires;
std::unordered_map<ModuleName, std::shared_ptr<SourceNode>> sourceNodes;
std::unordered_map<ModuleName, std::shared_ptr<SourceModule>> sourceModules;
std::unordered_map<ModuleName, RequireTraceResult> requireTrace;
Stats stats = {};
std::vector<ModuleName> moduleQueue;
};
ModulePtr check(
const SourceModule& sourceModule,
Mode mode,
const std::vector<RequireCycle>& requireCycles,
NotNull<BuiltinTypes> builtinTypes,
NotNull<InternalErrorReporter> iceHandler,
NotNull<ModuleResolver> moduleResolver,
NotNull<FileResolver> fileResolver,
const ScopePtr& globalScope,
const ScopePtr& typeFunctionScope,
std::function<void(const ModuleName&, const ScopePtr&)> prepareModuleScope,
FrontendOptions options,
TypeCheckLimits limits
);
ModulePtr check(
const SourceModule& sourceModule,
Mode mode,
const std::vector<RequireCycle>& requireCycles,
NotNull<BuiltinTypes> builtinTypes,
NotNull<InternalErrorReporter> iceHandler,
NotNull<ModuleResolver> moduleResolver,
NotNull<FileResolver> fileResolver,
const ScopePtr& globalScope,
const ScopePtr& typeFunctionScope,
std::function<void(const ModuleName&, const ScopePtr&)> prepareModuleScope,
FrontendOptions options,
TypeCheckLimits limits,
bool recordJsonLog,
std::function<void(const ModuleName&, std::string)> writeJsonLog
);
} // namespace Luau

View file

@ -0,0 +1,82 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Scope.h"
#include "Luau/NotNull.h"
#include "Luau/TypeFwd.h"
namespace Luau
{
template<typename TID>
struct GeneralizationParams
{
bool foundOutsideFunctions = false;
size_t useCount = 0;
Polarity polarity = Polarity::None;
};
template<typename TID>
struct GeneralizationResult
{
std::optional<TID> result;
// True if the provided type was replaced with a generic.
bool wasReplacedByGeneric = false;
bool resourceLimitsExceeded = false;
explicit operator bool() const
{
return bool(result);
}
};
// Replace a single free type by its bounds according to the polarity provided.
GeneralizationResult<TypeId> generalizeType(
NotNull<TypeArena> arena,
NotNull<BuiltinTypes> builtinTypes,
NotNull<Scope> scope,
TypeId freeTy,
const GeneralizationParams<TypeId>& params
);
// Generalize one type pack
GeneralizationResult<TypePackId> generalizeTypePack(
NotNull<TypeArena> arena,
NotNull<BuiltinTypes> builtinTypes,
NotNull<Scope> scope,
TypePackId tp,
const GeneralizationParams<TypePackId>& params
);
void sealTable(NotNull<Scope> scope, TypeId ty);
/** Attempt to generalize a type.
*
* If generalizationTarget is set, then only that type will be replaced by its
* bounds. The way this is intended to be used is that ty is some function that
* is not fully generalized, and generalizationTarget is a type within its
* signature. There should be no further constraints that could affect the
* bounds of generalizationTarget.
*
* Returns nullopt if generalization failed due to resources limits.
*/
std::optional<TypeId> generalize(
NotNull<TypeArena> arena,
NotNull<BuiltinTypes> builtinTypes,
NotNull<Scope> scope,
NotNull<DenseHashSet<TypeId>> cachedTypes,
TypeId ty,
std::optional<TypeId> generalizationTarget = {}
);
void pruneUnnecessaryGenerics(
NotNull<TypeArena> arena,
NotNull<BuiltinTypes> builtinTypes,
NotNull<Scope> scope,
NotNull<DenseHashSet<TypeId>> cachedTypes,
TypeId ty
);
} // namespace Luau

View file

@ -0,0 +1,27 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Module.h"
#include "Luau/NotNull.h"
#include "Luau/Scope.h"
#include "Luau/TypeArena.h"
#include "Luau/TypeFwd.h"
namespace Luau
{
struct GlobalTypes
{
explicit GlobalTypes(NotNull<BuiltinTypes> builtinTypes);
NotNull<BuiltinTypes> builtinTypes; // Global types are based on builtin types
TypeArena globalTypes;
SourceModule globalNames; // names for symbols entered into globalScope
ScopePtr globalScope; // shared by all modules
ScopePtr globalTypeFunctionScope; // shared by all modules
};
} // namespace Luau

View file

@ -0,0 +1,16 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/NotNull.h"
#include "Luau/TypeFwd.h"
namespace Luau
{
struct Scope;
struct TypeArena;
void inferGenericPolarities(NotNull<TypeArena> arena, NotNull<Scope> scope, TypeId ty);
void inferGenericPolarities(NotNull<TypeArena> arena, NotNull<Scope> scope, TypePackId tp);
} // namespace Luau

View file

@ -0,0 +1,147 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Common.h"
#include <unordered_map>
#include <vector>
#include <type_traits>
#include <iterator>
namespace Luau
{
template<typename K, typename V>
struct InsertionOrderedMap
{
static_assert(std::is_trivially_copyable_v<K>, "key must be trivially copyable");
private:
using vec = std::vector<std::pair<K, V>>;
public:
using iterator = typename vec::iterator;
using const_iterator = typename vec::const_iterator;
void insert(K k, V v)
{
if (indices.count(k) != 0)
return;
pairs.push_back(std::make_pair(k, std::move(v)));
indices[k] = pairs.size() - 1;
}
void clear()
{
pairs.clear();
indices.clear();
}
size_t size() const
{
LUAU_ASSERT(pairs.size() == indices.size());
return pairs.size();
}
bool contains(const K& k) const
{
return indices.count(k) > 0;
}
const V* get(const K& k) const
{
auto it = indices.find(k);
if (it == indices.end())
return nullptr;
else
return &pairs.at(it->second).second;
}
V* get(const K& k)
{
auto it = indices.find(k);
if (it == indices.end())
return nullptr;
else
return &pairs.at(it->second).second;
}
V& operator[](const K& k)
{
auto it = indices.find(k);
if (it == indices.end())
{
pairs.push_back(std::make_pair(k, V()));
indices[k] = pairs.size() - 1;
return pairs.back().second;
}
else
return pairs.at(it->second).second;
}
const_iterator begin() const
{
return pairs.begin();
}
const_iterator end() const
{
return pairs.end();
}
iterator begin()
{
return pairs.begin();
}
iterator end()
{
return pairs.end();
}
const_iterator find(K k) const
{
auto indicesIt = indices.find(k);
if (indicesIt == indices.end())
return end();
else
return begin() + indicesIt->second;
}
iterator find(K k)
{
auto indicesIt = indices.find(k);
if (indicesIt == indices.end())
return end();
else
return begin() + indicesIt->second;
}
void erase(iterator it)
{
if (it == pairs.end())
return;
K k = it->first;
auto indexIt = indices.find(k);
if (indexIt == indices.end())
return;
size_t removed = indexIt->second;
indices.erase(indexIt);
pairs.erase(it);
for (auto& [_, index] : indices)
{
if (index > removed)
--index;
}
}
private:
vec pairs;
std::unordered_map<K, size_t> indices;
};
} // namespace Luau

View file

@ -0,0 +1,165 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/NotNull.h"
#include "Luau/Substitution.h"
#include "Luau/TypeFwd.h"
#include "Luau/Unifiable.h"
#include "Luau/VisitType.h"
namespace Luau
{
struct TxnLog;
struct TypeArena;
struct TypeCheckLimits;
// A substitution which replaces generic types in a given set by free types.
struct ReplaceGenerics : Substitution
{
ReplaceGenerics(
const TxnLog* log,
TypeArena* arena,
NotNull<BuiltinTypes> builtinTypes,
TypeLevel level,
Scope* scope,
const std::vector<TypeId>& generics,
const std::vector<TypePackId>& genericPacks
)
: Substitution(log, arena)
, builtinTypes(builtinTypes)
, level(level)
, scope(scope)
, generics(generics)
, genericPacks(genericPacks)
{
}
void resetState(
const TxnLog* log,
TypeArena* arena,
NotNull<BuiltinTypes> builtinTypes,
TypeLevel level,
Scope* scope,
const std::vector<TypeId>& generics,
const std::vector<TypePackId>& genericPacks
);
NotNull<BuiltinTypes> builtinTypes;
TypeLevel level;
Scope* scope;
std::vector<TypeId> generics;
std::vector<TypePackId> genericPacks;
bool ignoreChildren(TypeId ty) override;
bool isDirty(TypeId ty) override;
bool isDirty(TypePackId tp) override;
TypeId clean(TypeId ty) override;
TypePackId clean(TypePackId tp) override;
};
// A substitution which replaces generic functions by monomorphic functions
struct Instantiation final : Substitution
{
Instantiation(const TxnLog* log, TypeArena* arena, NotNull<BuiltinTypes> builtinTypes, TypeLevel level, Scope* scope)
: Substitution(log, arena)
, builtinTypes(builtinTypes)
, level(level)
, scope(scope)
, reusableReplaceGenerics(log, arena, builtinTypes, level, scope, {}, {})
{
}
void resetState(const TxnLog* log, TypeArena* arena, NotNull<BuiltinTypes> builtinTypes, TypeLevel level, Scope* scope);
NotNull<BuiltinTypes> builtinTypes;
TypeLevel level;
Scope* scope;
ReplaceGenerics reusableReplaceGenerics;
bool ignoreChildren(TypeId ty) override;
bool isDirty(TypeId ty) override;
bool isDirty(TypePackId tp) override;
TypeId clean(TypeId ty) override;
TypePackId clean(TypePackId tp) override;
};
// Used to find if a FunctionType requires generic type cleanup during instantiation
struct GenericTypeFinder : TypeOnceVisitor
{
bool found = false;
bool visit(TypeId ty) override
{
return !found;
}
bool visit(TypePackId ty) override
{
return !found;
}
bool visit(TypeId ty, const Luau::FunctionType& ftv) override
{
if (ftv.hasNoFreeOrGenericTypes)
return false;
if (!ftv.generics.empty() || !ftv.genericPacks.empty())
found = true;
return !found;
}
bool visit(TypeId ty, const Luau::TableType& ttv) override
{
if (ttv.state == Luau::TableState::Generic)
found = true;
return !found;
}
bool visit(TypeId ty, const Luau::GenericType&) override
{
found = true;
return false;
}
bool visit(TypePackId ty, const Luau::GenericTypePack&) override
{
found = true;
return false;
}
bool visit(TypeId ty, const Luau::ExternType&) override
{
// During function instantiation, extern types are not traversed even if they have generics
return false;
}
};
/** Attempt to instantiate a type. Only used under local type inference.
*
* When given a generic function type, instantiate() will return a copy with the
* generics replaced by fresh types. Instantiation will return the same TypeId
* back if the function does not have any generics.
*
* All higher order generics are left as-is. For example, instantiation of
* <X>(<Y>(Y) -> (X, Y)) -> (X, Y) is (<Y>(Y) -> ('x, Y)) -> ('x, Y)
*
* We substitute the generic X for the free 'x, but leave the generic Y alone.
*
* Instantiation fails only when processing the type causes internal recursion
* limits to be exceeded.
*/
std::optional<TypeId> instantiate(
NotNull<BuiltinTypes> builtinTypes,
NotNull<TypeArena> arena,
NotNull<TypeCheckLimits> limits,
NotNull<Scope> scope,
TypeId ty
);
} // namespace Luau

View file

@ -0,0 +1,90 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/NotNull.h"
#include "Luau/Substitution.h"
#include "Luau/TxnLog.h"
#include "Luau/TypeFwd.h"
#include "Luau/Unifiable.h"
namespace Luau
{
struct TypeArena;
struct TypeCheckLimits;
struct Replacer : Substitution
{
DenseHashMap<TypeId, TypeId> replacements;
DenseHashMap<TypePackId, TypePackId> replacementPacks;
Replacer(NotNull<TypeArena> arena, DenseHashMap<TypeId, TypeId> replacements, DenseHashMap<TypePackId, TypePackId> replacementPacks)
: Substitution(TxnLog::empty(), arena)
, replacements(std::move(replacements))
, replacementPacks(std::move(replacementPacks))
{
}
bool isDirty(TypeId ty) override
{
return replacements.find(ty) != nullptr;
}
bool isDirty(TypePackId tp) override
{
return replacementPacks.find(tp) != nullptr;
}
TypeId clean(TypeId ty) override
{
TypeId res = replacements[ty];
LUAU_ASSERT(res);
dontTraverseInto(res);
return res;
}
TypePackId clean(TypePackId tp) override
{
TypePackId res = replacementPacks[tp];
LUAU_ASSERT(res);
dontTraverseInto(res);
return res;
}
};
// A substitution which replaces generic functions by monomorphic functions
struct Instantiation2 final : Substitution
{
// Mapping from generic types to free types to be used in instantiation.
DenseHashMap<TypeId, TypeId> genericSubstitutions{nullptr};
// Mapping from generic type packs to `TypePack`s of free types to be used in instantiation.
DenseHashMap<TypePackId, TypePackId> genericPackSubstitutions{nullptr};
Instantiation2(TypeArena* arena, DenseHashMap<TypeId, TypeId> genericSubstitutions, DenseHashMap<TypePackId, TypePackId> genericPackSubstitutions)
: Substitution(TxnLog::empty(), arena)
, genericSubstitutions(std::move(genericSubstitutions))
, genericPackSubstitutions(std::move(genericPackSubstitutions))
{
}
bool ignoreChildren(TypeId ty) override;
bool isDirty(TypeId ty) override;
bool isDirty(TypePackId tp) override;
TypeId clean(TypeId ty) override;
TypePackId clean(TypePackId tp) override;
};
std::optional<TypeId> instantiate2(
TypeArena* arena,
DenseHashMap<TypeId, TypeId> genericSubstitutions,
DenseHashMap<TypePackId, TypePackId> genericPackSubstitutions,
TypeId ty
);
std::optional<TypePackId> instantiate2(
TypeArena* arena,
DenseHashMap<TypeId, TypeId> genericSubstitutions,
DenseHashMap<TypePackId, TypePackId> genericPackSubstitutions,
TypePackId tp
);
} // namespace Luau

View file

@ -3,8 +3,9 @@
#include "Luau/Error.h"
#include "Luau/Location.h"
#include "Luau/TypeVar.h"
#include "Luau/Type.h"
#include "Luau/Ast.h"
#include "Luau/TypePath.h"
#include <ostream>
@ -30,17 +31,32 @@ std::ostream& operator<<(std::ostream& lhs, const OccursCheckFailed& error);
std::ostream& operator<<(std::ostream& lhs, const UnknownRequire& error);
std::ostream& operator<<(std::ostream& lhs, const UnknownPropButFoundLikeProp& e);
std::ostream& operator<<(std::ostream& lhs, const GenericError& error);
std::ostream& operator<<(std::ostream& lhs, const InternalError& error);
std::ostream& operator<<(std::ostream& lhs, const FunctionExitsWithoutReturning& error);
std::ostream& operator<<(std::ostream& lhs, const MissingProperties& error);
std::ostream& operator<<(std::ostream& lhs, const IllegalRequire& error);
std::ostream& operator<<(std::ostream& lhs, const ModuleHasCyclicDependency& error);
std::ostream& operator<<(std::ostream& lhs, const DuplicateGenericParameter& error);
std::ostream& operator<<(std::ostream& lhs, const CannotInferBinaryOperation& error);
std::ostream& operator<<(std::ostream& lhs, const SwappedGenericTypeParameter& error);
std::ostream& operator<<(std::ostream& lhs, const OptionalValueAccess& error);
std::ostream& operator<<(std::ostream& lhs, const MissingUnionProperty& error);
std::ostream& operator<<(std::ostream& lhs, const TypesAreUnrelated& error);
std::ostream& operator<<(std::ostream& lhs, const TableState& tv);
std::ostream& operator<<(std::ostream& lhs, const TypeVar& tv);
std::ostream& operator<<(std::ostream& lhs, const Type& tv);
std::ostream& operator<<(std::ostream& lhs, const TypePackVar& tv);
std::ostream& operator<<(std::ostream& lhs, const TypeErrorData& ted);
std::ostream& operator<<(std::ostream& lhs, TypeId ty);
std::ostream& operator<<(std::ostream& lhs, TypePackId tp);
namespace TypePath
{
std::ostream& operator<<(std::ostream& lhs, const Path& path);
}; // namespace TypePath
} // namespace Luau

View file

@ -0,0 +1,247 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include <type_traits>
#include <string>
#include <optional>
#include <unordered_map>
#include <vector>
#include "Luau/NotNull.h"
namespace Luau::Json
{
struct JsonEmitter;
/// Writes a value to the JsonEmitter. Note that this can produce invalid JSON
/// if you do not insert commas or appropriate object / array syntax.
template<typename T>
void write(JsonEmitter&, T) = delete;
/// Writes a boolean to a JsonEmitter.
/// @param emitter the emitter to write to.
/// @param b the boolean to write.
void write(JsonEmitter& emitter, bool b);
/// Writes an integer to a JsonEmitter.
/// @param emitter the emitter to write to.
/// @param i the integer to write.
void write(JsonEmitter& emitter, int i);
/// Writes an integer to a JsonEmitter.
/// @param emitter the emitter to write to.
/// @param i the integer to write.
void write(JsonEmitter& emitter, long i);
/// Writes an integer to a JsonEmitter.
/// @param emitter the emitter to write to.
/// @param i the integer to write.
void write(JsonEmitter& emitter, long long i);
/// Writes an integer to a JsonEmitter.
/// @param emitter the emitter to write to.
/// @param i the integer to write.
void write(JsonEmitter& emitter, unsigned int i);
/// Writes an integer to a JsonEmitter.
/// @param emitter the emitter to write to.
/// @param i the integer to write.
void write(JsonEmitter& emitter, unsigned long i);
/// Writes an integer to a JsonEmitter.
/// @param emitter the emitter to write to.
/// @param i the integer to write.
void write(JsonEmitter& emitter, unsigned long long i);
/// Writes a double to a JsonEmitter.
/// @param emitter the emitter to write to.
/// @param d the double to write.
void write(JsonEmitter& emitter, double d);
/// Writes a string to a JsonEmitter. The string will be escaped.
/// @param emitter the emitter to write to.
/// @param sv the string to write.
void write(JsonEmitter& emitter, std::string_view sv);
/// Writes a character to a JsonEmitter as a single-character string. The
/// character will be escaped.
/// @param emitter the emitter to write to.
/// @param c the string to write.
void write(JsonEmitter& emitter, char c);
/// Writes a string to a JsonEmitter. The string will be escaped.
/// @param emitter the emitter to write to.
/// @param str the string to write.
void write(JsonEmitter& emitter, const char* str);
/// Writes a string to a JsonEmitter. The string will be escaped.
/// @param emitter the emitter to write to.
/// @param str the string to write.
void write(JsonEmitter& emitter, const std::string& str);
/// Writes null to a JsonEmitter.
/// @param emitter the emitter to write to.
void write(JsonEmitter& emitter, std::nullptr_t);
/// Writes null to a JsonEmitter.
/// @param emitter the emitter to write to.
void write(JsonEmitter& emitter, std::nullopt_t);
struct ObjectEmitter;
struct ArrayEmitter;
struct JsonEmitter
{
JsonEmitter();
/// Converts the current contents of the JsonEmitter to a string value. This
/// does not invalidate the emitter, but it does not clear it either.
std::string str();
/// Returns the current comma state and resets it to false. Use popComma to
/// restore the old state.
/// @returns the previous comma state.
bool pushComma();
/// Restores a previous comma state.
/// @param c the comma state to restore.
void popComma(bool c);
/// Writes a raw sequence of characters to the buffer, without escaping or
/// other processing.
/// @param sv the character sequence to write.
void writeRaw(std::string_view sv);
/// Writes a character to the buffer, without escaping or other processing.
/// @param c the character to write.
void writeRaw(char c);
/// Writes a comma if this wasn't the first time writeComma has been
/// invoked. Otherwise, sets the comma state to true.
/// @see pushComma
/// @see popComma
void writeComma();
/// Begins writing an object to the emitter.
/// @returns an ObjectEmitter that can be used to write key-value pairs.
ObjectEmitter writeObject();
/// Begins writing an array to the emitter.
/// @returns an ArrayEmitter that can be used to write values.
ArrayEmitter writeArray();
private:
bool comma = false;
std::vector<std::string> chunks;
void newChunk();
};
/// An interface for writing an object into a JsonEmitter instance.
/// @see JsonEmitter::writeObject
struct ObjectEmitter
{
ObjectEmitter(NotNull<JsonEmitter> emitter);
~ObjectEmitter();
NotNull<JsonEmitter> emitter;
bool comma;
bool finished;
/// Writes a key-value pair to the associated JsonEmitter. Keys will be escaped.
/// @param name the name of the key-value pair.
/// @param value the value to write.
template<typename T>
void writePair(std::string_view name, T value)
{
if (finished)
{
return;
}
emitter->writeComma();
write(*emitter, name);
emitter->writeRaw(':');
write(*emitter, value);
}
/// Finishes writing the object, appending a closing `}` character and
/// resetting the comma state of the associated emitter. This can only be
/// called once, and once called will render the emitter unusable. This
/// method is also called when the ObjectEmitter is destructed.
void finish();
};
/// An interface for writing an array into a JsonEmitter instance. Array values
/// do not need to be the same type.
/// @see JsonEmitter::writeArray
struct ArrayEmitter
{
ArrayEmitter(NotNull<JsonEmitter> emitter);
~ArrayEmitter();
NotNull<JsonEmitter> emitter;
bool comma;
bool finished;
/// Writes a value to the array.
/// @param value the value to write.
template<typename T>
void writeValue(T value)
{
if (finished)
{
return;
}
emitter->writeComma();
write(*emitter, value);
}
/// Finishes writing the object, appending a closing `]` character and
/// resetting the comma state of the associated emitter. This can only be
/// called once, and once called will render the emitter unusable. This
/// method is also called when the ArrayEmitter is destructed.
void finish();
};
/// Writes a vector as an array to a JsonEmitter.
/// @param emitter the emitter to write to.
/// @param vec the vector to write.
template<typename T>
void write(JsonEmitter& emitter, const std::vector<T>& vec)
{
ArrayEmitter a = emitter.writeArray();
for (const T& value : vec)
a.writeValue(value);
a.finish();
}
/// Writes an optional to a JsonEmitter. Will write the contained value, if
/// present, or null, if no value is present.
/// @param emitter the emitter to write to.
/// @param v the value to write.
template<typename T>
void write(JsonEmitter& emitter, const std::optional<T>& v)
{
if (v.has_value())
write(emitter, *v);
else
emitter.writeRaw("null");
}
template<typename T>
void write(JsonEmitter& emitter, const std::unordered_map<std::string, T>& map)
{
ObjectEmitter o = emitter.writeObject();
for (const auto& [k, v] : map)
o.writePair(k, v);
o.finish();
}
} // namespace Luau::Json

View file

@ -0,0 +1,51 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Variant.h"
#include "Luau/Symbol.h"
#include "Luau/TypeFwd.h"
#include <memory>
#include <unordered_map>
namespace Luau
{
struct Field;
// Deprecated. Do not use in new work.
using LValue = Variant<Symbol, Field>;
struct Field
{
std::shared_ptr<LValue> parent;
std::string key;
bool operator==(const Field& rhs) const;
bool operator!=(const Field& rhs) const;
};
struct LValueHasher
{
size_t operator()(const LValue& lvalue) const;
};
const LValue* baseof(const LValue& lvalue);
std::optional<LValue> tryGetLValue(const class AstExpr& expr);
// Utility function: breaks down an LValue to get at the Symbol
Symbol getBaseSymbol(const LValue& lvalue);
template<typename T>
const T* get(const LValue& lvalue)
{
return get_if<T>(&lvalue);
}
using RefinementMap = std::unordered_map<LValue, TypeId, LValueHasher>;
void merge(RefinementMap& l, const RefinementMap& r, std::function<TypeId(TypeId, TypeId)> f);
void addRefinement(RefinementMap& refis, const LValue& lvalue, TypeId ty);
} // namespace Luau

View file

@ -1,9 +1,11 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/LinterConfig.h"
#include "Luau/Location.h"
#include <memory>
#include <string>
#include <vector>
namespace Luau
@ -17,79 +19,20 @@ struct Module;
using ScopePtr = std::shared_ptr<struct Scope>;
struct LintWarning
{
// Make sure any new lint codes are documented here: https://luau-lang.org/lint
// Note that in Studio, the active set of lint warnings is determined by FStringStudioLuauLints
enum Code
{
Code_Unknown = 0,
Code_UnknownGlobal = 1, // superseded by type checker
Code_DeprecatedGlobal = 2,
Code_GlobalUsedAsLocal = 3,
Code_LocalShadow = 4, // disabled in Studio
Code_SameLineStatement = 5, // disabled in Studio
Code_MultiLineStatement = 6,
Code_LocalUnused = 7, // disabled in Studio
Code_FunctionUnused = 8, // disabled in Studio
Code_ImportUnused = 9, // disabled in Studio
Code_BuiltinGlobalWrite = 10,
Code_PlaceholderRead = 11,
Code_UnreachableCode = 12,
Code_UnknownType = 13,
Code_ForRange = 14,
Code_UnbalancedAssignment = 15,
Code_ImplicitReturn = 16, // disabled in Studio, superseded by type checker in strict mode
Code_DuplicateLocal = 17,
Code_FormatString = 18,
Code_TableLiteral = 19,
Code_UninitializedLocal = 20,
Code_DuplicateFunction = 21,
Code_DeprecatedApi = 22,
Code_TableOperations = 23,
Code_DuplicateCondition = 24,
Code__Count
};
Code code;
Location location;
std::string text;
static const char* getName(Code code);
static Code parseName(const char* name);
static uint64_t parseMask(const std::vector<std::string>& hotcomments);
};
struct LintResult
{
std::vector<LintWarning> errors;
std::vector<LintWarning> warnings;
};
struct LintOptions
{
uint64_t warningMask = 0;
void enableWarning(LintWarning::Code code)
{
warningMask |= 1ull << code;
}
void disableWarning(LintWarning::Code code)
{
warningMask &= ~(1ull << code);
}
bool isEnabled(LintWarning::Code code) const
{
return 0 != (warningMask & (1ull << code));
}
void setDefaults();
};
std::vector<LintWarning> lint(AstStat* root, const AstNameTable& names, const ScopePtr& env, const Module* module, const LintOptions& options);
std::vector<LintWarning> lint(
AstStat* root,
const AstNameTable& names,
const ScopePtr& env,
const Module* module,
const std::vector<HotComment>& hotcomments,
const LintOptions& options
);
std::vector<AstName> getDeprecatedGlobals(const AstNameTable& names);

View file

@ -0,0 +1,33 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Ast.h"
#include <unordered_map>
namespace Luau
{
static const std::unordered_map<AstExprBinary::Op, const char*> kBinaryOpMetamethods{
{AstExprBinary::Op::CompareEq, "__eq"},
{AstExprBinary::Op::CompareNe, "__eq"},
{AstExprBinary::Op::CompareGe, "__lt"},
{AstExprBinary::Op::CompareGt, "__le"},
{AstExprBinary::Op::CompareLe, "__le"},
{AstExprBinary::Op::CompareLt, "__lt"},
{AstExprBinary::Op::Add, "__add"},
{AstExprBinary::Op::Sub, "__sub"},
{AstExprBinary::Op::Mul, "__mul"},
{AstExprBinary::Op::Div, "__div"},
{AstExprBinary::Op::FloorDiv, "__idiv"},
{AstExprBinary::Op::Pow, "__pow"},
{AstExprBinary::Op::Mod, "__mod"},
{AstExprBinary::Op::Concat, "__concat"},
};
static const std::unordered_map<AstExprUnary::Op, const char*> kUnaryOpMetamethods{
{AstExprUnary::Op::Minus, "__unm"},
{AstExprUnary::Op::Len, "__len"},
};
} // namespace Luau

View file

@ -1,12 +1,14 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/FileResolver.h"
#include "Luau/TypePack.h"
#include "Luau/TypedAllocator.h"
#include "Luau/ParseOptions.h"
#include "Luau/Error.h"
#include "Luau/Parser.h"
#include "Luau/Linter.h"
#include "Luau/FileResolver.h"
#include "Luau/ParseOptions.h"
#include "Luau/ParseResult.h"
#include "Luau/Scope.h"
#include "Luau/TypeArena.h"
#include "Luau/DataFlowGraph.h"
#include <memory>
#include <vector>
@ -16,27 +18,38 @@
namespace Luau
{
using LogLuauProc = void (*)(std::string_view, std::string_view);
extern LogLuauProc logLuau;
void setLogLuau(LogLuauProc ll);
void resetLogLuauProc();
struct Module;
using ScopePtr = std::shared_ptr<struct Scope>;
using ModulePtr = std::shared_ptr<Module>;
class AstType;
class AstTypePack;
/// Root of the AST of a parsed source file
struct SourceModule
{
ModuleName name; // DataModel path if possible. Filename if not.
ModuleName name; // Module identifier or a filename
std::string humanReadableName;
SourceCode::Type type = SourceCode::None;
std::optional<std::string> environmentName;
bool cyclic = false;
std::unique_ptr<Allocator> allocator;
std::unique_ptr<AstNameTable> names;
std::shared_ptr<Allocator> allocator;
std::shared_ptr<AstNameTable> names;
std::vector<ParseError> parseErrors;
AstStatBlock* root = nullptr;
std::optional<Mode> mode;
uint64_t ignoreLints = 0;
std::vector<HotComment> hotcomments;
std::vector<Comment> commentLocations;
SourceModule()
@ -46,68 +59,97 @@ struct SourceModule
}
};
bool isWithinComment(const std::vector<Comment>& commentLocations, Position pos);
bool isWithinComment(const SourceModule& sourceModule, Position pos);
bool isWithinComment(const ParseResult& result, Position pos);
struct TypeArena
struct RequireCycle
{
TypedAllocator<TypeVar> typeVars;
TypedAllocator<TypePackVar> typePacks;
void clear();
template<typename T>
TypeId addType(T tv)
{
return addTV(TypeVar(std::move(tv)));
}
TypeId addTV(TypeVar&& tv);
TypeId freshType(TypeLevel level);
TypePackId addTypePack(std::initializer_list<TypeId> types);
TypePackId addTypePack(std::vector<TypeId> types);
TypePackId addTypePack(TypePack pack);
TypePackId addTypePack(TypePackVar pack);
Location location;
std::vector<ModuleName> path; // one of the paths for a require() to go all the way back to the originating module
};
void freeze(TypeArena& arena);
void unfreeze(TypeArena& arena);
// Only exposed so they can be unit tested.
using SeenTypes = std::unordered_map<TypeId, TypeId>;
using SeenTypePacks = std::unordered_map<TypePackId, TypePackId>;
TypePackId clone(TypePackId tp, TypeArena& dest, SeenTypes& seenTypes, SeenTypePacks& seenTypePacks, bool* encounteredFreeType = nullptr);
TypeId clone(TypeId tp, TypeArena& dest, SeenTypes& seenTypes, SeenTypePacks& seenTypePacks, bool* encounteredFreeType = nullptr);
TypeFun clone(const TypeFun& typeFun, TypeArena& dest, SeenTypes& seenTypes, SeenTypePacks& seenTypePacks, bool* encounteredFreeType = nullptr);
struct Module
{
~Module();
// TODO: Clip this when we clip FFlagLuauSolverV2
bool checkedInNewSolver = false;
ModuleName name;
std::string humanReadableName;
TypeArena interfaceTypes;
TypeArena internalTypes;
// Scopes and AST types refer to parse data, so we need to keep that alive
std::shared_ptr<Allocator> allocator;
std::shared_ptr<AstNameTable> names;
AstStatBlock* root = nullptr;
std::vector<std::pair<Location, ScopePtr>> scopes; // never empty
DenseHashMap<const AstExpr*, TypeId> astTypes{nullptr};
DenseHashMap<const AstExpr*, TypePackId> astTypePacks{nullptr};
DenseHashMap<const AstExpr*, TypeId> astExpectedTypes{nullptr};
DenseHashMap<const AstExpr*, TypeId> astOriginalCallTypes{nullptr};
DenseHashMap<const AstExpr*, TypeId> astOverloadResolvedTypes{nullptr};
// For AST nodes that are function calls, this map provides the
// unspecialized type of the function that was called. If a function call
// resolves to a __call metamethod application, this map will point at that
// metamethod.
//
// This is useful for type checking and Signature Help.
DenseHashMap<const AstNode*, TypeId> astOriginalCallTypes{nullptr};
// The specialization of a function that was selected. If the function is
// generic, those generic type parameters will be replaced with the actual
// types that were passed. If the function is an overload, this map will
// point at the specific overloads that were selected.
DenseHashMap<const AstNode*, TypeId> astOverloadResolvedTypes{nullptr};
// Only used with for...in loops. The computed type of the next() function
// is kept here for type checking.
DenseHashMap<const AstNode*, TypeId> astForInNextTypes{nullptr};
DenseHashMap<const AstType*, TypeId> astResolvedTypes{nullptr};
DenseHashMap<const AstTypePack*, TypePackId> astResolvedTypePacks{nullptr};
// The computed result type of a compound assignment. (eg foo += 1)
//
// Type checking uses this to check that the result of such an operation is
// actually compatible with the left-side operand.
DenseHashMap<const AstStat*, TypeId> astCompoundAssignResultTypes{nullptr};
DenseHashMap<TypeId, std::vector<std::pair<Location, TypeId>>> upperBoundContributors{nullptr};
// Map AST nodes to the scope they create. Cannot be NotNull<Scope> because
// we need a sentinel value for the map.
DenseHashMap<const AstNode*, Scope*> astScopes{nullptr};
std::unordered_map<Name, TypeId> declaredGlobals;
ErrorVec errors;
LintResult lintResult;
Mode mode;
SourceCode::Type type;
double checkDurationSec = 0.0;
bool timeout = false;
bool cancelled = false;
TypePackId returnType = nullptr;
std::unordered_map<Name, TypeFun> exportedTypeBindings;
// Arenas related to the DFG must persist after the DFG no longer exists, as
// Module objects maintain raw pointers to objects in these arenas.
DefArena defArena;
RefinementKeyArena keyArena;
bool hasModuleScope() const;
ScopePtr getModuleScope() const;
// Once a module has been typechecked, we clone its public interface into a separate arena.
// This helps us to force TypeVar ownership into a DAG rather than a DCG.
// Returns true if there were any free types encountered in the public interface. This
// indicates a bug in the type checker that we want to surface.
bool clonePublicInterface();
// Once a module has been typechecked, we clone its public interface into a
// separate arena. This helps us to force Type ownership into a DAG rather
// than a DCG.
void clonePublicInterface(NotNull<BuiltinTypes> builtinTypes, InternalErrorReporter& ice);
};
} // namespace Luau

View file

@ -20,8 +20,6 @@ struct ModuleResolver
virtual ~ModuleResolver() {}
/** Compute a ModuleName from an AST fragment. This AST fragment is generally the argument to the require() function.
*
* You probably want to implement this with some variation of pathExprToModuleName.
*
* @returns The ModuleInfo if the expression is a syntactically legal path.
* @returns std::nullopt if we are unable to determine whether or not the expression is a valid path. Type inference will

View file

@ -0,0 +1,30 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/DataFlowGraph.h"
#include "Luau/EqSatSimplification.h"
#include "Luau/Module.h"
#include "Luau/NotNull.h"
namespace Luau
{
struct BuiltinTypes;
struct TypeFunctionRuntime;
struct UnifierSharedState;
struct TypeCheckLimits;
void checkNonStrict(
NotNull<BuiltinTypes> builtinTypes,
NotNull<Simplifier> simplifier,
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
NotNull<InternalErrorReporter> ice,
NotNull<UnifierSharedState> unifierState,
NotNull<const DataFlowGraph> dfg,
NotNull<TypeCheckLimits> limits,
const SourceModule& sourceModule,
Module* module
);
} // namespace Luau

View file

@ -0,0 +1,465 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/EqSatSimplification.h"
#include "Luau/NotNull.h"
#include "Luau/Set.h"
#include "Luau/TypeFwd.h"
#include "Luau/UnifierSharedState.h"
#include <initializer_list>
#include <map>
#include <memory>
#include <unordered_map>
#include <vector>
namespace Luau
{
struct InternalErrorReporter;
struct Module;
struct Scope;
using ModulePtr = std::shared_ptr<Module>;
bool isSubtype(
TypeId subTy,
TypeId superTy,
NotNull<Scope> scope,
NotNull<BuiltinTypes> builtinTypes,
NotNull<Simplifier> simplifier,
InternalErrorReporter& ice
);
bool isSubtype(
TypePackId subPack,
TypePackId superPack,
NotNull<Scope> scope,
NotNull<BuiltinTypes> builtinTypes,
NotNull<Simplifier> simplifier,
InternalErrorReporter& ice
);
class TypeIds
{
private:
DenseHashMap<TypeId, bool> types{nullptr};
std::vector<TypeId> order;
std::size_t hash = 0;
public:
using iterator = std::vector<TypeId>::iterator;
using const_iterator = std::vector<TypeId>::const_iterator;
TypeIds() = default;
~TypeIds() = default;
TypeIds(std::initializer_list<TypeId> tys);
TypeIds(const TypeIds&) = default;
TypeIds& operator=(const TypeIds&) = default;
TypeIds(TypeIds&&) = default;
TypeIds& operator=(TypeIds&&) = default;
void insert(TypeId ty);
/// Erase every element that does not also occur in tys
void retain(const TypeIds& tys);
void clear();
TypeId front() const;
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
iterator erase(const_iterator it);
void erase(TypeId ty);
size_t size() const;
bool empty() const;
size_t count(TypeId ty) const;
template<class Iterator>
void insert(Iterator begin, Iterator end)
{
for (Iterator it = begin; it != end; ++it)
insert(*it);
}
bool operator==(const TypeIds& there) const;
size_t getHash() const;
bool isNever() const;
};
} // namespace Luau
template<>
struct std::hash<Luau::TypeIds>
{
std::size_t operator()(const Luau::TypeIds& tys) const
{
return tys.getHash();
}
};
template<>
struct std::hash<const Luau::TypeIds*>
{
std::size_t operator()(const Luau::TypeIds* tys) const
{
return tys->getHash();
}
};
template<>
struct std::equal_to<Luau::TypeIds>
{
bool operator()(const Luau::TypeIds& here, const Luau::TypeIds& there) const
{
return here == there;
}
};
template<>
struct std::equal_to<const Luau::TypeIds*>
{
bool operator()(const Luau::TypeIds* here, const Luau::TypeIds* there) const
{
return *here == *there;
}
};
namespace Luau
{
/** A normalized string type is either `string` (represented by `nullopt`) or a
* union of string singletons.
*
* The representation is as follows:
*
* * A union of string singletons is finite and includes the singletons named by
* the `singletons` field.
* * An intersection of negated string singletons is cofinite and includes the
* singletons excluded by the `singletons` field. It is implied that cofinite
* values are exclusions from `string` itself.
* * The `string` data type is a cofinite set minus zero elements.
* * The `never` data type is a finite set plus zero elements.
*/
struct NormalizedStringType
{
// When false, this type represents a union of singleton string types.
// eg "a" | "b" | "c"
//
// When true, this type represents string intersected with negated string
// singleton types.
// eg string & ~"a" & ~"b" & ...
bool isCofinite = false;
std::map<std::string, TypeId> singletons;
void resetToString();
void resetToNever();
bool isNever() const;
bool isString() const;
/// Returns true if the string has finite domain.
///
/// Important subtlety: This method returns true for `never`. The empty set
/// is indeed an empty set.
bool isUnion() const;
/// Returns true if the string has infinite domain.
bool isIntersection() const;
bool includes(const std::string& str) const;
static const NormalizedStringType never;
NormalizedStringType();
NormalizedStringType(bool isCofinite, std::map<std::string, TypeId> singletons);
};
bool isSubtype(const NormalizedStringType& subStr, const NormalizedStringType& superStr);
struct NormalizedExternType
{
/** Has the following structure:
*
* (C1 & ~N11 & ... & ~Nn) | (C2 & ~N21 & ... & ~N2n) | ...
*
* C2 is either not a subtype of any other Cm, or it is and is also a
* subtype of one of Nmn types within the same cluster.
*
* Each TypeId is a class type.
*/
std::unordered_map<TypeId, TypeIds> externTypes;
/**
* In order to maintain a consistent insertion order, we use this vector to
* keep track of it. An ordered std::map will sort by pointer identity,
* which is undesirable.
*/
std::vector<TypeId> ordering;
void pushPair(TypeId ty, TypeIds negations);
void resetToNever();
bool isNever() const;
};
// A normalized function type can be `never`, the top function type `function`,
// or an intersection of function types.
//
// NOTE: type normalization can fail on function types with generics (e.g.
// because we do not support unions and intersections of generic type packs), so
// this type may contain `error`.
struct NormalizedFunctionType
{
bool isTop = false;
TypeIds parts;
void resetToNever();
void resetToTop();
bool isNever() const;
};
// A normalized generic/free type is a union, where each option is of the form (X & T) where
// * X is either a free type, a generic or a blocked type.
// * T is a normalized type.
struct NormalizedType;
using NormalizedTyvars = std::unordered_map<TypeId, std::unique_ptr<NormalizedType>>;
// Operations provided by `Normalizer` can have ternary results:
// 1. The operation returned true.
// 2. The operation returned false.
// 3. They can hit resource limitations, which invalidates _all normalized types_.
enum class NormalizationResult
{
// The operation returned true or succeeded.
True,
// The operation returned false or failed.
False,
// Resource limits were hit, invalidating all normalized types.
HitLimits,
};
// A normalized type is either any, unknown, or one of the form P | T | F | G where
// * P is a union of primitive types (including singletons, extern types and the error type)
// * T is a union of table types
// * F is a union of an intersection of function types
// * G is a union of generic/free/blocked types, intersected with a normalized type
struct NormalizedType
{
// The top part of the type.
// This type is either never, unknown, or any.
// If this type is not never, all the other fields are null.
TypeId tops;
// The boolean part of the type.
// This type is either never, boolean type, or a boolean singleton.
TypeId booleans;
NormalizedExternType externTypes;
// The error part of the type.
// This type is either never or the error type.
TypeId errors;
// The nil part of the type.
// This type is either never or nil.
TypeId nils;
// The number part of the type.
// This type is either never or number.
TypeId numbers;
// The string part of the type.
// This may be the `string` type, or a union of singletons.
NormalizedStringType strings;
// The thread part of the type.
// This type is either never or thread.
TypeId threads;
// The buffer part of the type.
// This type is either never or buffer.
TypeId buffers;
// The (meta)table part of the type.
// Each element of this set is a (meta)table type, or the top `table` type.
// An empty set denotes never.
TypeIds tables;
// The function part of the type.
NormalizedFunctionType functions;
// The generic/free part of the type.
NormalizedTyvars tyvars;
// Free types, blocked types, and certain other types change shape as type
// inference is done. If we were to cache the normalization of these types,
// we'd be reusing bad, stale data.
bool isCacheable = true;
NormalizedType(NotNull<BuiltinTypes> builtinTypes);
NormalizedType() = delete;
~NormalizedType() = default;
NormalizedType(const NormalizedType&) = delete;
NormalizedType& operator=(const NormalizedType&) = delete;
NormalizedType(NormalizedType&&) = default;
NormalizedType& operator=(NormalizedType&&) = default;
// IsType functions
bool isUnknown() const;
/// Returns true if the type is exactly a number. Behaves like Type::isNumber()
bool isExactlyNumber() const;
/// Returns true if the type is a subtype of string(it could be a singleton). Behaves like Type::isString()
bool isSubtypeOfString() const;
/// Returns true if the type is a subtype of boolean(it could be a singleton). Behaves like Type::isBoolean()
bool isSubtypeOfBooleans() const;
/// Returns true if this type should result in error suppressing behavior.
bool shouldSuppressErrors() const;
/// Returns true if this type contains the primitve top table type, `table`.
bool hasTopTable() const;
// Helpers that improve readability of the above (they just say if the component is present)
bool hasTops() const;
bool hasBooleans() const;
bool hasExternTypes() const;
bool hasErrors() const;
bool hasNils() const;
bool hasNumbers() const;
bool hasStrings() const;
bool hasThreads() const;
bool hasBuffers() const;
bool hasTables() const;
bool hasFunctions() const;
bool hasTyvars() const;
bool isFalsy() const;
bool isTruthy() const;
};
using SeenTablePropPairs = Set<std::pair<TypeId, TypeId>, TypeIdPairHash>;
class Normalizer
{
std::unordered_map<TypeId, std::shared_ptr<NormalizedType>> cachedNormals;
std::unordered_map<const TypeIds*, TypeId> cachedIntersections;
std::unordered_map<const TypeIds*, TypeId> cachedUnions;
std::unordered_map<const TypeIds*, std::unique_ptr<TypeIds>> cachedTypeIds;
DenseHashMap<TypeId, bool> cachedIsInhabited{nullptr};
DenseHashMap<std::pair<TypeId, TypeId>, bool, TypeIdPairHash> cachedIsInhabitedIntersection{{nullptr, nullptr}};
bool withinResourceLimits();
public:
TypeArena* arena;
NotNull<BuiltinTypes> builtinTypes;
NotNull<UnifierSharedState> sharedState;
bool cacheInhabitance = false;
Normalizer(TypeArena* arena, NotNull<BuiltinTypes> builtinTypes, NotNull<UnifierSharedState> sharedState, bool cacheInhabitance = false);
Normalizer(const Normalizer&) = delete;
Normalizer(Normalizer&&) = delete;
Normalizer() = delete;
~Normalizer() = default;
Normalizer& operator=(Normalizer&&) = delete;
Normalizer& operator=(Normalizer&) = delete;
// If this returns null, the typechecker should emit a "too complex" error
std::shared_ptr<const NormalizedType> normalize(TypeId ty);
void clearNormal(NormalizedType& norm);
// ------- Cached TypeIds
TypeId unionType(TypeId here, TypeId there);
TypeId intersectionType(TypeId here, TypeId there);
const TypeIds* cacheTypeIds(TypeIds tys);
void clearCaches();
// ------- Normalizing unions
void unionTysWithTy(TypeIds& here, TypeId there);
TypeId unionOfTops(TypeId here, TypeId there);
TypeId unionOfBools(TypeId here, TypeId there);
void unionExternTypesWithExternType(TypeIds& heres, TypeId there);
void unionExternTypes(TypeIds& heres, const TypeIds& theres);
void unionExternTypesWithExternType(NormalizedExternType& heres, TypeId there);
void unionExternTypes(NormalizedExternType& heres, const NormalizedExternType& theres);
void unionStrings(NormalizedStringType& here, const NormalizedStringType& there);
std::optional<TypePackId> unionOfTypePacks(TypePackId here, TypePackId there);
std::optional<TypeId> unionOfFunctions(TypeId here, TypeId there);
std::optional<TypeId> unionSaturatedFunctions(TypeId here, TypeId there);
void unionFunctionsWithFunction(NormalizedFunctionType& heress, TypeId there);
void unionFunctions(NormalizedFunctionType& heress, const NormalizedFunctionType& theress);
void unionTablesWithTable(TypeIds& heres, TypeId there);
void unionTables(TypeIds& heres, const TypeIds& theres);
NormalizationResult unionNormals(NormalizedType& here, const NormalizedType& there, int ignoreSmallerTyvars = -1);
NormalizationResult unionNormalWithTy(
NormalizedType& here,
TypeId there,
SeenTablePropPairs& seenTablePropPairs,
Set<TypeId>& seenSetTypes,
int ignoreSmallerTyvars = -1
);
// ------- Negations
std::optional<NormalizedType> negateNormal(const NormalizedType& here);
TypeIds negateAll(const TypeIds& theres);
TypeId negate(TypeId there);
void subtractPrimitive(NormalizedType& here, TypeId ty);
void subtractSingleton(NormalizedType& here, TypeId ty);
NormalizationResult intersectNormalWithNegationTy(TypeId toNegate, NormalizedType& intersect);
// ------- Normalizing intersections
TypeId intersectionOfTops(TypeId here, TypeId there);
TypeId intersectionOfBools(TypeId here, TypeId there);
void intersectExternTypes(NormalizedExternType& heres, const NormalizedExternType& theres);
void intersectExternTypesWithExternType(NormalizedExternType& heres, TypeId there);
void intersectStrings(NormalizedStringType& here, const NormalizedStringType& there);
std::optional<TypePackId> intersectionOfTypePacks(TypePackId here, TypePackId there);
std::optional<TypeId> intersectionOfTables(TypeId here, TypeId there, SeenTablePropPairs& seenTablePropPairs, Set<TypeId>& seenSet);
void intersectTablesWithTable(TypeIds& heres, TypeId there, SeenTablePropPairs& seenTablePropPairs, Set<TypeId>& seenSetTypes);
void intersectTables(TypeIds& heres, const TypeIds& theres);
std::optional<TypeId> intersectionOfFunctions(TypeId here, TypeId there);
void intersectFunctionsWithFunction(NormalizedFunctionType& heress, TypeId there);
void intersectFunctions(NormalizedFunctionType& heress, const NormalizedFunctionType& theress);
NormalizationResult intersectTyvarsWithTy(
NormalizedTyvars& here,
TypeId there,
SeenTablePropPairs& seenTablePropPairs,
Set<TypeId>& seenSetTypes
);
NormalizationResult intersectNormals(NormalizedType& here, const NormalizedType& there, int ignoreSmallerTyvars = -1);
NormalizationResult intersectNormalWithTy(NormalizedType& here, TypeId there, SeenTablePropPairs& seenTablePropPairs, Set<TypeId>& seenSetTypes);
NormalizationResult normalizeIntersections(
const std::vector<TypeId>& intersections,
NormalizedType& outType,
SeenTablePropPairs& seenTablePropPairs,
Set<TypeId>& seenSet
);
// Check for inhabitance
NormalizationResult isInhabited(TypeId ty);
NormalizationResult isInhabited(TypeId ty, Set<TypeId>& seen);
NormalizationResult isInhabited(const NormalizedType* norm);
NormalizationResult isInhabited(const NormalizedType* norm, Set<TypeId>& seen);
// Check for intersections being inhabited
NormalizationResult isIntersectionInhabited(TypeId left, TypeId right);
NormalizationResult isIntersectionInhabited(TypeId left, TypeId right, SeenTablePropPairs& seenTablePropPairs, Set<TypeId>& seenSet);
// -------- Convert back from a normalized type to a type
TypeId typeFromNormal(const NormalizedType& norm);
};
} // namespace Luau

View file

@ -0,0 +1,104 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Common.h"
#include <functional>
namespace Luau
{
/** A non-owning, non-null pointer to a T.
*
* A NotNull<T> is notionally identical to a T* with the added restriction that
* it can never store nullptr.
*
* The sole conversion rule from T* to NotNull<T> is the single-argument
* constructor, which is intentionally marked explicit. This constructor
* performs a runtime test to verify that the passed pointer is never nullptr.
*
* Pointer arithmetic, increment, decrement, and array indexing are all
* forbidden.
*
* An implicit coersion from NotNull<T> to T* is afforded, as are the pointer
* indirection and member access operators. (*p and p->prop)
*
* The explicit delete statement is permitted (but not recommended) on a
* NotNull<T> through this implicit conversion.
*/
template<typename T>
struct NotNull
{
explicit NotNull(T* t)
: ptr(t)
{
LUAU_ASSERT(t);
}
explicit NotNull(std::nullptr_t) = delete;
void operator=(std::nullptr_t) = delete;
template<typename U>
NotNull(NotNull<U> other)
: ptr(other.get())
{
}
operator T*() const noexcept
{
return ptr;
}
T& operator*() const noexcept
{
return *ptr;
}
T* operator->() const noexcept
{
return ptr;
}
template<typename U>
bool operator==(NotNull<U> other) const noexcept
{
return get() == other.get();
}
template<typename U>
bool operator!=(NotNull<U> other) const noexcept
{
return get() != other.get();
}
operator bool() const noexcept = delete;
T& operator[](int) = delete;
T& operator+(int) = delete;
T& operator-(int) = delete;
T* get() const noexcept
{
return ptr;
}
private:
T* ptr;
};
} // namespace Luau
namespace std
{
template<typename T>
struct hash<Luau::NotNull<T>>
{
size_t operator()(const Luau::NotNull<T>& p) const
{
return std::hash<T*>()(p.get());
}
};
} // namespace std

View file

@ -0,0 +1,127 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Ast.h"
#include "Luau/EqSatSimplification.h"
#include "Luau/Error.h"
#include "Luau/InsertionOrderedMap.h"
#include "Luau/Location.h"
#include "Luau/NotNull.h"
#include "Luau/Subtyping.h"
#include "Luau/TypeFwd.h"
namespace Luau
{
struct BuiltinTypes;
struct TypeArena;
struct Scope;
struct InternalErrorReporter;
struct TypeCheckLimits;
struct Subtyping;
class Normalizer;
struct OverloadResolver
{
enum Analysis
{
Ok,
TypeIsNotAFunction,
ArityMismatch,
OverloadIsNonviable, // Arguments were incompatible with the overloads parameters but were otherwise compatible by arity
};
OverloadResolver(
NotNull<BuiltinTypes> builtinTypes,
NotNull<TypeArena> arena,
NotNull<Simplifier> simplifier,
NotNull<Normalizer> normalizer,
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
NotNull<Scope> scope,
NotNull<InternalErrorReporter> reporter,
NotNull<TypeCheckLimits> limits,
Location callLocation
);
NotNull<BuiltinTypes> builtinTypes;
NotNull<TypeArena> arena;
NotNull<Simplifier> simplifier;
NotNull<Normalizer> normalizer;
NotNull<TypeFunctionRuntime> typeFunctionRuntime;
NotNull<Scope> scope;
NotNull<InternalErrorReporter> ice;
NotNull<TypeCheckLimits> limits;
Subtyping subtyping;
Location callLoc;
// Resolver results
std::vector<TypeId> ok;
std::vector<TypeId> nonFunctions;
std::vector<std::pair<TypeId, ErrorVec>> arityMismatches;
std::vector<std::pair<TypeId, ErrorVec>> nonviableOverloads;
InsertionOrderedMap<TypeId, std::pair<OverloadResolver::Analysis, size_t>> resolution;
std::pair<OverloadResolver::Analysis, TypeId> selectOverload(TypeId ty, TypePackId args);
void resolve(TypeId fnTy, const TypePack* args, AstExpr* selfExpr, const std::vector<AstExpr*>* argExprs);
private:
std::optional<ErrorVec> testIsSubtype(const Location& location, TypeId subTy, TypeId superTy);
std::optional<ErrorVec> testIsSubtype(const Location& location, TypePackId subTy, TypePackId superTy);
std::pair<Analysis, ErrorVec> checkOverload(
TypeId fnTy,
const TypePack* args,
AstExpr* fnLoc,
const std::vector<AstExpr*>* argExprs,
bool callMetamethodOk = true
);
static bool isLiteral(AstExpr* expr);
LUAU_NOINLINE
std::pair<Analysis, ErrorVec> checkOverload_(
TypeId fnTy,
const FunctionType* fn,
const TypePack* args,
AstExpr* fnExpr,
const std::vector<AstExpr*>* argExprs
);
size_t indexof(Analysis analysis);
void add(Analysis analysis, TypeId ty, ErrorVec&& errors);
};
struct SolveResult
{
enum OverloadCallResult
{
Ok,
CodeTooComplex,
OccursCheckFailed,
NoMatchingOverload,
};
OverloadCallResult result;
std::optional<TypePackId> typePackId; // nullopt if result != Ok
TypeId overloadToUse = nullptr;
TypeId inferredTy = nullptr;
DenseHashMap<TypeId, std::vector<TypeId>> expandedFreeTypes{nullptr};
};
// Helper utility, presently used for binary operator type functions.
//
// Given a function and a set of arguments, select a suitable overload.
SolveResult solveFunctionCall(
NotNull<TypeArena> arena,
NotNull<BuiltinTypes> builtinTypes,
NotNull<Simplifier> simplifier,
NotNull<Normalizer> normalizer,
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
NotNull<InternalErrorReporter> iceReporter,
NotNull<TypeCheckLimits> limits,
NotNull<Scope> scope,
const Location& location,
TypeId fn,
TypePackId argsPack
);
} // namespace Luau

View file

@ -0,0 +1,68 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include <cstdint>
namespace Luau
{
enum struct Polarity : uint8_t
{
None = 0b000,
Positive = 0b001,
Negative = 0b010,
Mixed = 0b011,
Unknown = 0b100,
};
inline Polarity operator|(Polarity lhs, Polarity rhs)
{
return Polarity(uint8_t(lhs) | uint8_t(rhs));
}
inline Polarity& operator|=(Polarity& lhs, Polarity rhs)
{
lhs = lhs | rhs;
return lhs;
}
inline Polarity operator&(Polarity lhs, Polarity rhs)
{
return Polarity(uint8_t(lhs) & uint8_t(rhs));
}
inline Polarity& operator&=(Polarity& lhs, Polarity rhs)
{
lhs = lhs & rhs;
return lhs;
}
inline bool isPositive(Polarity p)
{
return bool(p & Polarity::Positive);
}
inline bool isNegative(Polarity p)
{
return bool(p & Polarity::Negative);
}
inline bool isKnown(Polarity p)
{
return p != Polarity::Unknown;
}
inline Polarity invert(Polarity p)
{
switch (p)
{
case Polarity::Positive:
return Polarity::Negative;
case Polarity::Negative:
return Polarity::Positive;
default:
return p;
}
}
} // namespace Luau

View file

@ -1,48 +1,16 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Variant.h"
#include "Luau/Location.h"
#include "Luau/Symbol.h"
#include "Luau/LValue.h"
#include "Luau/Variant.h"
#include "Luau/TypeFwd.h"
#include <map>
#include <memory>
#include <vector>
namespace Luau
{
struct TypeVar;
using TypeId = const TypeVar*;
struct Field;
using LValue = Variant<Symbol, Field>;
struct Field
{
std::shared_ptr<LValue> parent; // TODO: Eventually use unique_ptr to enforce non-copyable trait.
std::string key;
};
std::optional<LValue> tryGetLValue(const class AstExpr& expr);
// Utility function: breaks down an LValue to get at the Symbol, and reverses the vector of keys.
std::pair<Symbol, std::vector<std::string>> getFullName(const LValue& lvalue);
std::string toString(const LValue& lvalue);
template<typename T>
const T* get(const LValue& lvalue)
{
return get_if<T>(&lvalue);
}
// Key is a stringified encoding of an LValue.
using RefinementMap = std::map<std::string, TypeId>;
void merge(RefinementMap& l, const RefinementMap& r, std::function<TypeId(TypeId, TypeId)> f);
void addRefinement(RefinementMap& refis, const LValue& lvalue, TypeId ty);
struct TruthyPredicate;
struct IsAPredicate;
struct TypeGuardPredicate;
@ -87,11 +55,7 @@ struct AndPredicate
PredicateVec lhs;
PredicateVec rhs;
AndPredicate(PredicateVec&& lhs, PredicateVec&& rhs)
: lhs(std::move(lhs))
, rhs(std::move(rhs))
{
}
AndPredicate(PredicateVec&& lhs, PredicateVec&& rhs);
};
struct OrPredicate
@ -99,11 +63,7 @@ struct OrPredicate
PredicateVec lhs;
PredicateVec rhs;
OrPredicate(PredicateVec&& lhs, PredicateVec&& rhs)
: lhs(std::move(lhs))
, rhs(std::move(rhs))
{
}
OrPredicate(PredicateVec&& lhs, PredicateVec&& rhs);
};
struct NotPredicate
@ -111,6 +71,19 @@ struct NotPredicate
PredicateVec predicates;
};
// Outside definition works around clang 15 issue where vector instantiation is triggered while Predicate is still incomplete
inline AndPredicate::AndPredicate(PredicateVec&& lhs, PredicateVec&& rhs)
: lhs(std::move(lhs))
, rhs(std::move(rhs))
{
}
inline OrPredicate::OrPredicate(PredicateVec&& lhs, PredicateVec&& rhs)
: lhs(std::move(lhs))
, rhs(std::move(rhs))
{
}
template<typename T>
const T* get(const Predicate& predicate)
{

View file

@ -1,14 +1,34 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/TypeVar.h"
#include "Luau/TypeFwd.h"
#include "Luau/DenseHash.h"
#include "Luau/Unifiable.h"
#include <vector>
#include <optional>
namespace Luau
{
struct Module;
using ModulePtr = std::shared_ptr<Module>;
struct TypeArena;
struct Scope;
void quantify(ModulePtr module, TypeId ty, TypeLevel level);
void quantify(TypeId ty, TypeLevel level);
// TODO: This is eerily similar to the pattern that NormalizedExternType
// implements. We could, and perhaps should, merge them together.
template<typename K, typename V>
struct OrderedMap
{
std::vector<K> keys;
DenseHashMap<K, V> pairings{nullptr};
void push(K k, V v)
{
keys.push_back(k);
pairings[k] = v;
}
};
} // namespace Luau

View file

@ -2,12 +2,22 @@
#pragma once
#include "Luau/Common.h"
#include "Luau/Error.h"
#include <stdexcept>
#include <exception>
namespace Luau
{
struct RecursionLimitException : public InternalCompilerError
{
RecursionLimitException()
: InternalCompilerError("Internal recursion counter limit exceeded")
{
}
};
struct RecursionCounter
{
RecursionCounter(int* count)
@ -22,7 +32,7 @@ struct RecursionCounter
--(*count);
}
private:
protected:
int* count;
};
@ -32,7 +42,9 @@ struct RecursionLimiter : RecursionCounter
: RecursionCounter(count)
{
if (limit > 0 && *count > limit)
throw std::runtime_error("Internal recursion counter limit exceeded");
{
throw RecursionLimitException();
}
}
};

View file

@ -0,0 +1,79 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/NotNull.h"
#include "Luau/TypedAllocator.h"
#include "Luau/Variant.h"
#include "Luau/TypeFwd.h"
namespace Luau
{
struct RefinementKey;
using DefId = NotNull<const struct Def>;
struct Variadic;
struct Negation;
struct Conjunction;
struct Disjunction;
struct Equivalence;
struct Proposition;
using Refinement = Variant<Variadic, Negation, Conjunction, Disjunction, Equivalence, Proposition>;
using RefinementId = Refinement*; // Can and most likely is nullptr.
struct Variadic
{
std::vector<RefinementId> refinements;
};
struct Negation
{
RefinementId refinement;
};
struct Conjunction
{
RefinementId lhs;
RefinementId rhs;
};
struct Disjunction
{
RefinementId lhs;
RefinementId rhs;
};
struct Equivalence
{
RefinementId lhs;
RefinementId rhs;
};
struct Proposition
{
const RefinementKey* key;
TypeId discriminantTy;
bool implicitFromCall;
};
template<typename T>
const T* get(RefinementId refinement)
{
return get_if<T>(refinement);
}
struct RefinementArena
{
RefinementId variadic(const std::vector<RefinementId>& refis);
RefinementId negation(RefinementId refinement);
RefinementId conjunction(RefinementId lhs, RefinementId rhs);
RefinementId disjunction(RefinementId lhs, RefinementId rhs);
RefinementId equivalence(RefinementId lhs, RefinementId rhs);
RefinementId proposition(const RefinementKey* key, TypeId discriminantTy);
RefinementId implicitProposition(const RefinementKey* key, TypeId discriminantTy);
private:
TypedAllocator<Refinement> allocator;
};
} // namespace Luau

View file

@ -6,20 +6,19 @@
#include "Luau/Location.h"
#include <string>
#include <vector>
namespace Luau
{
class AstStat;
class AstExpr;
class AstNode;
class AstStatBlock;
struct AstLocal;
struct RequireTraceResult
{
DenseHashMap<const AstExpr*, ModuleInfo> exprs{nullptr};
DenseHashMap<const AstNode*, ModuleInfo> exprs{nullptr};
std::vector<std::pair<ModuleName, Location>> requires;
std::vector<std::pair<ModuleName, Location>> requireList;
};
RequireTraceResult traceRequires(FileResolver* fileResolver, AstStatBlock* root, const ModuleName& currentModuleName);

View file

@ -1,8 +1,14 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Def.h"
#include "Luau/LValue.h"
#include "Luau/Location.h"
#include "Luau/TypeVar.h"
#include "Luau/NotNull.h"
#include "Luau/Type.h"
#include "Luau/DenseHash.h"
#include "Luau/Symbol.h"
#include "Luau/Unifiable.h"
#include <unordered_map>
#include <optional>
@ -29,39 +35,89 @@ struct Scope
explicit Scope(TypePackId returnType); // root scope
explicit Scope(const ScopePtr& parent, int subLevel = 0); // child scope. Parent must not be nullptr.
const ScopePtr parent; // null for the root
ScopePtr parent; // null for the root
// All the children of this scope.
std::vector<NotNull<Scope>> children;
std::unordered_map<Symbol, Binding> bindings;
TypePackId returnType;
bool breakOk = false;
TypePackId returnType = nullptr;
std::optional<TypePackId> varargPack;
TypeLevel level;
Location location; // the spanning location associated with this scope
std::unordered_map<Name, TypeFun> exportedTypeBindings;
std::unordered_map<Name, TypeFun> privateTypeBindings;
std::unordered_map<Name, Location> typeAliasLocations;
std::unordered_map<Name, Location> typeAliasNameLocations;
std::unordered_map<Name, ModuleName> importedModules; // Mapping from the name in the require statement to the internal moduleName.
std::unordered_map<Name, std::unordered_map<Name, TypeFun>> importedTypeBindings;
std::optional<TypeId> lookup(const Symbol& name);
DenseHashSet<Name> builtinTypeNames{""};
void addBuiltinTypeBinding(const Name& name, const TypeFun& tyFun);
std::optional<TypeFun> lookupType(const Name& name);
std::optional<TypeFun> lookupImportedType(const Name& moduleAlias, const Name& name);
std::optional<TypeId> lookup(Symbol sym) const;
std::optional<TypeId> lookupUnrefinedType(DefId def) const;
std::optional<TypeId> lookupRValueRefinementType(DefId def) const;
std::optional<TypeId> lookup(DefId def) const;
std::optional<std::pair<TypeId, Scope*>> lookupEx(DefId def);
std::optional<std::pair<Binding*, Scope*>> lookupEx(Symbol sym);
std::optional<TypeFun> lookupType(const Name& name) const;
std::optional<TypeFun> lookupImportedType(const Name& moduleAlias, const Name& name) const;
std::unordered_map<Name, TypePackId> privateTypePackBindings;
std::optional<TypePackId> lookupPack(const Name& name);
std::optional<TypePackId> lookupPack(const Name& name) const;
// WARNING: This function linearly scans for a string key of equal value! It is thus O(n**2)
std::optional<Binding> linearSearchForBinding(const std::string& name, bool traverseScopeChain = true);
std::optional<Binding> linearSearchForBinding(const std::string& name, bool traverseScopeChain = true) const;
std::optional<std::pair<Symbol, Binding>> linearSearchForBindingPair(const std::string& name, bool traverseScopeChain) const;
RefinementMap refinements;
// This can be viewed as the "unrefined" type of each binding.
DenseHashMap<const Def*, TypeId> lvalueTypes{nullptr};
// Luau values are routinely refined more narrowly than their actual
// inferred type through control flow statements. We retain those refined
// types here.
DenseHashMap<const Def*, TypeId> rvalueRefinements{nullptr};
void inheritAssignments(const ScopePtr& childScope);
void inheritRefinements(const ScopePtr& childScope);
// Track globals that should emit warnings during type checking.
DenseHashSet<std::string> globalsToWarn{""};
bool shouldWarnGlobal(std::string name) const;
// For mutually recursive type aliases, it's important that
// they use the same types for the same names.
// For instance, in `type Tree<T> { data: T, children: Forest<T> } type Forest<T> = {Tree<T>}`
// we need that the generic type `T` in both cases is the same, so we use a cache.
std::unordered_map<Name, TypeId> typeAliasTypeParameters;
std::unordered_map<Name, TypePackId> typeAliasTypePackParameters;
std::optional<std::vector<TypeId>> interiorFreeTypes;
std::optional<std::vector<TypePackId>> interiorFreeTypePacks;
};
// Returns true iff the left scope encloses the right scope. A Scope* equal to
// nullptr is considered to be the outermost-possible scope.
bool subsumesStrict(Scope* left, Scope* right);
// Returns true if the left scope encloses the right scope, or if they are the
// same scope. As in subsumesStrict(), nullptr is considered to be the
// outermost-possible scope.
bool subsumes(Scope* left, Scope* right);
inline Scope* max(Scope* left, Scope* right)
{
if (subsumes(left, right))
return right;
else
return left;
}
} // namespace Luau

194
Analysis/include/Luau/Set.h Normal file
View file

@ -0,0 +1,194 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Common.h"
#include "Luau/DenseHash.h"
LUAU_FASTFLAG(LuauSolverV2)
namespace Luau
{
template<typename T>
using SetHashDefault = std::conditional_t<std::is_pointer_v<T>, DenseHashPointer, std::hash<T>>;
// This is an implementation of `unordered_set` using `DenseHashMap<T, bool>` to support erasure.
// This lets us work around `DenseHashSet` limitations and get a more traditional set interface.
template<typename T, typename Hash = SetHashDefault<T>>
class Set
{
private:
using Impl = DenseHashMap<T, bool, Hash>;
Impl mapping;
size_t entryCount = 0;
public:
class const_iterator;
using iterator = const_iterator;
Set(const T& empty_key)
: mapping{empty_key}
{
}
bool insert(const T& element)
{
bool& entry = mapping[element];
bool fresh = !entry;
if (fresh)
{
entry = true;
entryCount++;
}
return fresh;
}
template<class Iterator>
void insert(Iterator begin, Iterator end)
{
for (Iterator it = begin; it != end; ++it)
insert(*it);
}
void erase(T&& element)
{
bool& entry = mapping[element];
if (entry)
{
entry = false;
entryCount--;
}
}
void erase(const T& element)
{
bool& entry = mapping[element];
if (entry)
{
entry = false;
entryCount--;
}
}
void clear()
{
mapping.clear();
entryCount = 0;
}
size_t size() const
{
return entryCount;
}
bool empty() const
{
return entryCount == 0;
}
size_t count(const T& element) const
{
const bool* entry = mapping.find(element);
return (entry && *entry) ? 1 : 0;
}
bool contains(const T& element) const
{
return count(element) != 0;
}
const_iterator begin() const
{
return const_iterator(mapping.begin(), mapping.end());
}
const_iterator end() const
{
return const_iterator(mapping.end(), mapping.end());
}
bool operator==(const Set<T>& there) const
{
// if the sets are unequal sizes, then they cannot possibly be equal.
if (size() != there.size())
return false;
// otherwise, we'll need to check that every element we have here is in `there`.
for (auto [elem, present] : mapping)
{
// if it's not, we'll return `false`
if (present && there.contains(elem))
return false;
}
// otherwise, we've proven the two equal!
return true;
}
class const_iterator
{
public:
using value_type = T;
using reference = T&;
using pointer = T*;
using difference_type = ptrdiff_t;
using iterator_category = std::forward_iterator_tag;
const_iterator(typename Impl::const_iterator impl_, typename Impl::const_iterator end_)
: impl(impl_)
, end(end_)
{
while (impl != end && impl->second == false)
++impl;
}
const T& operator*() const
{
return impl->first;
}
const T* operator->() const
{
return &impl->first;
}
bool operator==(const const_iterator& other) const
{
return impl == other.impl;
}
bool operator!=(const const_iterator& other) const
{
return impl != other.impl;
}
const_iterator& operator++()
{
do
{
impl++;
} while (impl != end && impl->second == false);
// keep iterating past pairs where the value is `false`
return *this;
}
const_iterator operator++(int)
{
const_iterator res = *this;
++*this;
return res;
}
private:
typename Impl::const_iterator impl;
typename Impl::const_iterator end;
};
};
} // namespace Luau

View file

@ -0,0 +1,41 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/DenseHash.h"
#include "Luau/NotNull.h"
#include "Luau/TypeFwd.h"
#include <set>
namespace Luau
{
struct TypeArena;
struct SimplifyResult
{
TypeId result;
DenseHashSet<TypeId> blockedTypes;
};
SimplifyResult simplifyIntersection(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, TypeId left, TypeId right);
SimplifyResult simplifyIntersection(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, std::set<TypeId> parts);
SimplifyResult simplifyUnion(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, TypeId left, TypeId right);
SimplifyResult simplifyIntersectWithTruthy(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, TypeId target);
SimplifyResult simplifyIntersectWithFalsy(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, TypeId target);
enum class Relation
{
Disjoint, // No A is a B or vice versa
Coincident, // Every A is in B and vice versa
Intersects, // Some As are in B and some Bs are in A. ex (number | string) <-> (string | boolean)
Subset, // Every A is in B
Superset, // Every B is in A
};
Relation relate(TypeId left, TypeId right);
} // namespace Luau

View file

@ -1,10 +1,8 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Module.h"
#include "Luau/ModuleResolver.h"
#include "Luau/TypePack.h"
#include "Luau/TypeVar.h"
#include "Luau/TypeArena.h"
#include "Luau/TypeFwd.h"
#include "Luau/DenseHash.h"
// We provide an implementation of substitution on types,
@ -55,6 +53,8 @@
namespace Luau
{
struct TxnLog;
enum class TarjanResult
{
TooManyChildren,
@ -68,36 +68,50 @@ struct TarjanWorklistVertex
int lastEdge;
};
struct TarjanNode
{
TypeId ty;
TypePackId tp;
bool onStack;
bool dirty;
// Tarjan calculates the lowlink for each vertex,
// which is the lowest ancestor index reachable from the vertex.
int lowlink;
};
// Tarjan's algorithm for finding the SCCs in a cyclic structure.
// https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
struct Tarjan
{
Tarjan();
virtual ~Tarjan() = default;
// Vertices (types and type packs) are indexed, using pre-order traversal.
DenseHashMap<TypeId, int> typeToIndex{nullptr};
DenseHashMap<TypePackId, int> packToIndex{nullptr};
std::vector<TypeId> indexToType;
std::vector<TypePackId> indexToPack;
std::vector<TarjanNode> nodes;
// Tarjan keeps a stack of vertices where we're still in the process
// of finding their SCC.
std::vector<int> stack;
std::vector<bool> onStack;
// Tarjan calculates the lowlink for each vertex,
// which is the lowest ancestor index reachable from the vertex.
std::vector<int> lowlink;
int childCount = 0;
int childLimit = 0;
// This should never be null; ensure you initialize it before calling
// substitution methods.
const TxnLog* log = nullptr;
std::vector<TypeId> edgesTy;
std::vector<TypePackId> edgesTp;
std::vector<TarjanWorklistVertex> worklist;
// This is hot code, so we optimize recursion to a stack.
TarjanResult loop();
// Clear the state
void clear();
// Find or create the index for a vertex.
// Return a boolean which is `true` if it's a freshly created index.
std::pair<int, bool> indexify(TypeId ty);
@ -108,33 +122,21 @@ struct Tarjan
void visitChildren(TypePackId tp, int index);
void visitChild(TypeId ty);
void visitChild(TypePackId ty);
void visitChild(TypePackId tp);
template<typename Ty>
void visitChild(std::optional<Ty> ty)
{
if (ty)
visitChild(*ty);
}
// Visit the root vertex.
TarjanResult visitRoot(TypeId ty);
TarjanResult visitRoot(TypePackId ty);
TarjanResult visitRoot(TypePackId tp);
// Each subclass gets called back once for each edge,
// and once for each SCC.
virtual void visitEdge(int index, int parentIndex) {}
virtual void visitSCC(int index) {}
// Each subclass can decide to ignore some nodes.
virtual bool ignoreChildren(TypeId ty)
{
return false;
}
virtual bool ignoreChildren(TypePackId ty)
{
return false;
}
};
// We use Tarjan to calculate dirty bits. We set `dirty[i]` true
// if the vertex with index `i` can reach a dirty vertex.
struct FindDirty : Tarjan
{
std::vector<bool> dirty;
// Used to reuse the object for a new operation
void clearTarjan(const TxnLog* log);
// Get/set the dirty bit for an index (grows the vector if needed)
bool getDirty(int index);
@ -145,8 +147,16 @@ struct FindDirty : Tarjan
TarjanResult findDirty(TypePackId t);
// We find dirty vertices using Tarjan
void visitEdge(int index, int parentIndex) override;
void visitSCC(int index) override;
void visitEdge(int index, int parentIndex);
void visitSCC(int index);
// Each subclass can decide to ignore some nodes.
virtual bool ignoreChildren(TypeId ty);
virtual bool ignoreChildren(TypePackId ty);
// Some subclasses might ignore children visit, but not other actions like replacing the children
virtual bool ignoreChildrenVisit(TypeId ty);
virtual bool ignoreChildrenVisit(TypePackId ty);
// Subclasses should say which vertices are dirty,
// and what to do with dirty vertices.
@ -158,19 +168,47 @@ struct FindDirty : Tarjan
// And finally substitution, which finds all the reachable dirty vertices
// and replaces them with clean ones.
struct Substitution : FindDirty
struct Substitution : Tarjan
{
ModulePtr currentModule;
protected:
explicit Substitution(TypeArena* arena);
Substitution(const TxnLog* log_, TypeArena* arena);
/*
* By default, Substitution assumes that the types produced by clean() are
* freshly allocated types that are safe to mutate.
*
* If your clean() implementation produces a type that is not safe to
* mutate, you must call dontTraverseInto on this type (or type pack) to
* prevent Substitution from attempting to perform substitutions within the
* cleaned type.
*
* See the test weird_cyclic_instantiation for an example.
*/
void dontTraverseInto(TypeId ty);
void dontTraverseInto(TypePackId tp);
public:
TypeArena* arena;
DenseHashMap<TypeId, TypeId> newTypes{nullptr};
DenseHashMap<TypePackId, TypePackId> newPacks{nullptr};
DenseHashSet<TypeId> replacedTypes{nullptr};
DenseHashSet<TypePackId> replacedTypePacks{nullptr};
DenseHashSet<TypeId> noTraverseTypes{nullptr};
DenseHashSet<TypePackId> noTraverseTypePacks{nullptr};
std::optional<TypeId> substitute(TypeId ty);
std::optional<TypePackId> substitute(TypePackId tp);
void resetState(const TxnLog* log, TypeArena* arena);
TypeId replace(TypeId ty);
TypePackId replace(TypePackId tp);
void replaceChildren(TypeId ty);
void replaceChildren(TypePackId tp);
TypeId clone(TypeId ty);
TypePackId clone(TypePackId tp);
@ -182,17 +220,23 @@ struct Substitution : FindDirty
virtual TypeId clean(TypeId ty) = 0;
virtual TypePackId clean(TypePackId tp) = 0;
protected:
// Helper functions to create new types (used by subclasses)
template<typename T>
TypeId addType(const T& tv)
TypeId addType(T tv)
{
return currentModule->internalTypes.addType(tv);
return arena->addType(std::move(tv));
}
template<typename T>
TypePackId addTypePack(const T& tp)
TypePackId addTypePack(T tp)
{
return currentModule->internalTypes.addTypePack(TypePackVar{tp});
return arena->addTypePack(TypePackVar{std::move(tp)});
}
private:
template<typename Ty>
std::optional<Ty> replace(std::optional<Ty> ty);
};
} // namespace Luau

View file

@ -0,0 +1,313 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/DenseHash.h"
#include "Luau/EqSatSimplification.h"
#include "Luau/Set.h"
#include "Luau/TypeCheckLimits.h"
#include "Luau/TypeFunction.h"
#include "Luau/TypeFwd.h"
#include "Luau/TypePairHash.h"
#include "Luau/TypePath.h"
#include <vector>
#include <optional>
namespace Luau
{
template<typename A, typename B>
struct TryPair;
struct InternalErrorReporter;
class TypeIds;
class Normalizer;
struct NormalizedExternType;
struct NormalizedFunctionType;
struct NormalizedStringType;
struct NormalizedType;
struct Property;
struct Scope;
struct TableIndexer;
struct TypeArena;
struct TypeCheckLimits;
enum class SubtypingVariance
{
// Used for an empty key. Should never appear in actual code.
Invalid,
Covariant,
// This is used to identify cases where we have a covariant + a
// contravariant reason and we need to merge them.
Contravariant,
Invariant,
};
struct SubtypingReasoning
{
// The path, relative to the _root subtype_, where subtyping failed.
Path subPath;
// The path, relative to the _root supertype_, where subtyping failed.
Path superPath;
SubtypingVariance variance = SubtypingVariance::Covariant;
bool operator==(const SubtypingReasoning& other) const;
};
struct SubtypingReasoningHash
{
size_t operator()(const SubtypingReasoning& r) const;
};
using SubtypingReasonings = DenseHashSet<SubtypingReasoning, SubtypingReasoningHash>;
static const SubtypingReasoning kEmptyReasoning = SubtypingReasoning{TypePath::kEmpty, TypePath::kEmpty, SubtypingVariance::Invalid};
struct SubtypingResult
{
bool isSubtype = false;
bool normalizationTooComplex = false;
bool isCacheable = true;
ErrorVec errors;
/// The reason for isSubtype to be false. May not be present even if
/// isSubtype is false, depending on the input types.
SubtypingReasonings reasoning{kEmptyReasoning};
SubtypingResult& andAlso(const SubtypingResult& other);
SubtypingResult& orElse(const SubtypingResult& other);
SubtypingResult& withBothComponent(TypePath::Component component);
SubtypingResult& withSuperComponent(TypePath::Component component);
SubtypingResult& withSubComponent(TypePath::Component component);
SubtypingResult& withBothPath(TypePath::Path path);
SubtypingResult& withSubPath(TypePath::Path path);
SubtypingResult& withSuperPath(TypePath::Path path);
SubtypingResult& withErrors(ErrorVec& err);
SubtypingResult& withError(TypeError err);
// Only negates the `isSubtype`.
static SubtypingResult negate(const SubtypingResult& result);
static SubtypingResult all(const std::vector<SubtypingResult>& results);
static SubtypingResult any(const std::vector<SubtypingResult>& results);
};
struct SubtypingEnvironment
{
struct GenericBounds
{
DenseHashSet<TypeId> lowerBound{nullptr};
DenseHashSet<TypeId> upperBound{nullptr};
};
/* For nested subtyping relationship tests of mapped generic bounds, we keep the outer environment immutable */
SubtypingEnvironment* parent = nullptr;
/// Applies `mappedGenerics` to the given type.
/// This is used specifically to substitute for generics in type function instances.
std::optional<TypeId> applyMappedGenerics(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, TypeId ty);
const TypeId* tryFindSubstitution(TypeId ty) const;
const SubtypingResult* tryFindSubtypingResult(std::pair<TypeId, TypeId> subAndSuper) const;
bool containsMappedType(TypeId ty) const;
bool containsMappedPack(TypePackId tp) const;
GenericBounds& getMappedTypeBounds(TypeId ty);
TypePackId* getMappedPackBounds(TypePackId tp);
/*
* When we encounter a generic over the course of a subtyping test, we need
* to tentatively map that generic onto a type on the other side.
*/
DenseHashMap<TypeId, GenericBounds> mappedGenerics{nullptr};
DenseHashMap<TypePackId, TypePackId> mappedGenericPacks{nullptr};
/*
* See the test cyclic_tables_are_assumed_to_be_compatible_with_extern_types for
* details.
*
* An empty value is equivalent to a nonexistent key.
*/
DenseHashMap<TypeId, TypeId> substitutions{nullptr};
DenseHashMap<std::pair<TypeId, TypeId>, SubtypingResult, TypePairHash> ephemeralCache{{}};
};
struct Subtyping
{
NotNull<BuiltinTypes> builtinTypes;
NotNull<TypeArena> arena;
NotNull<Simplifier> simplifier;
NotNull<Normalizer> normalizer;
NotNull<TypeFunctionRuntime> typeFunctionRuntime;
NotNull<InternalErrorReporter> iceReporter;
TypeCheckLimits limits;
enum class Variance
{
Covariant,
Contravariant
};
Variance variance = Variance::Covariant;
using SeenSet = Set<std::pair<TypeId, TypeId>, TypePairHash>;
SeenSet seenTypes{{}};
Subtyping(
NotNull<BuiltinTypes> builtinTypes,
NotNull<TypeArena> typeArena,
NotNull<Simplifier> simplifier,
NotNull<Normalizer> normalizer,
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
NotNull<InternalErrorReporter> iceReporter
);
Subtyping(const Subtyping&) = delete;
Subtyping& operator=(const Subtyping&) = delete;
Subtyping(Subtyping&&) = default;
Subtyping& operator=(Subtyping&&) = default;
// Only used by unit tests to test that the cache works.
const DenseHashMap<std::pair<TypeId, TypeId>, SubtypingResult, TypePairHash>& peekCache() const
{
return resultCache;
}
// TODO cache
// TODO cyclic types
// TODO recursion limits
SubtypingResult isSubtype(TypeId subTy, TypeId superTy, NotNull<Scope> scope);
SubtypingResult isSubtype(TypePackId subTy, TypePackId superTy, NotNull<Scope> scope);
private:
DenseHashMap<std::pair<TypeId, TypeId>, SubtypingResult, TypePairHash> resultCache{{}};
SubtypingResult cache(SubtypingEnvironment& env, SubtypingResult res, TypeId subTy, TypeId superTy);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypeId subTy, TypeId superTy, NotNull<Scope> scope);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypePackId subTp, TypePackId superTp, NotNull<Scope> scope);
template<typename SubTy, typename SuperTy>
SubtypingResult isContravariantWith(SubtypingEnvironment& env, SubTy&& subTy, SuperTy&& superTy, NotNull<Scope> scope);
template<typename SubTy, typename SuperTy>
SubtypingResult isInvariantWith(SubtypingEnvironment& env, SubTy&& subTy, SuperTy&& superTy, NotNull<Scope> scope);
template<typename SubTy, typename SuperTy>
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TryPair<const SubTy*, const SuperTy*>& pair, NotNull<Scope> scope);
template<typename SubTy, typename SuperTy>
SubtypingResult isContravariantWith(SubtypingEnvironment& env, const TryPair<const SubTy*, const SuperTy*>& pair, NotNull<Scope>);
template<typename SubTy, typename SuperTy>
SubtypingResult isInvariantWith(SubtypingEnvironment& env, const TryPair<const SubTy*, const SuperTy*>& pair, NotNull<Scope>);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const UnionType* superUnion, NotNull<Scope> scope);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const UnionType* subUnion, TypeId superTy, NotNull<Scope> scope);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const IntersectionType* superIntersection, NotNull<Scope> scope);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const IntersectionType* subIntersection, TypeId superTy, NotNull<Scope> scope);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const NegationType* subNegation, TypeId superTy, NotNull<Scope> scope);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeId subTy, const NegationType* superNegation, NotNull<Scope> scope);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const PrimitiveType* subPrim, const PrimitiveType* superPrim, NotNull<Scope> scope);
SubtypingResult isCovariantWith(
SubtypingEnvironment& env,
const SingletonType* subSingleton,
const PrimitiveType* superPrim,
NotNull<Scope> scope
);
SubtypingResult isCovariantWith(
SubtypingEnvironment& env,
const SingletonType* subSingleton,
const SingletonType* superSingleton,
NotNull<Scope> scope
);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TableType* subTable, const TableType* superTable, NotNull<Scope> scope);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const MetatableType* superMt, NotNull<Scope> scope);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const TableType* superTable, NotNull<Scope> scope);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const ExternType* subExternType, const ExternType* superExternType, NotNull<Scope> scope);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const ExternType* subExternType, TypeId superTy, const TableType* superTable, NotNull<Scope>);
SubtypingResult isCovariantWith(
SubtypingEnvironment& env,
const FunctionType* subFunction,
const FunctionType* superFunction,
NotNull<Scope> scope
);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TableType* subTable, const PrimitiveType* superPrim, NotNull<Scope> scope);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const PrimitiveType* subPrim, const TableType* superTable, NotNull<Scope> scope);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const SingletonType* subSingleton, const TableType* superTable, NotNull<Scope> scope);
SubtypingResult isCovariantWith(
SubtypingEnvironment& env,
const TableIndexer& subIndexer,
const TableIndexer& superIndexer,
NotNull<Scope> scope
);
SubtypingResult
isCovariantWith(SubtypingEnvironment& env, const Property& subProperty, const Property& superProperty, const std::string& name, NotNull<Scope>);
SubtypingResult isCovariantWith(
SubtypingEnvironment& env,
const std::shared_ptr<const NormalizedType>& subNorm,
const std::shared_ptr<const NormalizedType>& superNorm,
NotNull<Scope> scope
);
SubtypingResult isCovariantWith(
SubtypingEnvironment& env,
const NormalizedExternType& subExternType,
const NormalizedExternType& superExternType,
NotNull<Scope> scope
);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const NormalizedExternType& subExternType, const TypeIds& superTables, NotNull<Scope> scope);
SubtypingResult isCovariantWith(
SubtypingEnvironment& env,
const NormalizedStringType& subString,
const NormalizedStringType& superString,
NotNull<Scope> scope
);
SubtypingResult isCovariantWith(
SubtypingEnvironment& env,
const NormalizedStringType& subString,
const TypeIds& superTables,
NotNull<Scope> scope
);
SubtypingResult
isCovariantWith(SubtypingEnvironment& env, const NormalizedFunctionType& subFunction, const NormalizedFunctionType& superFunction, NotNull<Scope>);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeIds& subTypes, const TypeIds& superTypes, NotNull<Scope> scope);
SubtypingResult isCovariantWith(
SubtypingEnvironment& env,
const VariadicTypePack* subVariadic,
const VariadicTypePack* superVariadic,
NotNull<Scope> scope
);
SubtypingResult isCovariantWith(
SubtypingEnvironment& env,
const TypeFunctionInstanceType* subFunctionInstance,
const TypeId superTy,
NotNull<Scope> scope
);
SubtypingResult isCovariantWith(
SubtypingEnvironment& env,
const TypeId subTy,
const TypeFunctionInstanceType* superFunctionInstance,
NotNull<Scope> scope
);
bool bindGeneric(SubtypingEnvironment& env, TypeId subTp, TypeId superTp);
bool bindGeneric(SubtypingEnvironment& env, TypePackId subTp, TypePackId superTp);
template<typename T, typename Container>
TypeId makeAggregateType(const Container& container, TypeId orElse);
std::pair<TypeId, ErrorVec> handleTypeFunctionReductionResult(const TypeFunctionInstanceType* functionInstance, NotNull<Scope> scope);
[[noreturn]] void unexpected(TypeId ty);
[[noreturn]] void unexpected(TypePackId tp);
};
} // namespace Luau

View file

@ -9,7 +9,6 @@
namespace Luau
{
// TODO Rename this to Name once the old type alias is gone.
struct Symbol
{
Symbol()
@ -30,18 +29,19 @@ struct Symbol
{
}
template<typename T>
Symbol(const T&) = delete;
AstLocal* local;
AstName global;
bool operator==(const Symbol& rhs) const
explicit operator bool() const
{
if (local)
return local == rhs.local;
if (global.value)
return rhs.global.value && global == rhs.global.value; // Subtlety: AstName::operator==(const char*) uses strcmp, not pointer identity.
return false;
return local != nullptr || global.value != nullptr;
}
bool operator==(const Symbol& rhs) const;
bool operator!=(const Symbol& rhs) const
{
return !(*this == rhs);
@ -55,8 +55,8 @@ struct Symbol
return global < rhs.global;
else if (local)
return true;
else
return false;
return false;
}
AstName astName() const

View file

@ -0,0 +1,32 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/DenseHash.h"
#include "Luau/NotNull.h"
#include "Luau/TypeFwd.h"
#include <vector>
namespace Luau
{
struct TypeArena;
struct BuiltinTypes;
struct Unifier2;
struct Subtyping;
class AstExpr;
TypeId matchLiteralType(
NotNull<DenseHashMap<const AstExpr*, TypeId>> astTypes,
NotNull<DenseHashMap<const AstExpr*, TypeId>> astExpectedTypes,
NotNull<BuiltinTypes> builtinTypes,
NotNull<TypeArena> arena,
NotNull<Unifier2> unifier,
NotNull<Subtyping> subtyping,
TypeId expectedType,
TypeId exprType,
const AstExpr* expr,
std::vector<TypeId>& toBlock
);
} // namespace Luau

View file

@ -0,0 +1,27 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Common.h"
#include "Luau/TypeFwd.h"
#include <string>
namespace Luau
{
struct ToDotOptions
{
bool showPointers = true; // Show pointer value in the node label
bool duplicatePrimitives = true; // Display primitive types and 'any' as separate nodes
};
std::string toDot(TypeId ty, const ToDotOptions& opts);
std::string toDot(TypePackId tp, const ToDotOptions& opts);
std::string toDot(TypeId ty);
std::string toDot(TypePackId tp);
void dumpDot(TypeId ty);
void dumpDot(TypePackId tp);
} // namespace Luau

View file

@ -2,12 +2,13 @@
#pragma once
#include "Luau/Common.h"
#include "Luau/TypeVar.h"
#include "Luau/TypeFwd.h"
#include <unordered_map>
#include <optional>
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
LUAU_FASTINT(LuauTableTypeMaximumStringifierLength)
LUAU_FASTINT(LuauTypeMaximumStringifierLength)
@ -15,29 +16,47 @@ LUAU_FASTINT(LuauTypeMaximumStringifierLength)
namespace Luau
{
class AstExpr;
struct Scope;
struct Constraint;
struct Position;
struct Location;
struct ToStringNameMap
{
std::unordered_map<TypeId, std::string> typeVars;
std::unordered_map<TypeId, std::string> types;
std::unordered_map<TypePackId, std::string> typePacks;
};
struct ToStringOptions
{
ToStringOptions(bool exhaustive = false)
: exhaustive(exhaustive)
{
}
bool exhaustive = false; // If true, we produce complete output rather than comprehensible output
bool useLineBreaks = false; // If true, we insert new lines to separate long results such as table entries/metatable.
bool functionTypeArguments = false; // If true, output function type argument names when they are available
bool hideTableKind = false; // If true, all tables will be surrounded with plain '{}'
bool hideNamedFunctionTypeParameters = false; // If true, type parameters of functions will be hidden at top-level.
size_t maxTableLength = size_t(FInt::LuauTableTypeMaximumStringifierLength); // Only applied to TableTypeVars
bool hideFunctionSelfArgument = false; // If true, `self: X` will be omitted from the function signature if the function has self
bool hideTableAliasExpansions = false; // If true, all table aliases will not be expanded
bool useQuestionMarks = true; // If true, use a postfix ? for options, else write them out as unions that include nil.
size_t maxTableLength = size_t(FInt::LuauTableTypeMaximumStringifierLength); // Only applied to TableTypes
size_t maxTypeLength = size_t(FInt::LuauTypeMaximumStringifierLength);
std::optional<ToStringNameMap> nameMap;
size_t compositeTypesSingleLineLimit = 5; // The number of type elements permitted on a single line when printing type unions/intersections
ToStringNameMap nameMap;
std::shared_ptr<Scope> scope; // If present, module names will be added and types that are not available in scope will be marked as 'invalid'
std::vector<std::string> namedFunctionOverrideArgNames; // If present, named function argument names will be overridden
};
struct ToStringResult
{
std::string name;
ToStringNameMap nameMap;
bool invalid = false;
bool error = false;
@ -45,11 +64,24 @@ struct ToStringResult
bool truncated = false;
};
ToStringResult toStringDetailed(TypeId ty, const ToStringOptions& opts = {});
ToStringResult toStringDetailed(TypePackId ty, const ToStringOptions& opts = {});
ToStringResult toStringDetailed(TypeId ty, ToStringOptions& opts);
ToStringResult toStringDetailed(TypePackId ty, ToStringOptions& opts);
std::string toString(TypeId ty, const ToStringOptions& opts);
std::string toString(TypePackId ty, const ToStringOptions& opts);
std::string toString(TypeId ty, ToStringOptions& opts);
std::string toString(TypePackId ty, ToStringOptions& opts);
// These overloads are selected when a temporary ToStringOptions is passed. (eg
// via an initializer list)
inline std::string toString(TypePackId ty, ToStringOptions&& opts)
{
// Delegate to the overload (TypePackId, ToStringOptions&)
return toString(ty, opts);
}
inline std::string toString(TypeId ty, ToStringOptions&& opts)
{
// Delegate to the overload (TypeId, ToStringOptions&)
return toString(ty, opts);
}
// These are offered as overloads rather than a default parameter so that they can be easily invoked from within the MSVC debugger.
// You can use them in watch expressions!
@ -62,16 +94,63 @@ inline std::string toString(TypePackId ty)
return toString(ty, ToStringOptions{});
}
std::string toString(const TypeVar& tv, const ToStringOptions& opts = {});
std::string toString(const TypePackVar& tp, const ToStringOptions& opts = {});
std::string toString(const Constraint& c, ToStringOptions& opts);
std::string toStringNamedFunction(const std::string& prefix, const FunctionTypeVar& ftv, ToStringOptions opts = {});
inline std::string toString(const Constraint& c, ToStringOptions&& opts)
{
return toString(c, opts);
}
std::string toString(const Constraint& c);
std::string toString(const Type& tv, ToStringOptions& opts);
std::string toString(const TypePackVar& tp, ToStringOptions& opts);
inline std::string toString(const Type& tv)
{
ToStringOptions opts;
return toString(tv, opts);
}
inline std::string toString(const TypePackVar& tp)
{
ToStringOptions opts;
return toString(tp, opts);
}
std::string toStringNamedFunction(const std::string& funcName, const FunctionType& ftv, ToStringOptions& opts);
inline std::string toStringNamedFunction(const std::string& funcName, const FunctionType& ftv)
{
ToStringOptions opts;
return toStringNamedFunction(funcName, ftv, opts);
}
std::optional<std::string> getFunctionNameAsString(const AstExpr& expr);
// It could be useful to see the text representation of a type during a debugging session instead of exploring the content of the class
// These functions will dump the type to stdout and can be evaluated in Watch/Immediate windows or as gdb/lldb expression
void dump(TypeId ty);
void dump(TypePackId ty);
std::string dump(TypeId ty);
std::string dump(const std::optional<TypeId>& ty);
std::string dump(TypePackId ty);
std::string dump(const std::optional<TypePackId>& ty);
std::string dump(const Constraint& c);
std::string dump(const std::shared_ptr<Scope>& scope, const char* name);
std::string generateName(size_t n);
std::string toString(const Position& position);
std::string toString(const Location& location, int offset = 0, bool useBegin = true);
std::string toString(const TypeOrPack& tyOrTp, ToStringOptions& opts);
inline std::string toString(const TypeOrPack& tyOrTp)
{
ToStringOptions opts{};
return toString(tyOrTp, opts);
}
std::string dump(const TypeOrPack& tyOrTp);
} // namespace Luau

View file

@ -1,37 +1,98 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/TypeVar.h"
#include "Luau/Type.h"
#include "Luau/TypePack.h"
LUAU_FASTFLAG(LuauShareTxnSeen);
#include <memory>
#include <unordered_map>
namespace Luau
{
// Log of where what TypeIds we are rebinding and what they used to be
using TypeOrPackId = const void*;
// Pending state for a Type. Generated by a TxnLog and committed via
// TxnLog::commit.
struct PendingType
{
// The pending Type state.
Type pending;
// On very rare occasions, we need to delete an entry from the TxnLog.
// DenseHashMap does not afford that so we note its deadness here.
bool dead = false;
explicit PendingType(Type state)
: pending(std::move(state))
{
}
};
std::string toString(PendingType* pending);
std::string dump(PendingType* pending);
// Pending state for a TypePackVar. Generated by a TxnLog and committed via
// TxnLog::commit.
struct PendingTypePack
{
// The pending TypePackVar state.
TypePackVar pending;
explicit PendingTypePack(TypePackVar state)
: pending(std::move(state))
{
}
};
std::string toString(PendingTypePack* pending);
std::string dump(PendingTypePack* pending);
template<typename T>
T* getMutable(PendingType* pending)
{
// We use getMutable here because this state is intended to be mutated freely.
return getMutable<T>(&pending->pending);
}
template<typename T>
T* getMutable(PendingTypePack* pending)
{
// We use getMutable here because this state is intended to be mutated freely.
return getMutable<T>(&pending->pending);
}
// Log of what TypeIds we are rebinding, to be committed later.
struct TxnLog
{
TxnLog()
: originalSeenSize(0)
explicit TxnLog()
: typeVarChanges(nullptr)
, typePackChanges(nullptr)
, ownedSeen()
, sharedSeen(&ownedSeen)
{
}
explicit TxnLog(std::vector<std::pair<TypeId, TypeId>>* sharedSeen)
: originalSeenSize(sharedSeen->size())
, ownedSeen()
, sharedSeen(sharedSeen)
explicit TxnLog(TxnLog* parent)
: typeVarChanges(nullptr)
, typePackChanges(nullptr)
, parent(parent)
{
if (parent)
{
sharedSeen = parent->sharedSeen;
}
else
{
sharedSeen = &ownedSeen;
}
}
explicit TxnLog(const std::vector<std::pair<TypeId, TypeId>>& ownedSeen)
: originalSeenSize(ownedSeen.size())
, ownedSeen(ownedSeen)
, sharedSeen(nullptr)
explicit TxnLog(std::vector<std::pair<TypeOrPackId, TypeOrPackId>>* sharedSeen)
: typeVarChanges(nullptr)
, typePackChanges(nullptr)
, sharedSeen(sharedSeen)
{
// This is deprecated!
LUAU_ASSERT(!FFlag::LuauShareTxnSeen);
}
TxnLog(const TxnLog&) = delete;
@ -40,27 +101,211 @@ struct TxnLog
TxnLog(TxnLog&&) = default;
TxnLog& operator=(TxnLog&&) = default;
void operator()(TypeId a);
void operator()(TypePackId a);
void operator()(TableTypeVar* a);
void rollback();
// Gets an empty TxnLog pointer. This is useful for constructs that
// take a TxnLog, like TypePackIterator - use the empty log if you
// don't have a TxnLog to give it.
static const TxnLog* empty();
// Joins another TxnLog onto this one. You should use std::move to avoid
// copying the rhs TxnLog.
//
// If both logs talk about the same type, pack, or table, the rhs takes
// priority.
void concat(TxnLog rhs);
void concatAsIntersections(TxnLog rhs, NotNull<TypeArena> arena);
void concatAsUnion(TxnLog rhs, NotNull<TypeArena> arena);
bool haveSeen(TypeId lhs, TypeId rhs);
// Commits the TxnLog, rebinding all type pointers to their pending states.
// Clears the TxnLog afterwards.
void commit();
// Clears the TxnLog without committing any pending changes.
void clear();
// Computes an inverse of this TxnLog at the current time.
// This method should be called before commit is called in order to give an
// accurate result. Committing the inverse of a TxnLog will undo the changes
// made by commit, assuming the inverse log is accurate.
TxnLog inverse();
bool haveSeen(TypeId lhs, TypeId rhs) const;
void pushSeen(TypeId lhs, TypeId rhs);
void popSeen(TypeId lhs, TypeId rhs);
bool haveSeen(TypePackId lhs, TypePackId rhs) const;
void pushSeen(TypePackId lhs, TypePackId rhs);
void popSeen(TypePackId lhs, TypePackId rhs);
// Queues a type for modification. The original type will not change until commit
// is called. Use pending to get the pending state.
//
// The pointer returned lives until `commit` or `clear` is called.
PendingType* queue(TypeId ty);
// Queues a type pack for modification. The original type pack will not change
// until commit is called. Use pending to get the pending state.
//
// The pointer returned lives until `commit` or `clear` is called.
PendingTypePack* queue(TypePackId tp);
// Returns the pending state of a type, or nullptr if there isn't any. It is important
// to note that this pending state is not transitive: the pending state may reference
// non-pending types freely, so you may need to call pending multiple times to view the
// entire pending state of a type graph.
//
// The pointer returned lives until `commit` or `clear` is called.
PendingType* pending(TypeId ty) const;
// Returns the pending state of a type pack, or nullptr if there isn't any. It is
// important to note that this pending state is not transitive: the pending state may
// reference non-pending types freely, so you may need to call pending multiple times
// to view the entire pending state of a type graph.
//
// The pointer returned lives until `commit` or `clear` is called.
PendingTypePack* pending(TypePackId tp) const;
// Queues a replacement of a type with another type.
//
// The pointer returned lives until `commit` or `clear` is called.
PendingType* replace(TypeId ty, Type replacement);
// Queues a replacement of a type pack with another type pack.
//
// The pointer returned lives until `commit` or `clear` is called.
PendingTypePack* replace(TypePackId tp, TypePackVar replacement);
// Queues a replacement of a table type with another table type that is bound
// to a specific value.
//
// The pointer returned lives until `commit` or `clear` is called.
PendingType* bindTable(TypeId ty, std::optional<TypeId> newBoundTo);
// Queues a replacement of a type with a level with a duplicate of that type
// with a new type level.
//
// The pointer returned lives until `commit` or `clear` is called.
PendingType* changeLevel(TypeId ty, TypeLevel newLevel);
// Queues a replacement of a type pack with a level with a duplicate of that
// type pack with a new type level.
//
// The pointer returned lives until `commit` or `clear` is called.
PendingTypePack* changeLevel(TypePackId tp, TypeLevel newLevel);
// Queues a replacement of a table type with another table type with a new
// indexer.
//
// The pointer returned lives until `commit` or `clear` is called.
PendingType* changeIndexer(TypeId ty, std::optional<TableIndexer> indexer);
// Returns the type level of the pending state of the type, or the level of that
// type, if no pending state exists. If the type doesn't have a notion of a level,
// returns nullopt. If the pending state doesn't have a notion of a level, but the
// original state does, returns nullopt.
std::optional<TypeLevel> getLevel(TypeId ty) const;
// Follows a type, accounting for pending type states. The returned type may have
// pending state; you should use `pending` or `get` to find out.
TypeId follow(TypeId ty) const;
// Follows a type pack, accounting for pending type states. The returned type pack
// may have pending state; you should use `pending` or `get` to find out.
TypePackId follow(TypePackId tp) const;
// Replaces a given type's state with a new variant. Returns the new pending state
// of that type.
//
// The pointer returned lives until `commit` or `clear` is called.
template<typename T>
PendingType* replace(TypeId ty, T replacement)
{
return replace(ty, Type(replacement));
}
// Replaces a given type pack's state with a new variant. Returns the new
// pending state of that type pack.
//
// The pointer returned lives until `commit` or `clear` is called.
template<typename T>
PendingTypePack* replace(TypePackId tp, T replacement)
{
return replace(tp, TypePackVar(replacement));
}
// Returns T if a given type or type pack is this variant, respecting the
// log's pending state.
//
// Do not retain this pointer; it has the potential to be invalidated when
// commit or clear is called.
template<typename T, typename TID>
T* getMutable(TID ty) const
{
auto* pendingTy = pending(ty);
if (pendingTy)
return Luau::getMutable<T>(pendingTy);
return Luau::getMutable<T>(ty);
}
template<typename T, typename TID>
const T* get(TID ty) const
{
return this->getMutable<T>(ty);
}
// Returns whether a given type or type pack is a given state, respecting the
// log's pending state.
//
// This method will not assert if called on a BoundType or BoundTypePack.
template<typename T, typename TID>
bool is(TID ty) const
{
// We do not use getMutable here because this method can be called on
// BoundTypes, which triggers an assertion.
auto* pendingTy = pending(ty);
if (pendingTy)
return Luau::get_if<T>(&pendingTy->pending.ty) != nullptr;
return Luau::get_if<T>(&ty->ty) != nullptr;
}
std::pair<std::vector<TypeId>, std::vector<TypePackId>> getChanges() const;
private:
std::vector<std::pair<TypeId, TypeVar>> typeVarChanges;
std::vector<std::pair<TypePackId, TypePackVar>> typePackChanges;
std::vector<std::pair<TableTypeVar*, std::optional<TypeId>>> tableChanges;
size_t originalSeenSize;
// unique_ptr is used to give us stable pointers across insertions into the
// map. Otherwise, it would be really easy to accidentally invalidate the
// pointers returned from queue/pending.
DenseHashMap<TypeId, std::unique_ptr<PendingType>> typeVarChanges;
DenseHashMap<TypePackId, std::unique_ptr<PendingTypePack>> typePackChanges;
TxnLog* parent = nullptr;
// Owned version of sharedSeen. This should not be accessed directly in
// TxnLogs; use sharedSeen instead. This field exists because in the tree
// of TxnLogs, the root must own its seen set. In all descendant TxnLogs,
// this is an empty vector.
std::vector<std::pair<TypeOrPackId, TypeOrPackId>> ownedSeen;
bool haveSeen(TypeOrPackId lhs, TypeOrPackId rhs) const;
void pushSeen(TypeOrPackId lhs, TypeOrPackId rhs);
void popSeen(TypeOrPackId lhs, TypeOrPackId rhs);
public:
std::vector<std::pair<TypeId, TypeId>> ownedSeen; // used to avoid infinite recursion when types are cyclic
std::vector<std::pair<TypeId, TypeId>>* sharedSeen; // shared with all the descendent logs
// There is one spot in the code where TxnLog has to reconcile collisions
// between parallel logs. In that codepath, we have to work out which of two
// FreeTypes subsumes the other. If useScopes is false, the TypeLevel is
// used. Else we use the embedded Scope*.
bool useScopes = false;
// It is sometimes the case under DCR that we speculatively rebind
// GenericTypes to other types as though they were free. We mark logs that
// contain these kinds of substitutions as radioactive so that we know that
// we must never commit one.
bool radioactive = false;
// Used to avoid infinite recursion when types are cyclic.
// Shared with all the descendent TxnLogs.
std::vector<std::pair<TypeOrPackId, TypeOrPackId>>* sharedSeen;
};
} // namespace Luau

1245
Analysis/include/Luau/Type.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,62 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Polarity.h"
#include "Luau/TypedAllocator.h"
#include "Luau/Type.h"
#include "Luau/TypePack.h"
#include <vector>
namespace Luau
{
struct Module;
struct TypeArena
{
TypedAllocator<Type> types;
TypedAllocator<TypePackVar> typePacks;
// Owning module, if any
Module* owningModule = nullptr;
void clear();
template<typename T>
TypeId addType(T tv)
{
if constexpr (std::is_same_v<T, UnionType>)
LUAU_ASSERT(tv.options.size() >= 2);
return addTV(Type(std::move(tv)));
}
TypeId addTV(Type&& tv);
TypeId freshType(NotNull<BuiltinTypes> builtins, TypeLevel level);
TypeId freshType(NotNull<BuiltinTypes> builtins, Scope* scope);
TypeId freshType(NotNull<BuiltinTypes> builtins, Scope* scope, TypeLevel level);
TypePackId freshTypePack(Scope* scope, Polarity polarity = Polarity::Unknown);
TypePackId addTypePack(std::initializer_list<TypeId> types);
TypePackId addTypePack(std::vector<TypeId> types, std::optional<TypePackId> tail = {});
TypePackId addTypePack(TypePack pack);
TypePackId addTypePack(TypePackVar pack);
template<typename T>
TypePackId addTypePack(T tp)
{
return addTypePack(TypePackVar(std::move(tp)));
}
TypeId addTypeFunction(const TypeFunction& function, std::initializer_list<TypeId> types);
TypeId addTypeFunction(const TypeFunction& function, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments = {});
TypePackId addTypePackFunction(const TypePackFunction& function, std::initializer_list<TypeId> types);
TypePackId addTypePackFunction(const TypePackFunction& function, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments = {});
};
void freeze(TypeArena& arena);
void unfreeze(TypeArena& arena);
} // namespace Luau

View file

@ -11,7 +11,7 @@ namespace Luau
struct TypeRehydrationOptions
{
std::unordered_set<std::string> bannedNames;
bool expandClassProps = false;
bool expandExternTypeProps = false;
};
void attachTypeData(SourceModule& source, Module& result);

View file

@ -0,0 +1,41 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Cancellation.h"
#include "Luau/Error.h"
#include <memory>
#include <optional>
#include <string>
namespace Luau
{
class TimeLimitError : public InternalCompilerError
{
public:
explicit TimeLimitError(const std::string& moduleName)
: InternalCompilerError("Typeinfer failed to complete in allotted time", moduleName)
{
}
};
class UserCancelError : public InternalCompilerError
{
public:
explicit UserCancelError(const std::string& moduleName)
: InternalCompilerError("Analysis has been cancelled by user", moduleName)
{
}
};
struct TypeCheckLimits
{
std::optional<double> finishTime;
std::optional<int> instantiationChildLimit;
std::optional<int> unifierIterationLimit;
std::shared_ptr<FrontendCancellationToken> cancellationToken;
};
} // namespace Luau

View file

@ -0,0 +1,243 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Common.h"
#include "Luau/EqSatSimplification.h"
#include "Luau/Error.h"
#include "Luau/Normalize.h"
#include "Luau/NotNull.h"
#include "Luau/Subtyping.h"
#include "Luau/Type.h"
#include "Luau/TypeFwd.h"
#include "Luau/TypeOrPack.h"
#include "Luau/TypeUtils.h"
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
namespace Luau
{
struct BuiltinTypes;
struct DcrLogger;
struct TypeCheckLimits;
struct UnifierSharedState;
struct SourceModule;
struct Module;
struct InternalErrorReporter;
struct Scope;
struct PropertyType;
struct PropertyTypes;
struct StackPusher;
struct Reasonings
{
// the list of reasons
std::vector<std::string> reasons;
// this should be true if _all_ of the reasons have an error suppressing type, and false otherwise.
bool suppressed;
std::string toString()
{
if (FFlag::LuauImproveTypePathsInErrors && reasons.empty())
return "";
// DenseHashSet ordering is entirely undefined, so we want to
// sort the reasons here to achieve a stable error
// stringification.
std::sort(reasons.begin(), reasons.end());
std::string allReasons = FFlag::LuauImproveTypePathsInErrors ? "\nthis is because " : "";
bool first = true;
for (const std::string& reason : reasons)
{
if (FFlag::LuauImproveTypePathsInErrors)
{
if (reasons.size() > 1)
allReasons += "\n\t * ";
}
else
{
if (first)
first = false;
else
allReasons += "\n\t";
}
allReasons += reason;
}
return allReasons;
}
};
void check(
NotNull<BuiltinTypes> builtinTypes,
NotNull<Simplifier> simplifier,
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
NotNull<UnifierSharedState> unifierState,
NotNull<TypeCheckLimits> limits,
DcrLogger* logger,
const SourceModule& sourceModule,
Module* module
);
struct TypeChecker2
{
NotNull<BuiltinTypes> builtinTypes;
NotNull<Simplifier> simplifier;
NotNull<TypeFunctionRuntime> typeFunctionRuntime;
DcrLogger* logger;
const NotNull<TypeCheckLimits> limits;
const NotNull<InternalErrorReporter> ice;
const SourceModule* sourceModule;
Module* module;
TypeContext typeContext = TypeContext::Default;
std::vector<NotNull<Scope>> stack;
std::vector<TypeId> functionDeclStack;
DenseHashSet<TypeId> seenTypeFunctionInstances{nullptr};
Normalizer normalizer;
Subtyping _subtyping;
NotNull<Subtyping> subtyping;
TypeChecker2(
NotNull<BuiltinTypes> builtinTypes,
NotNull<Simplifier> simplifier,
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
NotNull<UnifierSharedState> unifierState,
NotNull<TypeCheckLimits> limits,
DcrLogger* logger,
const SourceModule* sourceModule,
Module* module
);
void visit(AstStatBlock* block);
void reportError(TypeErrorData data, const Location& location);
Reasonings explainReasonings(TypeId subTy, TypeId superTy, Location location, const SubtypingResult& r);
Reasonings explainReasonings(TypePackId subTp, TypePackId superTp, Location location, const SubtypingResult& r);
private:
static bool allowsNoReturnValues(const TypePackId tp);
static Location getEndLocation(const AstExprFunction* function);
bool isErrorCall(const AstExprCall* call);
bool hasBreak(AstStat* node);
const AstStat* getFallthrough(const AstStat* node);
std::optional<StackPusher> pushStack(AstNode* node);
void checkForInternalTypeFunction(TypeId ty, Location location);
TypeId checkForTypeFunctionInhabitance(TypeId instance, Location location);
TypePackId lookupPack(AstExpr* expr) const;
TypeId lookupType(AstExpr* expr);
TypeId lookupAnnotation(AstType* annotation);
std::optional<TypePackId> lookupPackAnnotation(AstTypePack* annotation) const;
TypeId lookupExpectedType(AstExpr* expr) const;
TypePackId lookupExpectedPack(AstExpr* expr, TypeArena& arena) const;
TypePackId reconstructPack(AstArray<AstExpr*> exprs, TypeArena& arena);
Scope* findInnermostScope(Location location) const;
void visit(AstStat* stat);
void visit(AstStatIf* ifStatement);
void visit(AstStatWhile* whileStatement);
void visit(AstStatRepeat* repeatStatement);
void visit(AstStatBreak*);
void visit(AstStatContinue*);
void visit(AstStatReturn* ret);
void visit(AstStatExpr* expr);
void visit(AstStatLocal* local);
void visit(AstStatFor* forStatement);
void visit(AstStatForIn* forInStatement);
std::optional<TypeId> getBindingType(AstExpr* expr);
void reportErrorsFromAssigningToNever(AstExpr* lhs, TypeId rhsType);
void visit(AstStatAssign* assign);
void visit(AstStatCompoundAssign* stat);
void visit(AstStatFunction* stat);
void visit(AstStatLocalFunction* stat);
void visit(const AstTypeList* typeList);
void visit(AstStatTypeAlias* stat);
void visit(AstStatTypeFunction* stat);
void visit(AstTypeList types);
void visit(AstStatDeclareFunction* stat);
void visit(AstStatDeclareGlobal* stat);
void visit(AstStatDeclareExternType* stat);
void visit(AstStatError* stat);
void visit(AstExpr* expr, ValueContext context);
void visit(AstExprGroup* expr, ValueContext context);
void visit(AstExprConstantNil* expr);
void visit(AstExprConstantBool* expr);
void visit(AstExprConstantNumber* expr);
void visit(AstExprConstantString* expr);
void visit(AstExprLocal* expr);
void visit(AstExprGlobal* expr);
void visit(AstExprVarargs* expr);
void visitCall(AstExprCall* call);
void visit(AstExprCall* call);
std::optional<TypeId> tryStripUnionFromNil(TypeId ty) const;
TypeId stripFromNilAndReport(TypeId ty, const Location& location);
void visitExprName(AstExpr* expr, Location location, const std::string& propName, ValueContext context, TypeId astIndexExprTy);
void visit(AstExprIndexName* indexName, ValueContext context);
void indexExprMetatableHelper(AstExprIndexExpr* indexExpr, const MetatableType* metaTable, TypeId exprType, TypeId indexType);
void visit(AstExprIndexExpr* indexExpr, ValueContext context);
void visit(AstExprFunction* fn);
void visit(AstExprTable* expr);
void visit(AstExprUnary* expr);
TypeId visit(AstExprBinary* expr, AstNode* overrideKey = nullptr);
void visit(AstExprTypeAssertion* expr);
void visit(AstExprIfElse* expr);
void visit(AstExprInterpString* interpString);
void visit(AstExprError* expr);
TypeId flattenPack(TypePackId pack);
void visitGenerics(AstArray<AstGenericType*> generics, AstArray<AstGenericTypePack*> genericPacks);
void visit(AstType* ty);
void visit(AstTypeReference* ty);
void visit(AstTypeTable* table);
void visit(AstTypeFunction* ty);
void visit(AstTypeTypeof* ty);
void visit(AstTypeUnion* ty);
void visit(AstTypeIntersection* ty);
void visit(AstTypePack* pack);
void visit(AstTypePackExplicit* tp);
void visit(AstTypePackVariadic* tp);
void visit(AstTypePackGeneric* tp);
template<typename TID>
Reasonings explainReasonings_(TID subTy, TID superTy, Location location, const SubtypingResult& r);
void explainError(TypeId subTy, TypeId superTy, Location location, const SubtypingResult& result);
void explainError(TypePackId subTy, TypePackId superTy, Location location, const SubtypingResult& result);
bool testIsSubtype(TypeId subTy, TypeId superTy, Location location);
bool testIsSubtype(TypePackId subTy, TypePackId superTy, Location location);
void reportError(TypeError e);
void reportErrors(ErrorVec errors);
PropertyTypes lookupProp(
const NormalizedType* norm,
const std::string& prop,
ValueContext context,
const Location& location,
TypeId astIndexExprType,
std::vector<TypeError>& errors
);
// If the provided type does not have the named property, report an error.
void checkIndexTypeFromType(TypeId tableTy, const std::string& prop, ValueContext context, const Location& location, TypeId astIndexExprType);
PropertyType hasIndexTypeFromType(
TypeId ty,
const std::string& prop,
ValueContext context,
const Location& location,
DenseHashSet<TypeId>& seen,
TypeId astIndexExprType,
std::vector<TypeError>& errors
);
// Avoid duplicate warnings being emitted for the same global variable.
DenseHashSet<std::string> warnedGlobals{""};
void diagnoseMissingTableKey(UnknownProperty* utk, TypeErrorData& data) const;
bool isErrorSuppressing(Location loc, TypeId ty);
bool isErrorSuppressing(Location loc1, TypeId ty1, Location loc2, TypeId ty2);
bool isErrorSuppressing(Location loc, TypePackId tp);
bool isErrorSuppressing(Location loc1, TypePackId tp1, Location loc2, TypePackId tp2);
};
} // namespace Luau

View file

@ -0,0 +1,264 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Constraint.h"
#include "Luau/EqSatSimplification.h"
#include "Luau/Error.h"
#include "Luau/NotNull.h"
#include "Luau/TypeCheckLimits.h"
#include "Luau/TypeFunctionRuntime.h"
#include "Luau/TypeFwd.h"
#include <functional>
#include <string>
#include <optional>
struct lua_State;
namespace Luau
{
struct TypeArena;
struct TxnLog;
struct ConstraintSolver;
class Normalizer;
using StateRef = std::unique_ptr<lua_State, void (*)(lua_State*)>;
struct TypeFunctionRuntime
{
TypeFunctionRuntime(NotNull<InternalErrorReporter> ice, NotNull<TypeCheckLimits> limits);
~TypeFunctionRuntime();
// Return value is an error message if registration failed
std::optional<std::string> registerFunction(AstStatTypeFunction* function);
// For user-defined type functions, we store all generated types and packs for the duration of the typecheck
TypedAllocator<TypeFunctionType> typeArena;
TypedAllocator<TypeFunctionTypePackVar> typePackArena;
NotNull<InternalErrorReporter> ice;
NotNull<TypeCheckLimits> limits;
StateRef state;
// Set of functions which have their environment table initialized
DenseHashSet<AstStatTypeFunction*> initialized{nullptr};
// Evaluation of type functions should only be performed in the absence of parse errors in the source module
bool allowEvaluation = true;
// Root scope in which the type function operates in, set up by ConstraintGenerator
ScopePtr rootScope;
// Output created by 'print' function
std::vector<std::string> messages;
private:
void prepareState();
};
struct TypeFunctionContext
{
NotNull<TypeArena> arena;
NotNull<BuiltinTypes> builtins;
NotNull<Scope> scope;
NotNull<Simplifier> simplifier;
NotNull<Normalizer> normalizer;
NotNull<TypeFunctionRuntime> typeFunctionRuntime;
NotNull<InternalErrorReporter> ice;
NotNull<TypeCheckLimits> limits;
// nullptr if the type function is being reduced outside of the constraint solver.
ConstraintSolver* solver;
// The constraint being reduced in this run of the reduction
const Constraint* constraint;
std::optional<AstName> userFuncName; // Name of the user-defined type function; only available for UDTFs
TypeFunctionContext(NotNull<ConstraintSolver> cs, NotNull<Scope> scope, NotNull<const Constraint> constraint);
TypeFunctionContext(
NotNull<TypeArena> arena,
NotNull<BuiltinTypes> builtins,
NotNull<Scope> scope,
NotNull<Simplifier> simplifier,
NotNull<Normalizer> normalizer,
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
NotNull<InternalErrorReporter> ice,
NotNull<TypeCheckLimits> limits
)
: arena(arena)
, builtins(builtins)
, scope(scope)
, simplifier(simplifier)
, normalizer(normalizer)
, typeFunctionRuntime(typeFunctionRuntime)
, ice(ice)
, limits(limits)
, solver(nullptr)
, constraint(nullptr)
{
}
NotNull<Constraint> pushConstraint(ConstraintV&& c) const;
};
enum class Reduction
{
// The type function is either known to be reducible or the determination is blocked.
MaybeOk,
// The type function is known to be irreducible, but maybe not be erroneous, e.g. when it's over generics or free types.
Irreducible,
// The type function is known to be irreducible, and is definitely erroneous.
Erroneous,
};
/// Represents a reduction result, which may have successfully reduced the type,
/// may have concretely failed to reduce the type, or may simply be stuck
/// without more information.
template<typename Ty>
struct TypeFunctionReductionResult
{
/// The result of the reduction, if any. If this is nullopt, the type function
/// could not be reduced.
std::optional<Ty> result;
/// Indicates the status of this reduction: is `Reduction::Irreducible` if
/// the this result indicates the type function is irreducible, and
/// `Reduction::Erroneous` if this result indicates the type function is
/// erroneous. `Reduction::MaybeOk` otherwise.
Reduction reductionStatus;
/// Any types that need to be progressed or mutated before the reduction may
/// proceed.
std::vector<TypeId> blockedTypes;
/// Any type packs that need to be progressed or mutated before the
/// reduction may proceed.
std::vector<TypePackId> blockedPacks;
/// A runtime error message from user-defined type functions
std::optional<std::string> error;
/// Messages printed out from user-defined type functions
std::vector<std::string> messages;
};
template<typename T>
using ReducerFunction =
std::function<TypeFunctionReductionResult<T>(T, const std::vector<TypeId>&, const std::vector<TypePackId>&, NotNull<TypeFunctionContext>)>;
/// Represents a type function that may be applied to map a series of types and
/// type packs to a single output type.
struct TypeFunction
{
/// The human-readable name of the type function. Used to stringify instance
/// types.
std::string name;
/// The reducer function for the type function.
ReducerFunction<TypeId> reducer;
/// If true, this type function can reduce even if it is parameterized on a generic.
bool canReduceGenerics = false;
};
/// Represents a type function that may be applied to map a series of types and
/// type packs to a single output type pack.
struct TypePackFunction
{
/// The human-readable name of the type pack function. Used to stringify
/// instance packs.
std::string name;
/// The reducer function for the type pack function.
ReducerFunction<TypePackId> reducer;
/// If true, this type function can reduce even if it is parameterized on a generic.
bool canReduceGenerics = false;
};
struct FunctionGraphReductionResult
{
ErrorVec errors;
ErrorVec messages;
DenseHashSet<TypeId> blockedTypes{nullptr};
DenseHashSet<TypePackId> blockedPacks{nullptr};
DenseHashSet<TypeId> reducedTypes{nullptr};
DenseHashSet<TypePackId> reducedPacks{nullptr};
DenseHashSet<TypeId> irreducibleTypes{nullptr};
};
/**
* Attempt to reduce all instances of any type or type pack functions in the type
* graph provided.
*
* @param entrypoint the entry point to the type graph.
* @param location the location the reduction is occurring at; used to populate
* type errors.
* @param arena an arena to allocate types into.
* @param builtins the built-in types.
* @param normalizer the normalizer to use when normalizing types
* @param ice the internal error reporter to use for ICEs
*/
FunctionGraphReductionResult reduceTypeFunctions(TypeId entrypoint, Location location, TypeFunctionContext, bool force = false);
/**
* Attempt to reduce all instances of any type or type pack functions in the type
* graph provided.
*
* @param entrypoint the entry point to the type graph.
* @param location the location the reduction is occurring at; used to populate
* type errors.
* @param arena an arena to allocate types into.
* @param builtins the built-in types.
* @param normalizer the normalizer to use when normalizing types
* @param ice the internal error reporter to use for ICEs
*/
FunctionGraphReductionResult reduceTypeFunctions(TypePackId entrypoint, Location location, TypeFunctionContext, bool force = false);
struct BuiltinTypeFunctions
{
BuiltinTypeFunctions();
TypeFunction userFunc;
TypeFunction notFunc;
TypeFunction lenFunc;
TypeFunction unmFunc;
TypeFunction addFunc;
TypeFunction subFunc;
TypeFunction mulFunc;
TypeFunction divFunc;
TypeFunction idivFunc;
TypeFunction powFunc;
TypeFunction modFunc;
TypeFunction concatFunc;
TypeFunction andFunc;
TypeFunction orFunc;
TypeFunction ltFunc;
TypeFunction leFunc;
TypeFunction eqFunc;
TypeFunction refineFunc;
TypeFunction singletonFunc;
TypeFunction unionFunc;
TypeFunction intersectFunc;
TypeFunction keyofFunc;
TypeFunction rawkeyofFunc;
TypeFunction indexFunc;
TypeFunction rawgetFunc;
TypeFunction setmetatableFunc;
TypeFunction getmetatableFunc;
TypeFunction weakoptionalFunc;
void addToScope(NotNull<TypeArena> arena, NotNull<Scope> scope) const;
};
const BuiltinTypeFunctions& builtinTypeFunctions();
} // namespace Luau

View file

@ -0,0 +1,85 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Ast.h"
#include "Luau/VecDeque.h"
#include "Luau/DenseHash.h"
#include "Luau/TypeFunction.h"
#include "Luau/Type.h"
#include "Luau/TypePack.h"
#include "Luau/TypeUtils.h"
#include "Luau/Normalize.h"
#include "Luau/TypeFwd.h"
#include "Luau/VisitType.h"
#include "Luau/NotNull.h"
#include "TypeArena.h"
namespace Luau
{
struct TypeFunctionReductionGuessResult
{
std::vector<std::pair<std::string, TypeId>> guessedFunctionAnnotations;
TypeId guessedReturnType;
bool shouldRecommendAnnotation = true;
};
// An Inference result for a type function is a list of types corresponding to the guessed argument types, followed by a type for the result
struct TypeFunctionInferenceResult
{
std::vector<TypeId> operandInference;
TypeId functionResultInference;
};
struct TypeFunctionReductionGuesser
{
// Tracks our hypothesis about what a type function reduces to
DenseHashMap<TypeId, TypeId> functionReducesTo{nullptr};
// Tracks our constraints on type function operands
DenseHashMap<TypeId, TypeId> substitutable{nullptr};
// List of instances to try progress
VecDeque<TypeId> toInfer;
DenseHashSet<TypeId> cyclicInstances{nullptr};
// Utilities
NotNull<TypeArena> arena;
NotNull<BuiltinTypes> builtins;
NotNull<Normalizer> normalizer;
TypeFunctionReductionGuesser(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtins, NotNull<Normalizer> normalizer);
std::optional<TypeId> guess(TypeId typ);
std::optional<TypePackId> guess(TypePackId typ);
TypeFunctionReductionGuessResult guessTypeFunctionReductionForFunctionExpr(const AstExprFunction& expr, const FunctionType* ftv, TypeId retTy);
private:
std::optional<TypeId> guessType(TypeId arg);
void dumpGuesses();
bool isNumericBinopFunction(const TypeFunctionInstanceType& instance);
bool isComparisonFunction(const TypeFunctionInstanceType& instance);
bool isOrAndFunction(const TypeFunctionInstanceType& instance);
bool isNotFunction(const TypeFunctionInstanceType& instance);
bool isLenFunction(const TypeFunctionInstanceType& instance);
bool isUnaryMinus(const TypeFunctionInstanceType& instance);
// Operand is assignable if it looks like a cyclic type function instance, or a generic type
bool operandIsAssignable(TypeId ty);
std::optional<TypeId> tryAssignOperandType(TypeId ty);
std::shared_ptr<const NormalizedType> normalize(TypeId ty);
void step();
void infer();
bool done();
bool isFunctionGenericsSaturated(const FunctionType& ftv, DenseHashSet<TypeId>& instanceArgs);
void inferTypeFunctionSubstitutions(TypeId ty, const TypeFunctionInstanceType* instance);
TypeFunctionInferenceResult inferNumericBinopFunction(const TypeFunctionInstanceType* instance);
TypeFunctionInferenceResult inferComparisonFunction(const TypeFunctionInstanceType* instance);
TypeFunctionInferenceResult inferOrAndFunction(const TypeFunctionInstanceType* instance);
TypeFunctionInferenceResult inferNotFunction(const TypeFunctionInstanceType* instance);
TypeFunctionInferenceResult inferLenFunction(const TypeFunctionInstanceType* instance);
TypeFunctionInferenceResult inferUnaryMinusFunction(const TypeFunctionInstanceType* instance);
};
} // namespace Luau

View file

@ -0,0 +1,296 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Common.h"
#include "Luau/Variant.h"
#include "Luau/TypeFwd.h"
#include <optional>
#include <string>
#include <map>
#include <vector>
using lua_State = struct lua_State;
namespace Luau
{
void* typeFunctionAlloc(void* ud, void* ptr, size_t osize, size_t nsize);
// Replica of types from Type.h
struct TypeFunctionType;
using TypeFunctionTypeId = const TypeFunctionType*;
struct TypeFunctionTypePackVar;
using TypeFunctionTypePackId = const TypeFunctionTypePackVar*;
struct TypeFunctionPrimitiveType
{
enum Type
{
NilType,
Boolean,
Number,
String,
Thread,
Buffer,
};
Type type;
TypeFunctionPrimitiveType(Type type)
: type(type)
{
}
};
struct TypeFunctionBooleanSingleton
{
bool value = false;
};
struct TypeFunctionStringSingleton
{
std::string value;
};
using TypeFunctionSingletonVariant = Variant<TypeFunctionBooleanSingleton, TypeFunctionStringSingleton>;
struct TypeFunctionSingletonType
{
TypeFunctionSingletonVariant variant;
explicit TypeFunctionSingletonType(TypeFunctionSingletonVariant variant)
: variant(std::move(variant))
{
}
};
template<typename T>
const T* get(const TypeFunctionSingletonType* tv)
{
LUAU_ASSERT(tv);
return tv ? get_if<T>(&tv->variant) : nullptr;
}
template<typename T>
T* getMutable(const TypeFunctionSingletonType* tv)
{
LUAU_ASSERT(tv);
return tv ? get_if<T>(&const_cast<TypeFunctionSingletonType*>(tv)->variant) : nullptr;
}
struct TypeFunctionUnionType
{
std::vector<TypeFunctionTypeId> components;
};
struct TypeFunctionIntersectionType
{
std::vector<TypeFunctionTypeId> components;
};
struct TypeFunctionAnyType
{
};
struct TypeFunctionUnknownType
{
};
struct TypeFunctionNeverType
{
};
struct TypeFunctionNegationType
{
TypeFunctionTypeId type;
};
struct TypeFunctionTypePack
{
std::vector<TypeFunctionTypeId> head;
std::optional<TypeFunctionTypePackId> tail;
};
struct TypeFunctionVariadicTypePack
{
TypeFunctionTypeId type;
};
struct TypeFunctionGenericTypePack
{
bool isNamed = false;
std::string name;
};
using TypeFunctionTypePackVariant = Variant<TypeFunctionTypePack, TypeFunctionVariadicTypePack, TypeFunctionGenericTypePack>;
struct TypeFunctionTypePackVar
{
TypeFunctionTypePackVariant type;
TypeFunctionTypePackVar(TypeFunctionTypePackVariant type)
: type(std::move(type))
{
}
bool operator==(const TypeFunctionTypePackVar& rhs) const;
};
struct TypeFunctionFunctionType
{
std::vector<TypeFunctionTypeId> generics;
std::vector<TypeFunctionTypePackId> genericPacks;
TypeFunctionTypePackId argTypes;
TypeFunctionTypePackId retTypes;
};
template<typename T>
const T* get(TypeFunctionTypePackId tv)
{
LUAU_ASSERT(tv);
return tv ? get_if<T>(&tv->type) : nullptr;
}
template<typename T>
T* getMutable(TypeFunctionTypePackId tv)
{
LUAU_ASSERT(tv);
return tv ? get_if<T>(&const_cast<TypeFunctionTypePackVar*>(tv)->type) : nullptr;
}
struct TypeFunctionTableIndexer
{
TypeFunctionTableIndexer(TypeFunctionTypeId keyType, TypeFunctionTypeId valueType)
: keyType(keyType)
, valueType(valueType)
{
}
TypeFunctionTypeId keyType;
TypeFunctionTypeId valueType;
};
struct TypeFunctionProperty
{
static TypeFunctionProperty readonly(TypeFunctionTypeId ty);
static TypeFunctionProperty writeonly(TypeFunctionTypeId ty);
static TypeFunctionProperty rw(TypeFunctionTypeId ty); // Shared read-write type.
static TypeFunctionProperty rw(TypeFunctionTypeId read, TypeFunctionTypeId write); // Separate read-write type.
bool isReadOnly() const;
bool isWriteOnly() const;
std::optional<TypeFunctionTypeId> readTy;
std::optional<TypeFunctionTypeId> writeTy;
};
struct TypeFunctionTableType
{
using Name = std::string;
using Props = std::map<Name, TypeFunctionProperty>;
Props props;
std::optional<TypeFunctionTableIndexer> indexer;
// Should always be a TypeFunctionTableType
std::optional<TypeFunctionTypeId> metatable;
};
struct TypeFunctionExternType
{
using Name = std::string;
using Props = std::map<Name, TypeFunctionProperty>;
Props props;
std::optional<TypeFunctionTableIndexer> indexer;
std::optional<TypeFunctionTypeId> metatable; // metaclass?
// this was mistaken, and we should actually be keeping separate read/write types here.
std::optional<TypeFunctionTypeId> parent_DEPRECATED;
std::optional<TypeFunctionTypeId> readParent;
std::optional<TypeFunctionTypeId> writeParent;
TypeId externTy;
};
struct TypeFunctionGenericType
{
bool isNamed = false;
bool isPack = false;
std::string name;
};
using TypeFunctionTypeVariant = Luau::Variant<
TypeFunctionPrimitiveType,
TypeFunctionAnyType,
TypeFunctionUnknownType,
TypeFunctionNeverType,
TypeFunctionSingletonType,
TypeFunctionUnionType,
TypeFunctionIntersectionType,
TypeFunctionNegationType,
TypeFunctionFunctionType,
TypeFunctionTableType,
TypeFunctionExternType,
TypeFunctionGenericType>;
struct TypeFunctionType
{
TypeFunctionTypeVariant type;
TypeFunctionType(TypeFunctionTypeVariant type)
: type(std::move(type))
{
}
bool operator==(const TypeFunctionType& rhs) const;
};
template<typename T>
const T* get(TypeFunctionTypeId tv)
{
LUAU_ASSERT(tv);
return tv ? Luau::get_if<T>(&tv->type) : nullptr;
}
template<typename T>
T* getMutable(TypeFunctionTypeId tv)
{
LUAU_ASSERT(tv);
return tv ? Luau::get_if<T>(&const_cast<TypeFunctionType*>(tv)->type) : nullptr;
}
std::optional<std::string> checkResultForError(lua_State* L, const char* typeFunctionName, int luaResult);
TypeFunctionType* allocateTypeFunctionType(lua_State* L, TypeFunctionTypeVariant type);
TypeFunctionTypePackVar* allocateTypeFunctionTypePack(lua_State* L, TypeFunctionTypePackVariant type);
void allocTypeUserData(lua_State* L, TypeFunctionTypeVariant type);
bool isTypeUserData(lua_State* L, int idx);
TypeFunctionTypeId getTypeUserData(lua_State* L, int idx);
std::optional<TypeFunctionTypeId> optionalTypeUserData(lua_State* L, int idx);
void registerTypesLibrary(lua_State* L);
void registerTypeUserData(lua_State* L);
void setTypeFunctionEnvironment(lua_State* L);
void resetTypeFunctionState(lua_State* L);
} // namespace Luau

View file

@ -0,0 +1,44 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Type.h"
#include "Luau/TypeFunction.h"
#include "Luau/TypeFunctionRuntime.h"
namespace Luau
{
using Kind = Variant<TypeId, TypePackId>;
template<typename T>
const T* get(const Kind& kind)
{
return get_if<T>(&kind);
}
using TypeFunctionKind = Variant<TypeFunctionTypeId, TypeFunctionTypePackId>;
template<typename T>
const T* get(const TypeFunctionKind& tfkind)
{
return get_if<T>(&tfkind);
}
struct TypeFunctionRuntimeBuilderState
{
NotNull<TypeFunctionContext> ctx;
// List of errors that occur during serialization/deserialization
// At every iteration of serialization/deserialization, if this list.size() != 0, we halt the process
std::vector<std::string> errors{};
TypeFunctionRuntimeBuilderState(NotNull<TypeFunctionContext> ctx)
: ctx(ctx)
{
}
};
TypeFunctionTypeId serialize(TypeId ty, TypeFunctionRuntimeBuilderState* state);
TypeId deserialize(TypeFunctionTypeId ty, TypeFunctionRuntimeBuilderState* state);
} // namespace Luau

View file

@ -0,0 +1,59 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Variant.h"
#include <string>
namespace Luau
{
// So... why `const T*` here rather than `T*`?
// It's because we've had problems caused by the type graph being mutated
// in ways it shouldn't be, for example mutating types from other modules.
// To try to control this, we make the use of types immutable by default,
// then provide explicit mutable access via getMutable and asMutable.
// This means we can grep for all the places we're mutating the type graph,
// and it makes it possible to provide other APIs (e.g. the txn log)
// which control mutable access to the type graph.
struct Type;
using TypeId = const Type*;
struct FreeType;
struct GenericType;
struct PrimitiveType;
struct BlockedType;
struct PendingExpansionType;
struct SingletonType;
struct FunctionType;
struct TableType;
struct MetatableType;
struct ExternType;
struct AnyType;
struct UnionType;
struct IntersectionType;
struct LazyType;
struct UnknownType;
struct NeverType;
struct NegationType;
struct TypeFunctionInstanceType;
struct TypePackVar;
using TypePackId = const TypePackVar*;
struct FreeTypePack;
struct GenericTypePack;
struct TypePack;
struct VariadicTypePack;
struct BlockedTypePack;
struct TypeFunctionInstanceTypePack;
using Name = std::string;
using ModuleName = std::string;
struct BuiltinTypes;
using TypeOrPack = Variant<TypeId, TypePackId>;
} // namespace Luau

View file

@ -1,15 +1,18 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Predicate.h"
#include "Luau/Anyification.h"
#include "Luau/ControlFlow.h"
#include "Luau/Error.h"
#include "Luau/Instantiation.h"
#include "Luau/Module.h"
#include "Luau/Symbol.h"
#include "Luau/Parser.h"
#include "Luau/Predicate.h"
#include "Luau/Substitution.h"
#include "Luau/Symbol.h"
#include "Luau/TxnLog.h"
#include "Luau/TypePack.h"
#include "Luau/TypeVar.h"
#include "Luau/TypeFwd.h"
#include "Luau/TypeCheckLimits.h"
#include "Luau/TypeUtils.h"
#include "Luau/Unifier.h"
#include "Luau/UnifierSharedState.h"
@ -23,168 +26,207 @@ namespace Luau
struct Scope;
struct TypeChecker;
struct ModuleResolver;
struct FrontendCancellationToken;
using Name = std::string;
using ScopePtr = std::shared_ptr<Scope>;
using OverloadErrorEntry = std::tuple<std::vector<TypeError>, std::vector<TypeId>, const FunctionTypeVar*>;
struct OverloadErrorEntry
{
TxnLog log;
ErrorVec errors;
std::vector<TypeId> arguments;
const FunctionType* fnTy;
};
bool doesCallError(const AstExprCall* call);
bool hasBreak(AstStat* node);
const AstStat* getFallthrough(const AstStat* node);
struct UnifierOptions;
struct Unifier;
// A substitution which replaces generic types in a given set by free types.
struct ReplaceGenerics : Substitution
struct GenericTypeDefinitions
{
TypeLevel level;
std::vector<TypeId> generics;
std::vector<TypePackId> genericPacks;
bool ignoreChildren(TypeId ty) override;
bool isDirty(TypeId ty) override;
bool isDirty(TypePackId tp) override;
TypeId clean(TypeId ty) override;
TypePackId clean(TypePackId tp) override;
std::vector<GenericTypeDefinition> genericTypes;
std::vector<GenericTypePackDefinition> genericPacks;
};
// A substitution which replaces generic functions by monomorphic functions
struct Instantiation : Substitution
struct HashBoolNamePair
{
TypeLevel level;
ReplaceGenerics replaceGenerics;
bool ignoreChildren(TypeId ty) override;
bool isDirty(TypeId ty) override;
bool isDirty(TypePackId tp) override;
TypeId clean(TypeId ty) override;
TypePackId clean(TypePackId tp) override;
size_t operator()(const std::pair<bool, Name>& pair) const;
};
// A substitution which replaces free types by generic types.
struct Quantification : Substitution
{
TypeLevel level;
std::vector<TypeId> generics;
std::vector<TypePackId> genericPacks;
bool isDirty(TypeId ty) override;
bool isDirty(TypePackId tp) override;
TypeId clean(TypeId ty) override;
TypePackId clean(TypePackId tp) override;
};
// A substitution which replaces free types by any
struct Anyification : Substitution
{
TypeId anyType;
TypePackId anyTypePack;
bool isDirty(TypeId ty) override;
bool isDirty(TypePackId tp) override;
TypeId clean(TypeId ty) override;
TypePackId clean(TypePackId tp) override;
};
// A substitution which replaces the type parameters of a type function by arguments
struct ApplyTypeFunction : Substitution
{
TypeLevel level;
bool encounteredForwardedType;
std::unordered_map<TypeId, TypeId> typeArguments;
std::unordered_map<TypePackId, TypePackId> typePackArguments;
bool ignoreChildren(TypeId ty) override;
bool ignoreChildren(TypePackId tp) override;
bool isDirty(TypeId ty) override;
bool isDirty(TypePackId tp) override;
TypeId clean(TypeId ty) override;
TypePackId clean(TypePackId tp) override;
};
// All TypeVars are retained via Environment::typeVars. All TypeIds
// All Types are retained via Environment::types. All TypeIds
// within a program are borrowed pointers into this set.
struct TypeChecker
{
explicit TypeChecker(ModuleResolver* resolver, InternalErrorReporter* iceHandler);
explicit TypeChecker(
const ScopePtr& globalScope,
ModuleResolver* resolver,
NotNull<BuiltinTypes> builtinTypes,
InternalErrorReporter* iceHandler
);
TypeChecker(const TypeChecker&) = delete;
TypeChecker& operator=(const TypeChecker&) = delete;
ModulePtr check(const SourceModule& module, Mode mode, std::optional<ScopePtr> environmentScope = std::nullopt);
ModulePtr checkWithoutRecursionCheck(const SourceModule& module, Mode mode, std::optional<ScopePtr> environmentScope = std::nullopt);
std::vector<std::pair<Location, ScopePtr>> getScopes() const;
void check(const ScopePtr& scope, const AstStat& statement);
void check(const ScopePtr& scope, const AstStatBlock& statement);
void check(const ScopePtr& scope, const AstStatIf& statement);
void check(const ScopePtr& scope, const AstStatWhile& statement);
void check(const ScopePtr& scope, const AstStatRepeat& statement);
void check(const ScopePtr& scope, const AstStatReturn& return_);
void check(const ScopePtr& scope, const AstStatAssign& assign);
void check(const ScopePtr& scope, const AstStatCompoundAssign& assign);
void check(const ScopePtr& scope, const AstStatLocal& local);
void check(const ScopePtr& scope, const AstStatFor& local);
void check(const ScopePtr& scope, const AstStatForIn& forin);
void check(const ScopePtr& scope, TypeId ty, const ScopePtr& funScope, const AstStatFunction& function);
void check(const ScopePtr& scope, TypeId ty, const ScopePtr& funScope, const AstStatLocalFunction& function);
void check(const ScopePtr& scope, const AstStatTypeAlias& typealias, int subLevel = 0, bool forwardDeclare = false);
void check(const ScopePtr& scope, const AstStatDeclareClass& declaredClass);
void check(const ScopePtr& scope, const AstStatDeclareFunction& declaredFunction);
ControlFlow check(const ScopePtr& scope, const AstStat& statement);
ControlFlow check(const ScopePtr& scope, const AstStatBlock& statement);
ControlFlow check(const ScopePtr& scope, const AstStatIf& statement);
ControlFlow check(const ScopePtr& scope, const AstStatWhile& statement);
ControlFlow check(const ScopePtr& scope, const AstStatRepeat& statement);
ControlFlow check(const ScopePtr& scope, const AstStatReturn& return_);
ControlFlow check(const ScopePtr& scope, const AstStatAssign& assign);
ControlFlow check(const ScopePtr& scope, const AstStatCompoundAssign& assign);
ControlFlow check(const ScopePtr& scope, const AstStatLocal& local);
ControlFlow check(const ScopePtr& scope, const AstStatFor& local);
ControlFlow check(const ScopePtr& scope, const AstStatForIn& forin);
ControlFlow check(const ScopePtr& scope, TypeId ty, const ScopePtr& funScope, const AstStatFunction& function);
ControlFlow check(const ScopePtr& scope, TypeId ty, const ScopePtr& funScope, const AstStatLocalFunction& function);
ControlFlow check(const ScopePtr& scope, const AstStatTypeAlias& typealias);
ControlFlow check(const ScopePtr& scope, const AstStatTypeFunction& typefunction);
ControlFlow check(const ScopePtr& scope, const AstStatDeclareExternType& declaredExternType);
ControlFlow check(const ScopePtr& scope, const AstStatDeclareFunction& declaredFunction);
void checkBlock(const ScopePtr& scope, const AstStatBlock& statement);
void prototype(const ScopePtr& scope, const AstStatTypeAlias& typealias, int subLevel = 0);
void prototype(const ScopePtr& scope, const AstStatDeclareExternType& declaredExternType);
ControlFlow checkBlock(const ScopePtr& scope, const AstStatBlock& statement);
ControlFlow checkBlockWithoutRecursionCheck(const ScopePtr& scope, const AstStatBlock& statement);
void checkBlockTypeAliases(const ScopePtr& scope, std::vector<AstStat*>& sorted);
ExprResult<TypeId> checkExpr(const ScopePtr& scope, const AstExpr& expr, std::optional<TypeId> expectedType = std::nullopt);
ExprResult<TypeId> checkExpr(const ScopePtr& scope, const AstExprLocal& expr);
ExprResult<TypeId> checkExpr(const ScopePtr& scope, const AstExprGlobal& expr);
ExprResult<TypeId> checkExpr(const ScopePtr& scope, const AstExprVarargs& expr);
ExprResult<TypeId> checkExpr(const ScopePtr& scope, const AstExprCall& expr);
ExprResult<TypeId> checkExpr(const ScopePtr& scope, const AstExprIndexName& expr);
ExprResult<TypeId> checkExpr(const ScopePtr& scope, const AstExprIndexExpr& expr);
ExprResult<TypeId> checkExpr(const ScopePtr& scope, const AstExprFunction& expr, std::optional<TypeId> expectedType = std::nullopt);
ExprResult<TypeId> checkExpr(const ScopePtr& scope, const AstExprTable& expr, std::optional<TypeId> expectedType = std::nullopt);
ExprResult<TypeId> checkExpr(const ScopePtr& scope, const AstExprUnary& expr);
WithPredicate<TypeId> checkExpr(
const ScopePtr& scope,
const AstExpr& expr,
std::optional<TypeId> expectedType = std::nullopt,
bool forceSingleton = false
);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprLocal& expr);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprGlobal& expr);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprVarargs& expr);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprCall& expr);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprIndexName& expr);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprIndexExpr& expr);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprFunction& expr, std::optional<TypeId> expectedType = std::nullopt);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprTable& expr, std::optional<TypeId> expectedType = std::nullopt);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprUnary& expr);
TypeId checkRelationalOperation(
const ScopePtr& scope, const AstExprBinary& expr, TypeId lhsType, TypeId rhsType, const PredicateVec& predicates = {});
const ScopePtr& scope,
const AstExprBinary& expr,
TypeId lhsType,
TypeId rhsType,
const PredicateVec& predicates = {}
);
TypeId checkBinaryOperation(
const ScopePtr& scope, const AstExprBinary& expr, TypeId lhsType, TypeId rhsType, const PredicateVec& predicates = {});
ExprResult<TypeId> checkExpr(const ScopePtr& scope, const AstExprBinary& expr);
ExprResult<TypeId> checkExpr(const ScopePtr& scope, const AstExprTypeAssertion& expr);
ExprResult<TypeId> checkExpr(const ScopePtr& scope, const AstExprError& expr);
ExprResult<TypeId> checkExpr(const ScopePtr& scope, const AstExprIfElse& expr);
const ScopePtr& scope,
const AstExprBinary& expr,
TypeId lhsType,
TypeId rhsType,
const PredicateVec& predicates = {}
);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprBinary& expr, std::optional<TypeId> expectedType = std::nullopt);
WithPredicate<TypeId> checkExpr_DEPRECATED(const ScopePtr& scope, const AstExprBinary& expr, std::optional<TypeId> expectedType = std::nullopt);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprTypeAssertion& expr);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprError& expr);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprIfElse& expr, std::optional<TypeId> expectedType = std::nullopt);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprInterpString& expr);
TypeId checkExprTable(const ScopePtr& scope, const AstExprTable& expr, const std::vector<std::pair<TypeId, TypeId>>& fieldTypes,
std::optional<TypeId> expectedType);
TypeId checkExprTable(
const ScopePtr& scope,
const AstExprTable& expr,
const std::vector<std::pair<TypeId, TypeId>>& fieldTypes,
std::optional<TypeId> expectedType
);
// Returns the type of the lvalue.
TypeId checkLValue(const ScopePtr& scope, const AstExpr& expr);
TypeId checkLValue(const ScopePtr& scope, const AstExpr& expr, ValueContext ctx);
// Returns both the type of the lvalue and its binding (if the caller wants to mutate the binding).
// Note: the binding may be null.
std::pair<TypeId, TypeId*> checkLValueBinding(const ScopePtr& scope, const AstExpr& expr);
std::pair<TypeId, TypeId*> checkLValueBinding(const ScopePtr& scope, const AstExprLocal& expr);
std::pair<TypeId, TypeId*> checkLValueBinding(const ScopePtr& scope, const AstExprGlobal& expr);
std::pair<TypeId, TypeId*> checkLValueBinding(const ScopePtr& scope, const AstExprIndexName& expr);
std::pair<TypeId, TypeId*> checkLValueBinding(const ScopePtr& scope, const AstExprIndexExpr& expr);
// Returns the type of the lvalue.
TypeId checkLValueBinding(const ScopePtr& scope, const AstExpr& expr, ValueContext ctx);
TypeId checkLValueBinding(const ScopePtr& scope, const AstExprLocal& expr);
TypeId checkLValueBinding(const ScopePtr& scope, const AstExprGlobal& expr);
TypeId checkLValueBinding(const ScopePtr& scope, const AstExprIndexName& expr, ValueContext ctx);
TypeId checkLValueBinding(const ScopePtr& scope, const AstExprIndexExpr& expr, ValueContext ctx);
TypeId checkFunctionName(const ScopePtr& scope, AstExpr& funName);
std::pair<TypeId, ScopePtr> checkFunctionSignature(const ScopePtr& scope, int subLevel, const AstExprFunction& expr,
std::optional<Location> originalNameLoc, std::optional<TypeId> expectedType);
TypeId checkFunctionName(const ScopePtr& scope, AstExpr& funName, TypeLevel level);
std::pair<TypeId, ScopePtr> checkFunctionSignature(
const ScopePtr& scope,
int subLevel,
const AstExprFunction& expr,
std::optional<Location> originalNameLoc,
std::optional<TypeId> selfType,
std::optional<TypeId> expectedType
);
void checkFunctionBody(const ScopePtr& scope, TypeId type, const AstExprFunction& function);
void checkArgumentList(
const ScopePtr& scope, Unifier& state, TypePackId paramPack, TypePackId argPack, const std::vector<Location>& argLocations);
const ScopePtr& scope,
const AstExpr& funName,
Unifier& state,
TypePackId paramPack,
TypePackId argPack,
const std::vector<Location>& argLocations
);
WithPredicate<TypePackId> checkExprPack(const ScopePtr& scope, const AstExpr& expr);
WithPredicate<TypePackId> checkExprPackHelper(const ScopePtr& scope, const AstExpr& expr);
WithPredicate<TypePackId> checkExprPackHelper(const ScopePtr& scope, const AstExprCall& expr);
WithPredicate<TypePackId> checkExprPackHelper2(
const ScopePtr& scope,
const AstExprCall& expr,
TypeId selfType,
TypeId actualFunctionType,
TypeId functionType,
TypePackId retPack
);
ExprResult<TypePackId> checkExprPack(const ScopePtr& scope, const AstExpr& expr);
ExprResult<TypePackId> checkExprPack(const ScopePtr& scope, const AstExprCall& expr);
std::vector<std::optional<TypeId>> getExpectedTypesForCall(const std::vector<TypeId>& overloads, size_t argumentCount, bool selfCall);
std::optional<ExprResult<TypePackId>> checkCallOverload(const ScopePtr& scope, const AstExprCall& expr, TypeId fn, TypePackId retPack,
TypePackId argPack, TypePack* args, const std::vector<Location>& argLocations, const ExprResult<TypePackId>& argListResult,
std::vector<TypeId>& overloadsThatMatchArgCount, std::vector<TypeId>& overloadsThatDont, std::vector<OverloadErrorEntry>& errors);
bool handleSelfCallMismatch(const ScopePtr& scope, const AstExprCall& expr, TypePack* args, const std::vector<Location>& argLocations,
const std::vector<OverloadErrorEntry>& errors);
void reportOverloadResolutionError(const ScopePtr& scope, const AstExprCall& expr, TypePackId retPack, TypePackId argPack,
const std::vector<Location>& argLocations, const std::vector<TypeId>& overloads, const std::vector<TypeId>& overloadsThatMatchArgCount,
const std::vector<OverloadErrorEntry>& errors);
ExprResult<TypePackId> checkExprList(const ScopePtr& scope, const Location& location, const AstArray<AstExpr*>& exprs,
bool substituteFreeForNil = false, const std::vector<bool>& lhsAnnotations = {},
const std::vector<std::optional<TypeId>>& expectedTypes = {});
std::unique_ptr<WithPredicate<TypePackId>> checkCallOverload(
const ScopePtr& scope,
const AstExprCall& expr,
TypeId fn,
TypePackId retPack,
TypePackId argPack,
TypePack* args,
const std::vector<Location>* argLocations,
const WithPredicate<TypePackId>& argListResult,
std::vector<TypeId>& overloadsThatMatchArgCount,
std::vector<TypeId>& overloadsThatDont,
std::vector<OverloadErrorEntry>& errors
);
bool handleSelfCallMismatch(
const ScopePtr& scope,
const AstExprCall& expr,
TypePack* args,
const std::vector<Location>& argLocations,
const std::vector<OverloadErrorEntry>& errors
);
void reportOverloadResolutionError(
const ScopePtr& scope,
const AstExprCall& expr,
TypePackId retPack,
TypePackId argPack,
const std::vector<Location>& argLocations,
const std::vector<TypeId>& overloads,
const std::vector<TypeId>& overloadsThatMatchArgCount,
std::vector<OverloadErrorEntry>& errors
);
WithPredicate<TypePackId> checkExprList(
const ScopePtr& scope,
const Location& location,
const AstArray<AstExpr*>& exprs,
bool substituteFreeForNil = false,
const std::vector<bool>& lhsAnnotations = {},
const std::vector<std::optional<TypeId>>& expectedTypes = {}
);
static std::optional<AstExpr*> matchRequire(const AstExprCall& call);
TypeId checkRequire(const ScopePtr& scope, const ModuleInfo& moduleInfo, const Location& location);
@ -193,55 +235,53 @@ struct TypeChecker
// Reports an error if the type is already some kind of non-table.
void tablify(TypeId type);
/** In nonstrict mode, many typevars need to be replaced by any.
/** In nonstrict mode, many types need to be replaced by any.
*/
TypeId anyIfNonstrict(TypeId ty) const;
/** Attempt to unify the types left and right. Treat any failures as type errors
* in the final typecheck report.
/** Attempt to unify the types.
* Treat any failures as type errors in the final typecheck report.
*/
bool unify(TypeId left, TypeId right, const Location& location);
bool unify(TypePackId left, TypePackId right, const Location& location, CountMismatch::Context ctx = CountMismatch::Context::Arg);
bool unify(TypeId subTy, TypeId superTy, const ScopePtr& scope, const Location& location);
bool unify(TypeId subTy, TypeId superTy, const ScopePtr& scope, const Location& location, const UnifierOptions& options);
bool unify(
TypePackId subTy,
TypePackId superTy,
const ScopePtr& scope,
const Location& location,
CountMismatch::Context ctx = CountMismatch::Context::Arg
);
/** Attempt to unify the types left and right.
* If this fails, and the right type can be instantiated, do so and try unification again.
/** Attempt to unify the types.
* If this fails, and the subTy type can be instantiated, do so and try unification again.
*/
bool unifyWithInstantiationIfNeeded(const ScopePtr& scope, TypeId left, TypeId right, const Location& location);
void unifyWithInstantiationIfNeeded(const ScopePtr& scope, TypeId left, TypeId right, Unifier& state);
bool unifyWithInstantiationIfNeeded(TypeId subTy, TypeId superTy, const ScopePtr& scope, const Location& location);
void unifyWithInstantiationIfNeeded(TypeId subTy, TypeId superTy, const ScopePtr& scope, Unifier& state);
/** Attempt to unify left with right.
/** Attempt to unify.
* If there are errors, undo everything and return the errors.
* If there are no errors, commit and return an empty error vector.
*/
ErrorVec tryUnify(TypeId left, TypeId right, const Location& location);
ErrorVec tryUnify(TypePackId left, TypePackId right, const Location& location);
template<typename Id>
ErrorVec tryUnify_(Id subTy, Id superTy, const ScopePtr& scope, const Location& location);
ErrorVec tryUnify(TypeId subTy, TypeId superTy, const ScopePtr& scope, const Location& location);
ErrorVec tryUnify(TypePackId subTy, TypePackId superTy, const ScopePtr& scope, const Location& location);
// Test whether the two type vars unify. Never commits the result.
ErrorVec canUnify(TypeId superTy, TypeId subTy, const Location& location);
ErrorVec canUnify(TypePackId superTy, TypePackId subTy, const Location& location);
template<typename Id>
ErrorVec canUnify_(Id subTy, Id superTy, const ScopePtr& scope, const Location& location);
ErrorVec canUnify(TypeId subTy, TypeId superTy, const ScopePtr& scope, const Location& location);
ErrorVec canUnify(TypePackId subTy, TypePackId superTy, const ScopePtr& scope, const Location& location);
// Variant that takes a preexisting 'seen' set. We need this in certain cases to avoid infinitely recursing
// into cyclic types.
ErrorVec canUnify(const std::vector<std::pair<TypeId, TypeId>>& seen, TypeId left, TypeId right, const Location& location);
std::optional<TypeId> findMetatableEntry(TypeId type, std::string entry, const Location& location);
std::optional<TypeId> findTablePropertyRespectingMeta(TypeId lhsType, Name name, const Location& location);
std::optional<TypeId> findMetatableEntry(TypeId type, std::string entry, const Location& location, bool addErrors);
std::optional<TypeId> findTablePropertyRespectingMeta(TypeId lhsType, Name name, const Location& location, bool addErrors);
std::optional<TypeId> getIndexTypeFromType(const ScopePtr& scope, TypeId type, const Name& name, const Location& location, bool addErrors);
// Reduces the union to its simplest possible shape.
// (A | B) | B | C yields A | B | C
std::vector<TypeId> reduceUnion(const std::vector<TypeId>& types);
std::optional<TypeId> getIndexTypeFromTypeImpl(const ScopePtr& scope, TypeId type, const Name& name, const Location& location, bool addErrors);
std::optional<TypeId> tryStripUnionFromNil(TypeId ty);
TypeId stripFromNilAndReport(TypeId ty, const Location& location);
template<typename Id>
ErrorVec tryUnify_(Id left, Id right, const Location& location);
template<typename Id>
ErrorVec canUnify_(Id left, Id right, const Location& location);
public:
/*
* Convert monotype into a a polytype, by replacing any metavariables in descendant scopes
@ -262,22 +302,26 @@ public:
* {method: ({method: (<CYCLE>) -> a}) -> a}
*
*/
TypeId instantiate(const ScopePtr& scope, TypeId ty, Location location);
TypeId instantiate(const ScopePtr& scope, TypeId ty, Location location, const TxnLog* log = TxnLog::empty());
// Replace any free types or type packs by `any`.
// This is used when exporting types from modules, to make sure free types don't leak.
TypeId anyify(const ScopePtr& scope, TypeId ty, Location location);
TypePackId anyify(const ScopePtr& scope, TypePackId ty, Location location);
TypePackId anyifyModuleReturnTypePackGenerics(TypePackId ty);
void reportError(const TypeError& error);
void reportError(const Location& location, TypeErrorData error);
void reportErrors(const ErrorVec& errors);
[[noreturn]] void ice(const std::string& message, const Location& location);
[[noreturn]] void ice(const std::string& message);
[[noreturn]] void throwTimeLimitError();
[[noreturn]] void throwUserCancelError();
ScopePtr childFunctionScope(const ScopePtr& parent, const Location& location, int subLevel = 0);
ScopePtr childScope(const ScopePtr& parent, const Location& location, int subLevel = 0);
ScopePtr childScope(const ScopePtr& parent, const Location& location);
// Wrapper for merge(l, r, toUnion) but without the lambda junk.
void merge(RefinementMap& l, const RefinementMap& r);
@ -296,8 +340,7 @@ private:
void reportErrorCodeTooComplex(const Location& location);
private:
Unifier mkUnifier(const Location& location);
Unifier mkUnifier(const std::vector<std::pair<TypeId, TypeId>>& seen, const Location& location);
Unifier mkUnifier(const ScopePtr& scope, const Location& location);
// These functions are only safe to call when we are in the process of typechecking a module.
@ -309,22 +352,27 @@ private:
TypeId singletonType(bool value);
TypeId singletonType(std::string value);
// Returns nullopt if the predicate filters down the TypeId to 0 options.
std::optional<TypeId> filterMap(TypeId type, TypeIdPredicate predicate);
TypeIdPredicate mkTruthyPredicate(bool sense, TypeId emptySetTy);
TypeId unionOfTypes(TypeId a, TypeId b, const Location& location, bool unifyFreeTypes = true);
// TODO: Return TypeId only.
std::optional<TypeId> filterMapImpl(TypeId type, TypeIdPredicate predicate);
std::pair<std::optional<TypeId>, bool> filterMap(TypeId type, TypeIdPredicate predicate);
public:
std::pair<std::optional<TypeId>, bool> pickTypesFromSense(TypeId type, bool sense, TypeId emptySetTy);
private:
TypeId unionOfTypes(TypeId a, TypeId b, const ScopePtr& scope, const Location& location, bool unifyFreeTypes = true);
// ex
// TypeId id = addType(FreeTypeVar());
// TypeId id = addType(FreeType());
template<typename T>
TypeId addType(const T& tv)
{
return addTV(TypeVar(tv));
return addTV(Type(tv));
}
TypeId addType(const UnionTypeVar& utv);
TypeId addTV(TypeVar&& tv);
TypeId addTV(Type&& tv);
TypePackId addTypePack(TypePackVar&& tp);
TypePackId addTypePack(TypePack&& tp);
@ -336,32 +384,47 @@ private:
TypePackId freshTypePack(TypeLevel level);
TypeId resolveType(const ScopePtr& scope, const AstType& annotation);
TypeId resolveTypeWorker(const ScopePtr& scope, const AstType& annotation);
TypePackId resolveTypePack(const ScopePtr& scope, const AstTypeList& types);
TypePackId resolveTypePack(const ScopePtr& scope, const AstTypePack& annotation);
TypeId instantiateTypeFun(const ScopePtr& scope, const TypeFun& tf, const std::vector<TypeId>& typeParams,
const std::vector<TypePackId>& typePackParams, const Location& location);
TypeId instantiateTypeFun(
const ScopePtr& scope,
const TypeFun& tf,
const std::vector<TypeId>& typeParams,
const std::vector<TypePackId>& typePackParams,
const Location& location
);
// Note: `scope` must be a fresh scope.
std::pair<std::vector<TypeId>, std::vector<TypePackId>> createGenericTypes(const ScopePtr& scope, std::optional<TypeLevel> levelOpt,
const AstNode& node, const AstArray<AstName>& genericNames, const AstArray<AstName>& genericPackNames);
GenericTypeDefinitions createGenericTypes(
const ScopePtr& scope,
std::optional<TypeLevel> levelOpt,
const AstNode& node,
const AstArray<AstGenericType*>& genericNames,
const AstArray<AstGenericTypePack*>& genericPackNames,
bool useCache = false
);
public:
ErrorVec resolve(const PredicateVec& predicates, const ScopePtr& scope, bool sense);
void resolve(const PredicateVec& predicates, const ScopePtr& scope, bool sense);
private:
void refineLValue(const LValue& lvalue, RefinementMap& refis, const ScopePtr& scope, TypeIdPredicate predicate);
std::optional<TypeId> resolveLValue(const ScopePtr& scope, const LValue& lvalue);
std::optional<TypeId> resolveLValue(const RefinementMap& refis, const ScopePtr& scope, const LValue& lvalue);
void resolve(const PredicateVec& predicates, ErrorVec& errVec, RefinementMap& refis, const ScopePtr& scope, bool sense, bool fromOr = false);
void resolve(const Predicate& predicate, ErrorVec& errVec, RefinementMap& refis, const ScopePtr& scope, bool sense, bool fromOr);
void resolve(const TruthyPredicate& truthyP, ErrorVec& errVec, RefinementMap& refis, const ScopePtr& scope, bool sense, bool fromOr);
void resolve(const AndPredicate& andP, ErrorVec& errVec, RefinementMap& refis, const ScopePtr& scope, bool sense);
void resolve(const OrPredicate& orP, ErrorVec& errVec, RefinementMap& refis, const ScopePtr& scope, bool sense);
void resolve(const IsAPredicate& isaP, ErrorVec& errVec, RefinementMap& refis, const ScopePtr& scope, bool sense);
void resolve(const TypeGuardPredicate& typeguardP, ErrorVec& errVec, RefinementMap& refis, const ScopePtr& scope, bool sense);
void resolve(const EqPredicate& eqP, ErrorVec& errVec, RefinementMap& refis, const ScopePtr& scope, bool sense);
void resolve(const PredicateVec& predicates, RefinementMap& refis, const ScopePtr& scope, bool sense, bool fromOr = false);
void resolve(const Predicate& predicate, RefinementMap& refis, const ScopePtr& scope, bool sense, bool fromOr);
void resolve(const TruthyPredicate& truthyP, RefinementMap& refis, const ScopePtr& scope, bool sense, bool fromOr);
void resolve(const AndPredicate& andP, RefinementMap& refis, const ScopePtr& scope, bool sense);
void resolve(const OrPredicate& orP, RefinementMap& refis, const ScopePtr& scope, bool sense);
void resolve(const IsAPredicate& isaP, RefinementMap& refis, const ScopePtr& scope, bool sense);
void resolve(const TypeGuardPredicate& typeguardP, RefinementMap& refis, const ScopePtr& scope, bool sense);
void resolve(const EqPredicate& eqP, RefinementMap& refis, const ScopePtr& scope, bool sense);
bool isNonstrictMode() const;
bool useConstrainedIntersections() const;
public:
/** Extract the types in a type pack, given the assumption that the pack must have some exact length.
@ -369,27 +432,32 @@ public:
* Calling this function means submitting evidence that the pack must have the length provided.
* If the pack is known not to have the correct length, an error will be reported.
* The return vector is always of the exact requested length. In the event that the pack's length does
* not match up, excess TypeIds will be ErrorTypeVars.
* not match up, excess TypeIds will be ErrorTypes.
*/
std::vector<TypeId> unTypePack(const ScopePtr& scope, TypePackId pack, size_t expectedLength, const Location& location);
TypeArena globalTypes;
const ScopePtr& globalScope;
ModuleResolver* resolver;
SourceModule globalNames; // names for symbols entered into globalScope
ScopePtr globalScope; // shared by all modules
ModulePtr currentModule;
ModuleName currentModuleName;
Instantiation instantiation;
Quantification quantification;
Anyification anyification;
ApplyTypeFunction applyTypeFunction;
std::function<void(const ModuleName&, const ScopePtr&)> prepareModuleScope;
NotNull<BuiltinTypes> builtinTypes;
InternalErrorReporter* iceHandler;
UnifierSharedState unifierState;
Normalizer normalizer;
Instantiation reusableInstantiation;
std::vector<RequireCycle> requireCycles;
// Type inference limits
std::optional<double> finishTime;
std::optional<int> instantiationChildLimit;
std::optional<int> unifierIterationLimit;
std::shared_ptr<FrontendCancellationToken> cancellationToken;
public:
const TypeId nilType;
@ -397,18 +465,39 @@ public:
const TypeId stringType;
const TypeId booleanType;
const TypeId threadType;
const TypeId bufferType;
const TypeId anyType;
const TypeId optionalNumberType;
const TypeId unknownType;
const TypeId neverType;
const TypePackId anyTypePack;
const TypePackId neverTypePack;
const TypePackId uninhabitableTypePack;
private:
int checkRecursionCount = 0;
int recursionCount = 0;
/**
* We use this to avoid doing second-pass analysis of type aliases that are duplicates. We record a pair
* (exported, name) to properly deal with the case where the two duplicates do not have the same export status.
*/
DenseHashSet<std::pair<bool, Name>, HashBoolNamePair> duplicateTypeAliases;
/**
* A set of incorrect class definitions which is used to avoid a second-pass analysis.
*/
DenseHashSet<const AstStatDeclareExternType*> incorrectExternTypeDefinitions{nullptr};
std::vector<std::pair<TypeId, ScopePtr>> deferredQuantification;
};
using PrintLineProc = void (*)(const std::string&);
extern PrintLineProc luauPrintLine;
// Unit test hook
void setPrintLine(void (*pl)(const std::string& s));
void setPrintLine(PrintLineProc pl);
void resetPrintLine();
} // namespace Luau

View file

@ -0,0 +1,41 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Type.h"
#include "Luau/TypePack.h"
#include "Luau/Variant.h"
#include <type_traits>
namespace Luau
{
const void* ptr(TypeOrPack ty);
template<typename T, typename std::enable_if_t<TypeOrPack::is_part_of_v<T>, bool> = true>
const T* get(const TypeOrPack& tyOrTp)
{
return tyOrTp.get_if<T>();
}
template<typename T, typename std::enable_if_t<TypeVariant::is_part_of_v<T>, bool> = true>
const T* get(const TypeOrPack& tyOrTp)
{
if (const TypeId* ty = get<TypeId>(tyOrTp))
return get<T>(*ty);
else
return nullptr;
}
template<typename T, typename std::enable_if_t<TypePackVariant::is_part_of_v<T>, bool> = true>
const T* get(const TypeOrPack& tyOrTp)
{
if (const TypePackId* tp = get<TypePackId>(tyOrTp))
return get<T>(*tp);
else
return nullptr;
}
TypeOrPack follow(TypeOrPack ty);
} // namespace Luau

View file

@ -1,28 +1,66 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/TypeVar.h"
#include "Luau/Common.h"
#include "Luau/NotNull.h"
#include "Luau/Polarity.h"
#include "Luau/TypeFwd.h"
#include "Luau/Unifiable.h"
#include "Luau/Variant.h"
#include <optional>
#include <set>
#include <vector>
namespace Luau
{
struct TypeArena;
struct TypePackFunction;
struct TxnLog;
struct TypePack;
struct VariadicTypePack;
struct BlockedTypePack;
struct TypeFunctionInstanceTypePack;
struct TypePackVar;
struct FreeTypePack
{
explicit FreeTypePack(TypeLevel level);
explicit FreeTypePack(Scope* scope, Polarity polarity = Polarity::Unknown);
FreeTypePack(Scope* scope, TypeLevel level);
int index;
TypeLevel level;
Scope* scope = nullptr;
Polarity polarity = Polarity::Unknown;
};
struct GenericTypePack
{
// By default, generics are global, with a synthetic name
GenericTypePack();
explicit GenericTypePack(TypeLevel level);
explicit GenericTypePack(const Name& name);
explicit GenericTypePack(Scope* scope, Polarity polarity = Polarity::Unknown);
GenericTypePack(TypeLevel level, const Name& name);
GenericTypePack(Scope* scope, const Name& name);
int index;
TypeLevel level;
Scope* scope = nullptr;
Name name;
bool explicitName = false;
Polarity polarity = Polarity::Unknown;
};
using TypePackId = const TypePackVar*;
using FreeTypePack = Unifiable::Free;
using BoundTypePack = Unifiable::Bound<TypePackId>;
using GenericTypePack = Unifiable::Generic;
using TypePackVariant = Unifiable::Variant<TypePackId, TypePack, VariadicTypePack>;
using ErrorTypePack = Unifiable::Error<TypePackId>;
using TypePackVariant =
Unifiable::Variant<TypePackId, FreeTypePack, GenericTypePack, TypePack, VariadicTypePack, BlockedTypePack, TypeFunctionInstanceTypePack>;
/* A TypePack is a rope-like string of TypeIds. We use this structure to encode
* notions like packs of unknown length and packs of any length, as well as more
@ -38,29 +76,62 @@ struct TypePack
struct VariadicTypePack
{
TypeId ty;
bool hidden = false; // if true, we don't display this when toString()ing a pack with this variadic as its tail.
};
/**
* Analogous to a BlockedType.
*/
struct BlockedTypePack
{
BlockedTypePack();
size_t index;
struct Constraint* owner = nullptr;
static size_t nextIndex;
};
/**
* Analogous to a TypeFunctionInstanceType.
*/
struct TypeFunctionInstanceTypePack
{
NotNull<const TypePackFunction> function;
std::vector<TypeId> typeArguments;
std::vector<TypePackId> packArguments;
};
struct TypePackVar
{
explicit TypePackVar(const TypePackVariant& ty);
explicit TypePackVar(TypePackVariant&& ty);
TypePackVar(TypePackVariant&& ty, bool persistent);
explicit TypePackVar(const TypePackVariant& tp);
explicit TypePackVar(TypePackVariant&& tp);
TypePackVar(TypePackVariant&& tp, bool persistent);
bool operator==(const TypePackVar& rhs) const;
TypePackVar& operator=(TypePackVariant&& tp);
TypePackVar& operator=(const TypePackVar& rhs);
// Re-assignes the content of the pack, but doesn't change the owning arena and can't make pack persistent.
void reassign(const TypePackVar& rhs)
{
ty = rhs.ty;
}
TypePackVariant ty;
bool persistent = false;
// Pointer to the type arena that allocated this type.
// Do not depend on the value of this under any circumstances. This is for
// debugging purposes only. This is only set in debug builds; it is nullptr
// in all other environments.
// Pointer to the type arena that allocated this pack.
TypeArena* owningArena = nullptr;
};
/* Walk the set of TypeIds in a TypePack.
*
* Like TypeVars, individual TypePacks can be free, generic, or any.
* Like Types, individual TypePacks can be free, generic, or any.
*
* We afford the ability to work with these kinds of packs by giving the
* iterator a .tail() property that yields the tail-most TypePack in the
@ -84,6 +155,7 @@ struct TypePackIterator
TypePackIterator() = default;
explicit TypePackIterator(TypePackId tp);
TypePackIterator(TypePackId tp, const TxnLog* log);
TypePackIterator& operator++();
TypePackIterator operator++(int);
@ -102,23 +174,30 @@ struct TypePackIterator
private:
TypePackId currentTypePack = nullptr;
TypePackId tailCycleCheck = nullptr;
const TypePack* tp = nullptr;
size_t currentIndex = 0;
const TxnLog* log;
};
TypePackIterator begin(TypePackId tp);
TypePackIterator begin(TypePackId tp, const TxnLog* log);
TypePackIterator end(TypePackId tp);
using SeenSet = std::set<std::pair<void*, void*>>;
TypePackId getTail(TypePackId tp);
using SeenSet = std::set<std::pair<const void*, const void*>>;
bool areEqual(SeenSet& seen, const TypePackVar& lhs, const TypePackVar& rhs);
TypePackId follow(TypePackId tp);
TypePackId follow(TypePackId t, const void* context, TypePackId (*mapper)(const void*, TypePackId));
size_t size(TypePackId tp);
bool finite(TypePackId tp);
size_t size(const TypePack& tp);
std::optional<TypeId> first(TypePackId tp);
size_t size(TypePackId tp, TxnLog* log = nullptr);
bool finite(TypePackId tp, TxnLog* log = nullptr);
size_t size(const TypePack& tp, TxnLog* log = nullptr);
std::optional<TypeId> first(TypePackId tp, bool ignoreHiddenVariadics = true);
TypePackVar* asMutable(TypePackId tp);
TypePack* asMutable(const TypePack* tp);
@ -150,5 +229,30 @@ bool isEmpty(TypePackId tp);
/// Flattens out a type pack. Also returns a valid TypePackId tail if the type pack's full size is not known
std::pair<std::vector<TypeId>, std::optional<TypePackId>> flatten(TypePackId tp);
std::pair<std::vector<TypeId>, std::optional<TypePackId>> flatten(TypePackId tp, const TxnLog& log);
/// Returs true if the type pack arose from a function that is declared to be variadic.
/// Returns *false* for function argument packs that are inferred to be safe to oversaturate!
bool isVariadic(TypePackId tp);
bool isVariadic(TypePackId tp, const TxnLog& log);
// Returns true if the TypePack is Generic or Variadic. Does not walk TypePacks!!
bool isVariadicTail(TypePackId tp, const TxnLog& log, bool includeHiddenVariadics = false);
bool containsNever(TypePackId tp);
/*
* Use this to change the kind of a particular type pack.
*
* LUAU_NOINLINE so that the calling frame doesn't have to pay the stack storage for the new variant.
*/
template<typename T, typename... Args>
LUAU_NOINLINE T* emplaceTypePack(TypePackVar* ty, Args&&... args)
{
return &ty->ty.emplace<T>(std::forward<Args>(args)...);
}
template<>
LUAU_NOINLINE Unifiable::Bound<TypePackId>* emplaceTypePack<BoundTypePack>(TypePackVar* ty, TypePackId& tyArg);
} // namespace Luau

View file

@ -0,0 +1,35 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/TypeFwd.h"
#include <stdint.h>
#include <utility>
namespace Luau
{
struct TypePairHash
{
size_t hashOne(TypeId key) const
{
return (uintptr_t(key) >> 4) ^ (uintptr_t(key) >> 9);
}
size_t hashOne(TypePackId key) const
{
return (uintptr_t(key) >> 4) ^ (uintptr_t(key) >> 9);
}
size_t operator()(const std::pair<TypeId, TypeId>& x) const
{
return hashOne(x.first) ^ (hashOne(x.second) << 1);
}
size_t operator()(const std::pair<TypePackId, TypePackId>& x) const
{
return hashOne(x.first) ^ (hashOne(x.second) << 1);
}
};
} // namespace Luau

View file

@ -0,0 +1,252 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/TypeFwd.h"
#include "Luau/Variant.h"
#include "Luau/NotNull.h"
#include <optional>
#include <string>
#include <vector>
namespace Luau
{
namespace TypePath
{
/// Represents a property of a class, table, or anything else with a concept of
/// a named property.
struct Property
{
/// The name of the property.
std::string name;
/// Whether to look at the read or the write type.
bool isRead = true;
explicit Property(std::string name);
Property(std::string name, bool read)
: name(std::move(name))
, isRead(read)
{
}
static Property read(std::string name);
static Property write(std::string name);
bool operator==(const Property& other) const;
};
/// Represents an index into a type or a pack. For a type, this indexes into a
/// union or intersection's list. For a pack, this indexes into the pack's nth
/// element.
struct Index
{
enum class Variant
{
Pack,
Union,
Intersection
};
/// The 0-based index to use for the lookup.
size_t index;
/// The sort of thing we're indexing from, this is used in stringifying the type path for errors.
Variant variant;
bool operator==(const Index& other) const;
};
/// Represents fields of a type or pack that contain a type.
enum class TypeField
{
/// The table of a metatable type.
Table,
/// The metatable of a type. This could be a metatable type, a primitive
/// type, a class type, or perhaps even a string singleton type.
Metatable,
/// The lower bound of this type, if one is present.
LowerBound,
/// The upper bound of this type, if present.
UpperBound,
/// The index type.
IndexLookup,
/// The indexer result type.
IndexResult,
/// The negated type, for negations.
Negated,
/// The variadic type for a type pack.
Variadic,
};
/// Represents fields of a type or type pack that contain a type pack.
enum class PackField
{
/// What arguments this type accepts.
Arguments,
/// What this type returns when called.
Returns,
/// The tail of a type pack.
Tail,
};
/// Component that represents the result of a reduction
/// `resultType` is `never` if the reduction could not proceed
struct Reduction
{
TypeId resultType;
bool operator==(const Reduction& other) const;
};
/// A single component of a path, representing one inner type or type pack to
/// traverse into.
using Component = Luau::Variant<Property, Index, TypeField, PackField, Reduction>;
/// A path through a type or type pack accessing a particular type or type pack
/// contained within.
///
/// Paths are always relative; to make use of a Path, you need to specify an
/// entry point. They are not canonicalized; two Paths may not compare equal but
/// may point to the same result, depending on the layout of the entry point.
///
/// Paths always descend through an entry point. This doesn't mean that they
/// cannot reach "upwards" in the actual type hierarchy in some cases, but it
/// does mean that there is no equivalent to `../` in file system paths. This is
/// intentional and unavoidable, because types and type packs don't have a
/// concept of a parent - they are a directed cyclic graph, with no hierarchy
/// that actually holds in all cases.
struct Path
{
/// The Components of this Path.
std::vector<Component> components;
/// Creates a new empty Path.
Path() {}
/// Creates a new Path from a list of components.
explicit Path(std::vector<Component> components)
: components(std::move(components))
{
}
/// Creates a new single-component Path.
explicit Path(Component component)
: components({component})
{
}
/// Creates a new Path by appending another Path to this one.
/// @param suffix the Path to append
/// @return a new Path representing `this + suffix`
Path append(const Path& suffix) const;
/// Creates a new Path by appending a Component to this Path.
/// @param component the Component to append
/// @return a new Path with `component` appended to it.
Path push(Component component) const;
/// Creates a new Path by prepending a Component to this Path.
/// @param component the Component to prepend
/// @return a new Path with `component` prepended to it.
Path push_front(Component component) const;
/// Creates a new Path by removing the last Component of this Path.
/// If the Path is empty, this is a no-op.
/// @return a Path with the last component removed.
Path pop() const;
/// Returns the last Component of this Path, if present.
std::optional<Component> last() const;
/// Returns whether this Path is empty, meaning it has no components at all.
/// Traversing an empty Path results in the type you started with.
bool empty() const;
bool operator==(const Path& other) const;
bool operator!=(const Path& other) const
{
return !(*this == other);
}
};
struct PathHash
{
size_t operator()(const Property& prop) const;
size_t operator()(const Index& idx) const;
size_t operator()(const TypeField& field) const;
size_t operator()(const PackField& field) const;
size_t operator()(const Reduction& reduction) const;
size_t operator()(const Component& component) const;
size_t operator()(const Path& path) const;
};
/// The canonical "empty" Path, meaning a Path with no components.
static const Path kEmpty{};
struct PathBuilder
{
std::vector<Component> components;
Path build();
PathBuilder& readProp(std::string name);
PathBuilder& writeProp(std::string name);
PathBuilder& prop(std::string name);
PathBuilder& index(size_t i);
PathBuilder& mt();
PathBuilder& lb();
PathBuilder& ub();
PathBuilder& indexKey();
PathBuilder& indexValue();
PathBuilder& negated();
PathBuilder& variadic();
PathBuilder& args();
PathBuilder& rets();
PathBuilder& tail();
};
} // namespace TypePath
using Path = TypePath::Path;
/// Converts a Path to a string for debugging purposes. This output may not be
/// terribly clear to end users of the Luau type system.
std::string toString(const TypePath::Path& path, bool prefixDot = false);
/// Converts a Path to a human readable string for error reporting.
std::string toStringHuman(const TypePath::Path& path);
std::optional<TypeOrPack> traverse(TypeId root, const Path& path, NotNull<BuiltinTypes> builtinTypes);
std::optional<TypeOrPack> traverse(TypePackId root, const Path& path, NotNull<BuiltinTypes> builtinTypes);
/// Traverses a path from a type to its end point, which must be a type.
/// @param root the entry point of the traversal
/// @param path the path to traverse
/// @param builtinTypes the built-in types in use (used to acquire the string metatable)
/// @returns the TypeId at the end of the path, or nullopt if the traversal failed.
std::optional<TypeId> traverseForType(TypeId root, const Path& path, NotNull<BuiltinTypes> builtinTypes);
/// Traverses a path from a type pack to its end point, which must be a type.
/// @param root the entry point of the traversal
/// @param path the path to traverse
/// @param builtinTypes the built-in types in use (used to acquire the string metatable)
/// @returns the TypeId at the end of the path, or nullopt if the traversal failed.
std::optional<TypeId> traverseForType(TypePackId root, const Path& path, NotNull<BuiltinTypes> builtinTypes);
/// Traverses a path from a type to its end point, which must be a type pack.
/// @param root the entry point of the traversal
/// @param path the path to traverse
/// @param builtinTypes the built-in types in use (used to acquire the string metatable)
/// @returns the TypePackId at the end of the path, or nullopt if the traversal failed.
std::optional<TypePackId> traverseForPack(TypeId root, const Path& path, NotNull<BuiltinTypes> builtinTypes);
/// Traverses a path from a type pack to its end point, which must be a type pack.
/// @param root the entry point of the traversal
/// @param path the path to traverse
/// @param builtinTypes the built-in types in use (used to acquire the string metatable)
/// @returns the TypePackId at the end of the path, or nullopt if the traversal failed.
std::optional<TypePackId> traverseForPack(TypePackId root, const Path& path, NotNull<BuiltinTypes> builtinTypes);
} // namespace Luau

View file

@ -3,7 +3,8 @@
#include "Luau/Error.h"
#include "Luau/Location.h"
#include "Luau/TypeVar.h"
#include "Luau/Type.h"
#include "Luau/TypePack.h"
#include <memory>
#include <optional>
@ -11,9 +12,283 @@
namespace Luau
{
struct TxnLog;
struct TypeArena;
class Normalizer;
enum class ValueContext
{
LValue,
RValue
};
/// the current context of the type checker
enum class TypeContext
{
/// the default context
Default,
/// inside of a condition
Condition,
};
bool inConditional(const TypeContext& context);
// sets the given type context to `Condition` and restores it to its original
// value when the struct drops out of scope
struct InConditionalContext
{
TypeContext* typeContext;
TypeContext oldValue;
explicit InConditionalContext(TypeContext* c)
: typeContext(c)
, oldValue(*c)
{
*typeContext = TypeContext::Condition;
}
~InConditionalContext()
{
*typeContext = oldValue;
}
};
using ScopePtr = std::shared_ptr<struct Scope>;
std::optional<TypeId> findMetatableEntry(ErrorVec& errors, const ScopePtr& globalScope, TypeId type, std::string entry, Location location);
std::optional<TypeId> findTablePropertyRespectingMeta(ErrorVec& errors, const ScopePtr& globalScope, TypeId ty, Name name, Location location);
std::optional<Property> findTableProperty(
NotNull<BuiltinTypes> builtinTypes,
ErrorVec& errors,
TypeId ty,
const std::string& name,
Location location
);
std::optional<TypeId> findMetatableEntry(
NotNull<BuiltinTypes> builtinTypes,
ErrorVec& errors,
TypeId type,
const std::string& entry,
Location location
);
std::optional<TypeId> findTablePropertyRespectingMeta(
NotNull<BuiltinTypes> builtinTypes,
ErrorVec& errors,
TypeId ty,
const std::string& name,
Location location
);
std::optional<TypeId> findTablePropertyRespectingMeta(
NotNull<BuiltinTypes> builtinTypes,
ErrorVec& errors,
TypeId ty,
const std::string& name,
ValueContext context,
Location location
);
bool occursCheck(TypeId needle, TypeId haystack);
// Returns the minimum and maximum number of types the argument list can accept.
std::pair<size_t, std::optional<size_t>> getParameterExtents(const TxnLog* log, TypePackId tp, bool includeHiddenVariadics = false);
// Extend the provided pack to at least `length` types.
// Returns a temporary TypePack that contains those types plus a tail.
TypePack extendTypePack(
TypeArena& arena,
NotNull<BuiltinTypes> builtinTypes,
TypePackId pack,
size_t length,
std::vector<std::optional<TypeId>> overrides = {}
);
/**
* Reduces a union by decomposing to the any/error type if it appears in the
* type list, and by merging child unions. Also strips out duplicate (by pointer
* identity) types.
* @param types the input type list to reduce.
* @returns the reduced type list.
*/
std::vector<TypeId> reduceUnion(const std::vector<TypeId>& types);
/**
* Tries to remove nil from a union type, if there's another option. T | nil
* reduces to T, but nil itself does not reduce.
* @param builtinTypes the singleton types to use
* @param arena the type arena to allocate the new type in, if necessary
* @param ty the type to remove nil from
* @returns a type with nil removed, or nil itself if that were the only option.
*/
TypeId stripNil(NotNull<BuiltinTypes> builtinTypes, TypeArena& arena, TypeId ty);
struct ErrorSuppression
{
enum Value
{
Suppress,
DoNotSuppress,
NormalizationFailed,
};
ErrorSuppression() = default;
constexpr ErrorSuppression(Value enumValue)
: value(enumValue)
{
}
constexpr operator Value() const
{
return value;
}
explicit operator bool() const = delete;
ErrorSuppression orElse(const ErrorSuppression& other) const
{
switch (value)
{
case DoNotSuppress:
return other;
default:
return *this;
}
}
private:
Value value;
};
/**
* Normalizes the given type using the normalizer to determine if the type
* should suppress any errors that would be reported involving it.
* @param normalizer the normalizer to use
* @param ty the type to check for error suppression
* @returns an enum indicating whether or not to suppress the error or to signal a normalization failure
*/
ErrorSuppression shouldSuppressErrors(NotNull<Normalizer> normalizer, TypeId ty);
/**
* Flattens and normalizes the given typepack using the normalizer to determine if the type
* should suppress any errors that would be reported involving it.
* @param normalizer the normalizer to use
* @param tp the typepack to check for error suppression
* @returns an enum indicating whether or not to suppress the error or to signal a normalization failure
*/
ErrorSuppression shouldSuppressErrors(NotNull<Normalizer> normalizer, TypePackId tp);
/**
* Normalizes the two given type using the normalizer to determine if either type
* should suppress any errors that would be reported involving it.
* @param normalizer the normalizer to use
* @param ty1 the first type to check for error suppression
* @param ty2 the second type to check for error suppression
* @returns an enum indicating whether or not to suppress the error or to signal a normalization failure
*/
ErrorSuppression shouldSuppressErrors(NotNull<Normalizer> normalizer, TypeId ty1, TypeId ty2);
/**
* Flattens and normalizes the two given typepacks using the normalizer to determine if either type
* should suppress any errors that would be reported involving it.
* @param normalizer the normalizer to use
* @param tp1 the first typepack to check for error suppression
* @param tp2 the second typepack to check for error suppression
* @returns an enum indicating whether or not to suppress the error or to signal a normalization failure
*/
ErrorSuppression shouldSuppressErrors(NotNull<Normalizer> normalizer, TypePackId tp1, TypePackId tp2);
// Similar to `std::optional<std::pair<A, B>>`, but whose `sizeof()` is the same as `std::pair<A, B>`
// and cooperates with C++'s `if (auto p = ...)` syntax without the extra fatness of `std::optional`.
template<typename A, typename B>
struct TryPair
{
A first;
B second;
explicit operator bool() const
{
return bool(first) && bool(second);
}
};
template<typename A, typename B, typename Ty>
TryPair<const A*, const B*> get2(Ty one, Ty two)
{
static_assert(std::is_pointer_v<Ty>, "argument must be a pointer type");
const A* a = get<A>(one);
const B* b = get<B>(two);
if (a && b)
return {a, b};
else
return {nullptr, nullptr};
}
template<typename T, typename Ty>
const T* get(std::optional<Ty> ty)
{
if (ty)
return get<T>(*ty);
else
return nullptr;
}
template<typename T, typename Ty>
T* getMutable(std::optional<Ty> ty)
{
if (ty)
return getMutable<T>(*ty);
else
return nullptr;
}
template<typename Ty>
std::optional<Ty> follow(std::optional<Ty> ty)
{
if (ty)
return follow(*ty);
else
return std::nullopt;
}
/**
* Returns whether or not expr is a literal expression, for example:
* - Scalar literals (numbers, booleans, strings, nil)
* - Table literals
* - Lambdas (a "function literal")
*/
bool isLiteral(const AstExpr* expr);
/**
* Given a table literal and a mapping from expression to type, determine
* whether any literal expression in this table depends on any blocked types.
* This is used as a precondition for bidirectional inference: be warned that
* the behavior of this algorithm is tightly coupled to that of bidirectional
* inference.
* @param expr Expression to search
* @param astTypes Mapping from AST node to TypeID
* @returns A vector of blocked types
*/
std::vector<TypeId> findBlockedTypesIn(AstExprTable* expr, NotNull<DenseHashMap<const AstExpr*, TypeId>> astTypes);
/**
* Given a function call and a mapping from expression to type, determine
* whether the type of any argument in said call in depends on a blocked types.
* This is used as a precondition for bidirectional inference: be warned that
* the behavior of this algorithm is tightly coupled to that of bidirectional
* inference.
* @param expr Expression to search
* @param astTypes Mapping from AST node to TypeID
* @returns A vector of blocked types
*/
std::vector<TypeId> findBlockedArgTypesIn(AstExprCall* expr, NotNull<DenseHashMap<const AstExpr*, TypeId>> astTypes);
/**
* Given a scope and a free type, find the closest parent that has a present
* `interiorFreeTypes` and append the given type to said list. This list will
* be generalized when the requiste `GeneralizationConstraint` is resolved.
* @param scope Initial scope this free type was attached to
* @param ty Free type to track.
*/
void trackInteriorFreeType(Scope* scope, TypeId ty);
void trackInteriorFreeTypePack(Scope* scope, TypePackId tp);
} // namespace Luau

View file

@ -1,618 +0,0 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Predicate.h"
#include "Luau/Unifiable.h"
#include "Luau/Variant.h"
#include "Luau/Common.h"
#include <set>
#include <string>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <vector>
#include <deque>
#include <memory>
#include <optional>
LUAU_FASTINT(LuauTableTypeMaximumStringifierLength)
LUAU_FASTINT(LuauTypeMaximumStringifierLength)
namespace Luau
{
struct TypeArena;
/**
* There are three kinds of type variables:
* - `Free` variables are metavariables, which stand for unconstrained types.
* - `Bound` variables are metavariables that have an equality constraint.
* - `Generic` variables are type variables that are bound by generic functions.
*
* For example, consider the program:
* ```
* function(x, y) x.f = y end
* ```
* To typecheck this, we first introduce free metavariables for the types of `x` and `y`:
* ```
* function(x: X, y: Y) x.f = y end
* ```
* Type inference for the function body then produces the constraint:
* ```
* X = { f: Y }
* ```
* so `X` is now a bound metavariable. We can then quantify the metavariables,
* which replaces any bound metavariables by their binding, and free metavariables
* by bound generic variables:
* ```
* function<a>(x: { f: a }, y: a) x.f = y end
* ```
*/
// So... why `const T*` here rather than `T*`?
// It's because we've had problems caused by the type graph being mutated
// in ways it shouldn't be, for example mutating types from other modules.
// To try to control this, we make the use of types immutable by default,
// then provide explicit mutable access via getMutable and asMutable.
// This means we can grep for all the places we're mutating the type graph,
// and it makes it possible to provide other APIs (e.g. the txn log)
// which control mutable access to the type graph.
struct TypePackVar;
using TypePackId = const TypePackVar*;
// TODO: rename to Type? CLI-39100
struct TypeVar;
// Should never be null
using TypeId = const TypeVar*;
using Name = std::string;
// A free type var is one whose exact shape has yet to be fully determined.
using FreeTypeVar = Unifiable::Free;
// When a free type var is unified with any other, it is then "bound"
// to that type var, indicating that the two types are actually the same type.
using BoundTypeVar = Unifiable::Bound<TypeId>;
using GenericTypeVar = Unifiable::Generic;
using Tags = std::vector<std::string>;
using ModuleName = std::string;
struct PrimitiveTypeVar
{
enum Type
{
NilType, // ObjC #defines Nil :(
Boolean,
Number,
String,
Thread,
};
Type type;
std::optional<TypeId> metatable; // string has a metatable
explicit PrimitiveTypeVar(Type type)
: type(type)
{
}
explicit PrimitiveTypeVar(Type type, TypeId metatable)
: type(type)
, metatable(metatable)
{
}
};
// Singleton types https://github.com/Roblox/luau/blob/master/rfcs/syntax-singleton-types.md
// Types for true and false
struct BoolSingleton
{
bool value;
bool operator==(const BoolSingleton& rhs) const
{
return value == rhs.value;
}
bool operator!=(const BoolSingleton& rhs) const
{
return !(*this == rhs);
}
};
// Types for "foo", "bar" etc.
struct StringSingleton
{
std::string value;
bool operator==(const StringSingleton& rhs) const
{
return value == rhs.value;
}
bool operator!=(const StringSingleton& rhs) const
{
return !(*this == rhs);
}
};
// No type for float singletons, partly because === isn't any equalivalence on floats
// (NaN != NaN).
using SingletonVariant = Luau::Variant<BoolSingleton, StringSingleton>;
struct SingletonTypeVar
{
explicit SingletonTypeVar(const SingletonVariant& variant)
: variant(variant)
{
}
explicit SingletonTypeVar(SingletonVariant&& variant)
: variant(std::move(variant))
{
}
// Default operator== is C++20.
bool operator==(const SingletonTypeVar& rhs) const
{
return variant == rhs.variant;
}
bool operator!=(const SingletonTypeVar& rhs) const
{
return !(*this == rhs);
}
SingletonVariant variant;
};
template<typename T>
const T* get(const SingletonTypeVar* stv)
{
if (stv)
return get_if<T>(&stv->variant);
else
return nullptr;
}
struct FunctionArgument
{
Name name;
Location location;
};
struct FunctionDefinition
{
std::optional<ModuleName> definitionModuleName;
Location definitionLocation;
std::optional<Location> varargLocation;
Location originalNameLocation;
};
// TODO: Come up with a better name.
// TODO: Do we actually need this? We'll find out later if we can delete this.
// Does not exactly belong in TypeVar.h, but this is the only way to appease the compiler.
template<typename T>
struct ExprResult
{
T type;
PredicateVec predicates;
};
using MagicFunction = std::function<std::optional<ExprResult<TypePackId>>(
struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, ExprResult<TypePackId>)>;
struct FunctionTypeVar
{
// Global monomorphic function
FunctionTypeVar(TypePackId argTypes, TypePackId retType, std::optional<FunctionDefinition> defn = {}, bool hasSelf = false);
// Global polymorphic function
FunctionTypeVar(std::vector<TypeId> generics, std::vector<TypePackId> genericPacks, TypePackId argTypes, TypePackId retType,
std::optional<FunctionDefinition> defn = {}, bool hasSelf = false);
// Local monomorphic function
FunctionTypeVar(TypeLevel level, TypePackId argTypes, TypePackId retType, std::optional<FunctionDefinition> defn = {}, bool hasSelf = false);
// Local polymorphic function
FunctionTypeVar(TypeLevel level, std::vector<TypeId> generics, std::vector<TypePackId> genericPacks, TypePackId argTypes, TypePackId retType,
std::optional<FunctionDefinition> defn = {}, bool hasSelf = false);
TypeLevel level;
/// These should all be generic
std::vector<TypeId> generics;
std::vector<TypePackId> genericPacks;
TypePackId argTypes;
std::vector<std::optional<FunctionArgument>> argNames;
TypePackId retType;
std::optional<FunctionDefinition> definition;
MagicFunction magicFunction = nullptr; // Function pointer, can be nullptr.
bool hasSelf;
Tags tags;
};
enum class TableState
{
// Sealed tables have an exact, known shape
Sealed,
// An unsealed table can have extra properties added to it
Unsealed,
// Tables which are not yet fully understood. We are still in the process of learning its shape.
Free,
// A table which is a generic parameter to a function. We know that certain properties are required,
// but we don't care about the full shape.
Generic,
};
struct TableIndexer
{
TableIndexer(TypeId indexType, TypeId indexResultType)
: indexType(indexType)
, indexResultType(indexResultType)
{
}
TypeId indexType;
TypeId indexResultType;
};
struct Property
{
TypeId type;
bool deprecated = false;
std::string deprecatedSuggestion;
std::optional<Location> location = std::nullopt;
Tags tags;
std::optional<std::string> documentationSymbol;
};
struct TableTypeVar
{
// We choose std::map over unordered_map here just because we have unit tests that compare
// textual outputs. I don't want to spend the effort making them resilient in the case where
// random events cause the iteration order of the map elements to change.
// If this shows up in a profile, we can revisit it.
using Props = std::map<Name, Property>;
TableTypeVar() = default;
explicit TableTypeVar(TableState state, TypeLevel level);
TableTypeVar(const Props& props, const std::optional<TableIndexer>& indexer, TypeLevel level, TableState state = TableState::Unsealed);
Props props;
std::optional<TableIndexer> indexer;
TableState state = TableState::Unsealed;
TypeLevel level;
std::optional<std::string> name;
// Sometimes we throw a type on a name to make for nicer error messages, but without creating any entry in the type namespace
// We need to know which is which when we stringify types.
std::optional<std::string> syntheticName;
std::map<Name, Location> methodDefinitionLocations;
std::vector<TypeId> instantiatedTypeParams;
std::vector<TypePackId> instantiatedTypePackParams;
ModuleName definitionModuleName;
std::optional<TypeId> boundTo;
Tags tags;
};
// Represents a metatable attached to a table typevar. Somewhat analogous to a bound typevar.
struct MetatableTypeVar
{
// Always points to a TableTypeVar.
TypeId table;
// Always points to either a TableTypeVar or a MetatableTypeVar.
TypeId metatable;
std::optional<std::string> syntheticName;
};
// Custom userdata of a class type
struct ClassUserData
{
virtual ~ClassUserData() {}
};
/** The type of a class.
*
* Classes behave like tables in many ways, but there are some important differences:
*
* The properties of a class are always exactly known.
* Classes optionally have a parent class.
* Two different classes that share the same properties are nevertheless distinct and mutually incompatible.
*/
struct ClassTypeVar
{
using Props = TableTypeVar::Props;
Name name;
Props props;
std::optional<TypeId> parent;
std::optional<TypeId> metatable; // metaclass?
Tags tags;
std::shared_ptr<ClassUserData> userData;
ClassTypeVar(
Name name, Props props, std::optional<TypeId> parent, std::optional<TypeId> metatable, Tags tags, std::shared_ptr<ClassUserData> userData)
: name(name)
, props(props)
, parent(parent)
, metatable(metatable)
, tags(tags)
, userData(userData)
{
}
};
struct TypeFun
{
// These should all be generic
std::vector<TypeId> typeParams;
std::vector<TypePackId> typePackParams;
/** The underlying type.
*
* WARNING! This is not safe to use as a type if typeParams is not empty!!
* You must first use TypeChecker::instantiateTypeFun to turn it into a real type.
*/
TypeId type;
TypeFun() = default;
TypeFun(std::vector<TypeId> typeParams, TypeId type)
: typeParams(std::move(typeParams))
, type(type)
{
}
TypeFun(std::vector<TypeId> typeParams, std::vector<TypePackId> typePackParams, TypeId type)
: typeParams(std::move(typeParams))
, typePackParams(std::move(typePackParams))
, type(type)
{
}
};
// Anything! All static checking is off.
struct AnyTypeVar
{
};
struct UnionTypeVar
{
std::vector<TypeId> options;
};
struct IntersectionTypeVar
{
std::vector<TypeId> parts;
};
struct LazyTypeVar
{
std::function<TypeId()> thunk;
};
using ErrorTypeVar = Unifiable::Error;
using TypeVariant = Unifiable::Variant<TypeId, PrimitiveTypeVar, SingletonTypeVar, FunctionTypeVar, TableTypeVar, MetatableTypeVar, ClassTypeVar,
AnyTypeVar, UnionTypeVar, IntersectionTypeVar, LazyTypeVar>;
struct TypeVar final
{
explicit TypeVar(const TypeVariant& ty)
: ty(ty)
{
}
explicit TypeVar(TypeVariant&& ty)
: ty(std::move(ty))
{
}
TypeVar(const TypeVariant& ty, bool persistent)
: ty(ty)
, persistent(persistent)
{
}
TypeVariant ty;
// Kludge: A persistent TypeVar is one that belongs to the global scope.
// Global type bindings are immutable but are reused many times.
// Persistent TypeVars do not get cloned.
bool persistent = false;
std::optional<std::string> documentationSymbol;
// Pointer to the type arena that allocated this type.
// Do not depend on the value of this under any circumstances. This is for
// debugging purposes only. This is only set in debug builds; it is nullptr
// in all other environments.
TypeArena* owningArena = nullptr;
bool operator==(const TypeVar& rhs) const;
bool operator!=(const TypeVar& rhs) const;
TypeVar& operator=(const TypeVariant& rhs);
TypeVar& operator=(TypeVariant&& rhs);
};
using SeenSet = std::set<std::pair<void*, void*>>;
bool areEqual(SeenSet& seen, const TypeVar& lhs, const TypeVar& rhs);
// Follow BoundTypeVars until we get to something real
TypeId follow(TypeId t);
std::vector<TypeId> flattenIntersection(TypeId ty);
bool isPrim(TypeId ty, PrimitiveTypeVar::Type primType);
bool isNil(TypeId ty);
bool isBoolean(TypeId ty);
bool isNumber(TypeId ty);
bool isString(TypeId ty);
bool isThread(TypeId ty);
bool isOptional(TypeId ty);
bool isTableIntersection(TypeId ty);
bool isOverloadedFunction(TypeId ty);
std::optional<TypeId> getMetatable(TypeId type);
TableTypeVar* getMutableTableType(TypeId type);
const TableTypeVar* getTableType(TypeId type);
// If the type has a name, return that. Else if it has a synthetic name, return that.
// Returns nullptr if the type has no name.
const std::string* getName(TypeId type);
// Checks whether a union contains all types of another union.
bool isSubset(const UnionTypeVar& super, const UnionTypeVar& sub);
// Checks if a type contains generic type binders
bool isGeneric(const TypeId ty);
// Checks if a type may be instantiated to one containing generic type binders
bool maybeGeneric(const TypeId ty);
// Checks if a type is of the form T1|...|Tn where one of the Ti is a singleton
bool maybeSingleton(TypeId ty);
struct SingletonTypes
{
const TypeId nilType;
const TypeId numberType;
const TypeId stringType;
const TypeId booleanType;
const TypeId threadType;
const TypeId anyType;
const TypeId optionalNumberType;
const TypePackId anyTypePack;
SingletonTypes();
SingletonTypes(const SingletonTypes&) = delete;
void operator=(const SingletonTypes&) = delete;
TypeId errorRecoveryType(TypeId guess);
TypePackId errorRecoveryTypePack(TypePackId guess);
TypeId errorRecoveryType();
TypePackId errorRecoveryTypePack();
private:
std::unique_ptr<struct TypeArena> arena;
TypeId makeStringMetatable();
};
extern SingletonTypes singletonTypes;
void persist(TypeId ty);
void persist(TypePackId tp);
struct ToDotOptions
{
bool showPointers = true; // Show pointer value in the node label
bool duplicatePrimitives = true; // Display primitive types and 'any' as separate nodes
};
std::string toDot(TypeId ty, const ToDotOptions& opts);
std::string toDot(TypePackId tp, const ToDotOptions& opts);
std::string toDot(TypeId ty);
std::string toDot(TypePackId tp);
void dumpDot(TypeId ty);
void dumpDot(TypePackId tp);
const TypeLevel* getLevel(TypeId ty);
TypeLevel* getMutableLevel(TypeId ty);
const Property* lookupClassProp(const ClassTypeVar* cls, const Name& name);
bool isSubclass(const ClassTypeVar* cls, const ClassTypeVar* parent);
bool hasGeneric(TypeId ty);
bool hasGeneric(TypePackId tp);
TypeVar* asMutable(TypeId ty);
template<typename T>
const T* get(TypeId tv)
{
LUAU_ASSERT(tv);
if constexpr (!std::is_same_v<T, BoundTypeVar>)
LUAU_ASSERT(get_if<BoundTypeVar>(&tv->ty) == nullptr);
return get_if<T>(&tv->ty);
}
template<typename T>
T* getMutable(TypeId tv)
{
LUAU_ASSERT(tv);
if constexpr (!std::is_same_v<T, BoundTypeVar>)
LUAU_ASSERT(get_if<BoundTypeVar>(&tv->ty) == nullptr);
return get_if<T>(&asMutable(tv)->ty);
}
/* Traverses the UnionTypeVar yielding each TypeId.
* If the iterator encounters a nested UnionTypeVar, it will instead yield each TypeId within.
*
* Beware: the iterator does not currently filter for unique TypeIds. This may change in the future.
*/
struct UnionTypeVarIterator
{
using value_type = Luau::TypeId;
using pointer = value_type*;
using reference = value_type&;
using difference_type = size_t;
using iterator_category = std::input_iterator_tag;
explicit UnionTypeVarIterator(const UnionTypeVar* utv);
UnionTypeVarIterator& operator++();
UnionTypeVarIterator operator++(int);
bool operator!=(const UnionTypeVarIterator& rhs);
bool operator==(const UnionTypeVarIterator& rhs);
const TypeId& operator*();
friend UnionTypeVarIterator end(const UnionTypeVar* utv);
private:
UnionTypeVarIterator() = default;
// (UnionTypeVar* utv, size_t currentIndex)
using SavedIterInfo = std::pair<const UnionTypeVar*, size_t>;
std::deque<SavedIterInfo> stack;
std::unordered_set<const UnionTypeVar*> seen; // Only needed to protect the iterator from hanging the thread.
void advance();
void descend();
};
UnionTypeVarIterator begin(const UnionTypeVar* utv);
UnionTypeVarIterator end(const UnionTypeVar* utv);
using TypeIdPredicate = std::function<std::optional<TypeId>(TypeId)>;
std::vector<TypeId> filterMap(TypeId type, TypeIdPredicate predicate);
void attachTag(TypeId ty, const std::string& tagName);
void attachTag(Property& prop, const std::string& tagName);
bool hasTag(TypeId ty, const std::string& tagName);
bool hasTag(const Property& prop, const std::string& tagName);
bool hasTag(const Tags& tags, const std::string& tagName); // Do not use in new work.
} // namespace Luau

View file

@ -10,7 +10,7 @@ namespace Luau
{
void* pagedAllocate(size_t size);
void pagedDeallocate(void* ptr);
void pagedDeallocate(void* ptr, size_t size);
void pagedFreeze(void* ptr, size_t size);
void pagedUnfreeze(void* ptr, size_t size);
@ -20,9 +20,15 @@ class TypedAllocator
public:
TypedAllocator()
{
appendBlock();
currentBlockSize = kBlockSize;
}
TypedAllocator(const TypedAllocator&) = delete;
TypedAllocator& operator=(const TypedAllocator&) = delete;
TypedAllocator(TypedAllocator&&) = default;
TypedAllocator& operator=(TypedAllocator&&) = default;
~TypedAllocator()
{
if (frozen)
@ -59,12 +65,12 @@ public:
bool empty() const
{
return stuff.size() == 1 && currentBlockSize == 0;
return stuff.empty();
}
size_t size() const
{
return kBlockSize * (stuff.size() - 1) + currentBlockSize;
return stuff.empty() ? 0 : kBlockSize * (stuff.size() - 1) + currentBlockSize;
}
void clear()
@ -72,7 +78,8 @@ public:
if (frozen)
unfreeze();
free();
appendBlock();
currentBlockSize = kBlockSize;
}
void freeze()
@ -106,7 +113,7 @@ private:
for (size_t i = 0; i < blockSize; ++i)
block[i].~T();
pagedDeallocate(block);
pagedDeallocate(block, kBlockSizeBytes);
}
stuff.clear();

View file

@ -3,13 +3,16 @@
#include "Luau/Variant.h"
#include <optional>
#include <string>
namespace Luau
{
struct Scope;
/**
* The 'level' of a TypeVar is an indirect way to talk about the scope that it 'belongs' too.
* The 'level' of a Type is an indirect way to talk about the scope that it 'belongs' too.
* To start, read http://okmij.org/ftp/ML/generalization.html
*
* We extend the idea by adding a "sub-level" which helps us to differentiate sibling scopes
@ -24,7 +27,7 @@ struct TypeLevel
int level = 0;
int subLevel = 0;
// Returns true if the typelevel "this" is "bigger" than rhs
// Returns true if the level of "this" belongs to an equal or larger scope than that of rhs
bool subsumes(const TypeLevel& rhs) const
{
if (level < rhs.level)
@ -38,6 +41,15 @@ struct TypeLevel
return false;
}
// Returns true if the level of "this" belongs to a larger (not equal) scope than that of rhs
bool subsumesStrict(const TypeLevel& rhs) const
{
if (level == rhs.level && subLevel == rhs.subLevel)
return false;
else
return subsumes(rhs);
}
TypeLevel incr() const
{
TypeLevel result;
@ -47,6 +59,14 @@ struct TypeLevel
}
};
inline TypeLevel max(const TypeLevel& a, const TypeLevel& b)
{
if (a.subsumes(b))
return b;
else
return a;
}
inline TypeLevel min(const TypeLevel& a, const TypeLevel& b)
{
if (a.subsumes(b))
@ -55,25 +75,14 @@ inline TypeLevel min(const TypeLevel& a, const TypeLevel& b)
return b;
}
namespace Unifiable
} // namespace Luau
namespace Luau::Unifiable
{
using Name = std::string;
struct Free
{
explicit Free(TypeLevel level);
int index;
TypeLevel level;
// True if this free type variable is part of a mutually
// recursive type alias whose definitions haven't been
// resolved yet.
bool forwardedTypeAlias = false;
private:
static int nextIndex;
};
int freshIndex();
template<typename Id>
struct Bound
@ -86,37 +95,29 @@ struct Bound
Id boundTo;
};
struct Generic
{
// By default, generics are global, with a synthetic name
Generic();
explicit Generic(TypeLevel level);
explicit Generic(const Name& name);
Generic(TypeLevel level, const Name& name);
int index;
TypeLevel level;
Name name;
bool explicitName;
private:
static int nextIndex;
};
template<typename Id>
struct Error
{
// This constructor has to be public, since it's used in TypeVar and TypePack,
// This constructor has to be public, since it's used in Type and TypePack,
// but shouldn't be called directly. Please use errorRecoveryType() instead.
Error();
explicit Error();
explicit Error(Id synthetic)
: synthetic{synthetic}
{
}
int index;
// This is used to create an error that can be rendered out using this field
// as appropriate metadata for communicating it to the user.
std::optional<Id> synthetic;
private:
static int nextIndex;
};
template<typename Id, typename... Value>
using Variant = Variant<Free, Bound<Id>, Generic, Error, Value...>;
using Variant = Luau::Variant<Bound<Id>, Error<Id>, Value...>;
} // namespace Unifiable
} // namespace Luau
} // namespace Luau::Unifiable

View file

@ -3,10 +3,13 @@
#include "Luau/Error.h"
#include "Luau/Location.h"
#include "Luau/ParseOptions.h"
#include "Luau/Scope.h"
#include "Luau/Substitution.h"
#include "Luau/TxnLog.h"
#include "Luau/TypeInfer.h"
#include "Luau/Module.h" // FIXME: For TypeArena. It merits breaking out into its own header.
#include "Luau/TypeArena.h"
#include "Luau/UnifierSharedState.h"
#include "Luau/Normalize.h"
#include <unordered_set>
@ -19,87 +22,165 @@ enum Variance
Invariant
};
struct UnifierCounters
// A substitution which replaces singleton types by their wider types
struct Widen : Substitution
{
int recursionCount = 0;
int iterationCount = 0;
Widen(TypeArena* arena, NotNull<BuiltinTypes> builtinTypes)
: Substitution(TxnLog::empty(), arena)
, builtinTypes(builtinTypes)
{
}
NotNull<BuiltinTypes> builtinTypes;
bool isDirty(TypeId ty) override;
bool isDirty(TypePackId ty) override;
TypeId clean(TypeId ty) override;
TypePackId clean(TypePackId ty) override;
bool ignoreChildren(TypeId ty) override;
TypeId operator()(TypeId ty);
TypePackId operator()(TypePackId ty);
};
/**
* Normally, when we unify table properties, we must do so invariantly, but we
* can introduce a special exception: If the table property in the subtype
* position arises from a literal expression, it is safe to instead perform a
* covariant check.
*
* This is very useful for typechecking cases where table literals (and trees of
* table literals) are passed directly to functions.
*
* In this case, we know that the property has no other name referring to it and
* so it is perfectly safe for the function to mutate the table any way it
* wishes.
*/
using LiteralProperties = DenseHashSet<Name>;
// TODO: Use this more widely.
struct UnifierOptions
{
bool isFunctionCall = false;
};
struct Unifier
{
TypeArena* const types;
Mode mode;
ScopePtr globalScope; // sigh. Needed solely to get at string's metatable.
NotNull<BuiltinTypes> builtinTypes;
NotNull<Normalizer> normalizer;
NotNull<Scope> scope; // const Scope maybe
TxnLog log;
bool failure = false;
ErrorVec errors;
Location location;
Variance variance = Covariant;
bool normalize = true; // Normalize unions and intersections if necessary
bool checkInhabited = true; // Normalize types to check if they are inhabited
CountMismatch::Context ctx = CountMismatch::Arg;
UnifierCounters* counters;
UnifierCounters countersData;
std::shared_ptr<UnifierCounters> counters_DEPRECATED;
// If true, generics act as free types when unifying.
bool hideousFixMeGenericsAreActuallyFree = false;
UnifierSharedState& sharedState;
Unifier(TypeArena* types, Mode mode, ScopePtr globalScope, const Location& location, Variance variance, UnifierSharedState& sharedState);
Unifier(TypeArena* types, Mode mode, ScopePtr globalScope, const std::vector<std::pair<TypeId, TypeId>>& ownedSeen, const Location& location,
Variance variance, UnifierSharedState& sharedState, const std::shared_ptr<UnifierCounters>& counters_DEPRECATED = nullptr,
UnifierCounters* counters = nullptr);
Unifier(TypeArena* types, Mode mode, ScopePtr globalScope, std::vector<std::pair<TypeId, TypeId>>* sharedSeen, const Location& location,
Variance variance, UnifierSharedState& sharedState, const std::shared_ptr<UnifierCounters>& counters_DEPRECATED = nullptr,
UnifierCounters* counters = nullptr);
// When the Unifier is forced to unify two blocked types (or packs), they
// get added to these vectors. The ConstraintSolver can use this to know
// when it is safe to reattempt dispatching a constraint.
std::vector<TypeId> blockedTypes;
std::vector<TypePackId> blockedTypePacks;
Unifier(NotNull<Normalizer> normalizer, NotNull<Scope> scope, const Location& location, Variance variance, TxnLog* parentLog = nullptr);
// Test whether the two type vars unify. Never commits the result.
ErrorVec canUnify(TypeId superTy, TypeId subTy);
ErrorVec canUnify(TypePackId superTy, TypePackId subTy, bool isFunctionCall = false);
ErrorVec canUnify(TypeId subTy, TypeId superTy);
ErrorVec canUnify(TypePackId subTy, TypePackId superTy, bool isFunctionCall = false);
/** Attempt to unify left with right.
/** Attempt to unify.
* Populate the vector errors with any type errors that may arise.
* Populate the transaction log with the set of TypeIds that need to be reset to undo the unification attempt.
*/
void tryUnify(TypeId superTy, TypeId subTy, bool isFunctionCall = false, bool isIntersection = false);
void tryUnify(
TypeId subTy,
TypeId superTy,
bool isFunctionCall = false,
bool isIntersection = false,
const LiteralProperties* aliasableMap = nullptr
);
private:
void tryUnify_(TypeId superTy, TypeId subTy, bool isFunctionCall = false, bool isIntersection = false);
void tryUnifyPrimitives(TypeId superTy, TypeId subTy);
void tryUnifySingletons(TypeId superTy, TypeId subTy);
void tryUnifyFunctions(TypeId superTy, TypeId subTy, bool isFunctionCall = false);
void tryUnifyTables(TypeId left, TypeId right, bool isIntersection = false);
void DEPRECATED_tryUnifyTables(TypeId left, TypeId right, bool isIntersection = false);
void tryUnifyFreeTable(TypeId free, TypeId other);
void tryUnifySealedTables(TypeId left, TypeId right, bool isIntersection);
void tryUnifyWithMetatable(TypeId metatable, TypeId other, bool reversed);
void tryUnifyWithClass(TypeId superTy, TypeId subTy, bool reversed);
void tryUnify(const TableIndexer& superIndexer, const TableIndexer& subIndexer);
void tryUnify_(
TypeId subTy,
TypeId superTy,
bool isFunctionCall = false,
bool isIntersection = false,
const LiteralProperties* aliasableMap = nullptr
);
void tryUnifyUnionWithType(TypeId subTy, const UnionType* uv, TypeId superTy);
// Traverse the two types provided and block on any BlockedTypes we find.
// Returns true if any types were blocked on.
bool DEPRECATED_blockOnBlockedTypes(TypeId subTy, TypeId superTy);
void tryUnifyTypeWithUnion(TypeId subTy, TypeId superTy, const UnionType* uv, bool cacheEnabled, bool isFunctionCall);
void tryUnifyTypeWithIntersection(TypeId subTy, TypeId superTy, const IntersectionType* uv);
void tryUnifyIntersectionWithType(TypeId subTy, const IntersectionType* uv, TypeId superTy, bool cacheEnabled, bool isFunctionCall);
void tryUnifyNormalizedTypes(
TypeId subTy,
TypeId superTy,
const NormalizedType& subNorm,
const NormalizedType& superNorm,
std::string reason,
std::optional<TypeError> error = std::nullopt
);
void tryUnifyPrimitives(TypeId subTy, TypeId superTy);
void tryUnifySingletons(TypeId subTy, TypeId superTy);
void tryUnifyFunctions(TypeId subTy, TypeId superTy, bool isFunctionCall = false);
void tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection = false, const LiteralProperties* aliasableMap = nullptr);
void tryUnifyScalarShape(TypeId subTy, TypeId superTy, bool reversed);
void tryUnifyWithMetatable(TypeId subTy, TypeId superTy, bool reversed);
void tryUnifyWithExternType(TypeId subTy, TypeId superTy, bool reversed);
void tryUnifyNegations(TypeId subTy, TypeId superTy);
TypePackId tryApplyOverloadedFunction(TypeId function, const NormalizedFunctionType& overloads, TypePackId args);
TypeId widen(TypeId ty);
TypePackId widen(TypePackId tp);
TypeId deeplyOptional(TypeId ty, std::unordered_map<TypeId, TypeId> seen = {});
void cacheResult(TypeId superTy, TypeId subTy);
bool canCacheResult(TypeId subTy, TypeId superTy);
void cacheResult(TypeId subTy, TypeId superTy, size_t prevErrorCount);
public:
void tryUnify(TypePackId superTy, TypePackId subTy, bool isFunctionCall = false);
void tryUnify(TypePackId subTy, TypePackId superTy, bool isFunctionCall = false);
private:
void tryUnify_(TypePackId superTy, TypePackId subTy, bool isFunctionCall = false);
void tryUnifyVariadics(TypePackId superTy, TypePackId subTy, bool reversed, int subOffset = 0);
void tryUnify_(TypePackId subTy, TypePackId superTy, bool isFunctionCall = false);
void tryUnifyVariadics(TypePackId subTy, TypePackId superTy, bool reversed, int subOffset = 0);
void tryUnifyWithAny(TypeId any, TypeId ty);
void tryUnifyWithAny(TypePackId any, TypePackId ty);
void tryUnifyWithAny(TypeId subTy, TypeId anyTy);
void tryUnifyWithAny(TypePackId subTy, TypePackId anyTp);
std::optional<TypeId> findTablePropertyRespectingMeta(TypeId lhsType, Name name);
public:
// Report an "infinite type error" if the type "needle" already occurs within "haystack"
void occursCheck(TypeId needle, TypeId haystack);
void occursCheck(std::unordered_set<TypeId>& seen_DEPRECATED, DenseHashSet<TypeId>& seen, TypeId needle, TypeId haystack);
void occursCheck(TypePackId needle, TypePackId haystack);
void occursCheck(std::unordered_set<TypePackId>& seen_DEPRECATED, DenseHashSet<TypePackId>& seen, TypePackId needle, TypePackId haystack);
TxnLog combineLogsIntoUnion(std::vector<TxnLog> logs);
Unifier makeChildUnifier();
public:
// Returns true if the type "needle" already occurs within "haystack" and reports an "infinite type error"
bool occursCheck(TypeId needle, TypeId haystack, bool reversed);
bool occursCheck(DenseHashSet<TypeId>& seen, TypeId needle, TypeId haystack);
bool occursCheck(TypePackId needle, TypePackId haystack, bool reversed);
bool occursCheck(DenseHashSet<TypePackId>& seen, TypePackId needle, TypePackId haystack);
std::unique_ptr<Unifier> makeChildUnifier();
void reportError(TypeError err);
LUAU_NOINLINE void reportError(Location location, TypeErrorData data);
private:
bool isNonstrictMode() const;
TypeMismatch::Context mismatchContext();
void checkChildUnifierTypeMismatch(const ErrorVec& innerErrors, TypeId wantedType, TypeId givenType);
void checkChildUnifierTypeMismatch(const ErrorVec& innerErrors, const std::string& prop, TypeId wantedType, TypeId givenType);
@ -107,9 +188,12 @@ private:
[[noreturn]] void ice(const std::string& message, const Location& location);
[[noreturn]] void ice(const std::string& message);
// Remove with FFlagLuauCacheUnifyTableResults
DenseHashSet<TypeId> tempSeenTy_DEPRECATED{nullptr};
DenseHashSet<TypePackId> tempSeenTp_DEPRECATED{nullptr};
// Available after regular type pack unification errors
std::optional<int> firstPackErrorPos;
};
void promoteTypeLevels(TxnLog& log, const TypeArena* arena, TypeLevel minLevel, Scope* outerScope, bool useScope, TypePackId tp);
std::optional<TypeError> hasUnificationTooComplex(const ErrorVec& errors);
std::optional<TypeError> hasCountMismatch(const ErrorVec& errors);
} // namespace Luau

View file

@ -0,0 +1,127 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Constraint.h"
#include "Luau/DenseHash.h"
#include "Luau/NotNull.h"
#include "Luau/TypeCheckLimits.h"
#include "Luau/TypeFwd.h"
#include "Luau/TypePairHash.h"
#include <optional>
#include <vector>
#include <utility>
namespace Luau
{
struct InternalErrorReporter;
struct Scope;
struct TypeArena;
enum class OccursCheckResult
{
Pass,
Fail
};
struct Unifier2
{
NotNull<TypeArena> arena;
NotNull<BuiltinTypes> builtinTypes;
NotNull<Scope> scope;
NotNull<InternalErrorReporter> ice;
TypeCheckLimits limits;
DenseHashSet<std::pair<TypeId, TypeId>, TypePairHash> seenTypePairings{{nullptr, nullptr}};
DenseHashSet<std::pair<TypePackId, TypePackId>, TypePairHash> seenTypePackPairings{{nullptr, nullptr}};
DenseHashMap<TypeId, std::vector<TypeId>> expandedFreeTypes{nullptr};
// Mapping from generic types to free types to be used in instantiation.
DenseHashMap<TypeId, TypeId> genericSubstitutions{nullptr};
// Mapping from generic type packs to `TypePack`s of free types to be used in instantiation.
DenseHashMap<TypePackId, TypePackId> genericPackSubstitutions{nullptr};
// Unification sometimes results in the creation of new free types.
// We collect them here so that other systems can perform necessary
// bookkeeping.
std::vector<TypeId> newFreshTypes;
std::vector<TypePackId> newFreshTypePacks;
int recursionCount = 0;
int recursionLimit = 0;
std::vector<ConstraintV> incompleteSubtypes;
// null if not in a constraint solving context
DenseHashSet<const void*>* uninhabitedTypeFunctions;
Unifier2(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, NotNull<Scope> scope, NotNull<InternalErrorReporter> ice);
Unifier2(
NotNull<TypeArena> arena,
NotNull<BuiltinTypes> builtinTypes,
NotNull<Scope> scope,
NotNull<InternalErrorReporter> ice,
DenseHashSet<const void*>* uninhabitedTypeFunctions
);
/** Attempt to commit the subtype relation subTy <: superTy to the type
* graph.
*
* @returns true if successful.
*
* Note that incoherent types can and will successfully be unified. We stop
* when we *cannot know* how to relate the provided types, not when doing so
* would narrow something down to never or broaden it to unknown.
*
* Presently, the only way unification can fail is if we attempt to bind one
* free TypePack to another and encounter an occurs check violation.
*/
bool unify(TypeId subTy, TypeId superTy);
bool unifyFreeWithType(TypeId subTy, TypeId superTy);
bool unify(TypeId subTy, const FunctionType* superFn);
bool unify(const UnionType* subUnion, TypeId superTy);
bool unify(TypeId subTy, const UnionType* superUnion);
bool unify(const IntersectionType* subIntersection, TypeId superTy);
bool unify(TypeId subTy, const IntersectionType* superIntersection);
bool unify(TableType* subTable, const TableType* superTable);
bool unify(const MetatableType* subMetatable, const MetatableType* superMetatable);
bool unify(const AnyType* subAny, const FunctionType* superFn);
bool unify(const FunctionType* subFn, const AnyType* superAny);
bool unify(const AnyType* subAny, const TableType* superTable);
bool unify(const TableType* subTable, const AnyType* superAny);
bool unify(const MetatableType* subMetatable, const AnyType*);
bool unify(const AnyType*, const MetatableType* superMetatable);
// TODO think about this one carefully. We don't do unions or intersections of type packs
bool unify(TypePackId subTp, TypePackId superTp);
std::optional<TypeId> generalize(TypeId ty);
private:
/**
* @returns simplify(left | right)
*/
TypeId mkUnion(TypeId left, TypeId right);
/**
* @returns simplify(left & right)
*/
TypeId mkIntersection(TypeId left, TypeId right);
// Returns true if needle occurs within haystack already. ie if we bound
// needle to haystack, would a cyclic type result?
OccursCheckResult occursCheck(DenseHashSet<TypeId>& seen, TypeId needle, TypeId haystack);
// Returns true if needle occurs within haystack already. ie if we bound
// needle to haystack, would a cyclic TypePack result?
OccursCheckResult occursCheck(DenseHashSet<TypePackId>& seen, TypePackId needle, TypePackId haystack);
TypeId freshType(NotNull<Scope> scope, Polarity polarity);
TypePackId freshTypePack(NotNull<Scope> scope, Polarity polarity);
};
} // namespace Luau

View file

@ -2,8 +2,8 @@
#pragma once
#include "Luau/DenseHash.h"
#include "Luau/TypeVar.h"
#include "Luau/TypePack.h"
#include "Luau/Error.h"
#include "Luau/TypeFwd.h"
#include <utility>
@ -24,6 +24,14 @@ struct TypeIdPairHash
}
};
struct UnifierCounters
{
int recursionCount = 0;
int recursionLimit = 0;
int iterationCount = 0;
int iterationLimit = 0;
};
struct UnifierSharedState
{
UnifierSharedState(InternalErrorReporter* iceHandler)
@ -33,12 +41,34 @@ struct UnifierSharedState
InternalErrorReporter* iceHandler;
DenseHashSet<void*> seenAny{nullptr};
DenseHashMap<TypeId, bool> skipCacheForType{nullptr};
DenseHashSet<std::pair<TypeId, TypeId>, TypeIdPairHash> cachedUnify{{nullptr, nullptr}};
DenseHashMap<std::pair<TypeId, TypeId>, TypeErrorData, TypeIdPairHash> cachedUnifyError{{nullptr, nullptr}};
DenseHashSet<TypeId> tempSeenTy{nullptr};
DenseHashSet<TypePackId> tempSeenTp{nullptr};
UnifierCounters counters;
bool reentrantTypeReduction = false;
};
struct TypeReductionRentrancyGuard final
{
explicit TypeReductionRentrancyGuard(NotNull<UnifierSharedState> sharedState)
: sharedState{sharedState}
{
sharedState->reentrantTypeReduction = true;
}
~TypeReductionRentrancyGuard()
{
sharedState->reentrantTypeReduction = false;
}
TypeReductionRentrancyGuard(const TypeReductionRentrancyGuard&) = delete;
TypeReductionRentrancyGuard(TypeReductionRentrancyGuard&&) = delete;
private:
NotNull<UnifierSharedState> sharedState;
};
} // namespace Luau

View file

@ -0,0 +1,531 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include <unordered_set>
#include "Luau/DenseHash.h"
#include "Luau/RecursionCounter.h"
#include "Luau/TypePack.h"
#include "Luau/Type.h"
#include "Type.h"
LUAU_FASTINT(LuauVisitRecursionLimit)
LUAU_FASTFLAG(LuauSolverV2)
namespace Luau
{
namespace visit_detail
{
/**
* Apply f(tid, t, seen) if doing so would pass type checking, else apply f(tid, t)
*
* We do this to permit (but not require) Type visitors to accept the seen set as an argument.
*/
template<typename F, typename A, typename B, typename C>
auto apply(A tid, const B& t, C& c, F& f) -> decltype(f(tid, t, c))
{
return f(tid, t, c);
}
template<typename A, typename B, typename C, typename F>
auto apply(A tid, const B& t, C&, F& f) -> decltype(f(tid, t))
{
return f(tid, t);
}
inline bool hasSeen(std::unordered_set<void*>& seen, const void* tv)
{
void* ttv = const_cast<void*>(tv);
return !seen.insert(ttv).second;
}
inline bool hasSeen(DenseHashSet<void*>& seen, const void* tv)
{
void* ttv = const_cast<void*>(tv);
if (seen.contains(ttv))
return true;
seen.insert(ttv);
return false;
}
inline void unsee(std::unordered_set<void*>& seen, const void* tv)
{
void* ttv = const_cast<void*>(tv);
seen.erase(ttv);
}
inline void unsee(DenseHashSet<void*>& seen, const void* tv)
{
// When DenseHashSet is used for 'visitTypeOnce', where don't forget visited elements
}
} // namespace visit_detail
// recursion counter is equivalent here, but we'd like a better name to express the intent.
using TypeFunctionDepthCounter = RecursionCounter;
template<typename S>
struct GenericTypeVisitor
{
using Set = S;
Set seen;
bool skipBoundTypes = false;
int recursionCounter = 0;
int typeFunctionDepth = 0;
GenericTypeVisitor() = default;
explicit GenericTypeVisitor(Set seen, bool skipBoundTypes = false)
: seen(std::move(seen))
, skipBoundTypes(skipBoundTypes)
{
}
virtual ~GenericTypeVisitor() {}
virtual void cycle(TypeId) {}
virtual void cycle(TypePackId) {}
virtual bool visit(TypeId ty)
{
return true;
}
virtual bool visit(TypeId ty, const BoundType& btv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const FreeType& ftv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const GenericType& gtv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const ErrorType& etv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const PrimitiveType& ptv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const FunctionType& ftv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const TableType& ttv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const MetatableType& mtv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const ExternType& etv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const AnyType& atv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const NoRefineType& nrt)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const UnknownType& utv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const NeverType& ntv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const UnionType& utv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const IntersectionType& itv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const BlockedType& btv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const PendingExpansionType& petv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const SingletonType& stv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const NegationType& ntv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const TypeFunctionInstanceType& tfit)
{
return visit(ty);
}
virtual bool visit(TypePackId tp)
{
return true;
}
virtual bool visit(TypePackId tp, const BoundTypePack& btp)
{
return visit(tp);
}
virtual bool visit(TypePackId tp, const FreeTypePack& ftp)
{
return visit(tp);
}
virtual bool visit(TypePackId tp, const GenericTypePack& gtp)
{
return visit(tp);
}
virtual bool visit(TypePackId tp, const ErrorTypePack& etp)
{
return visit(tp);
}
virtual bool visit(TypePackId tp, const TypePack& pack)
{
return visit(tp);
}
virtual bool visit(TypePackId tp, const VariadicTypePack& vtp)
{
return visit(tp);
}
virtual bool visit(TypePackId tp, const BlockedTypePack& btp)
{
return visit(tp);
}
virtual bool visit(TypePackId tp, const TypeFunctionInstanceTypePack& tfitp)
{
return visit(tp);
}
void traverse(TypeId ty)
{
RecursionLimiter limiter{&recursionCounter, FInt::LuauVisitRecursionLimit};
if (visit_detail::hasSeen(seen, ty))
{
cycle(ty);
return;
}
if (auto btv = get<BoundType>(ty))
{
if (skipBoundTypes)
traverse(btv->boundTo);
else if (visit(ty, *btv))
traverse(btv->boundTo);
}
else if (auto ftv = get<FreeType>(ty))
{
if (FFlag::LuauSolverV2)
{
if (visit(ty, *ftv))
{
// TODO: Replace these if statements with assert()s when we
// delete FFlag::LuauSolverV2.
//
// When the old solver is used, these pointers are always
// unused. When the new solver is used, they are never null.
if (ftv->lowerBound)
traverse(ftv->lowerBound);
if (ftv->upperBound)
traverse(ftv->upperBound);
}
}
else
visit(ty, *ftv);
}
else if (auto gtv = get<GenericType>(ty))
visit(ty, *gtv);
else if (auto etv = get<ErrorType>(ty))
visit(ty, *etv);
else if (auto ptv = get<PrimitiveType>(ty))
visit(ty, *ptv);
else if (auto ftv = get<FunctionType>(ty))
{
if (visit(ty, *ftv))
{
traverse(ftv->argTypes);
traverse(ftv->retTypes);
}
}
else if (auto ttv = get<TableType>(ty))
{
// Some visitors want to see bound tables, that's why we traverse the original type
if (skipBoundTypes && ttv->boundTo)
{
traverse(*ttv->boundTo);
}
else if (visit(ty, *ttv))
{
if (ttv->boundTo)
{
traverse(*ttv->boundTo);
}
else
{
for (auto& [_name, prop] : ttv->props)
{
if (FFlag::LuauSolverV2)
{
if (auto ty = prop.readTy)
traverse(*ty);
// In the case that the readType and the writeType
// are the same pointer, just traverse once.
// Traversing each property twice has pretty
// significant performance consequences.
if (auto ty = prop.writeTy; ty && !prop.isShared())
traverse(*ty);
}
else
traverse(prop.type());
}
if (ttv->indexer)
{
traverse(ttv->indexer->indexType);
traverse(ttv->indexer->indexResultType);
}
}
}
}
else if (auto mtv = get<MetatableType>(ty))
{
if (visit(ty, *mtv))
{
traverse(mtv->table);
traverse(mtv->metatable);
}
}
else if (auto etv = get<ExternType>(ty))
{
if (visit(ty, *etv))
{
for (const auto& [name, prop] : etv->props)
{
if (FFlag::LuauSolverV2)
{
if (auto ty = prop.readTy)
traverse(*ty);
// In the case that the readType and the writeType are
// the same pointer, just traverse once. Traversing each
// property twice would have pretty significant
// performance consequences.
if (auto ty = prop.writeTy; ty && !prop.isShared())
traverse(*ty);
}
else
traverse(prop.type());
}
if (etv->parent)
traverse(*etv->parent);
if (etv->metatable)
traverse(*etv->metatable);
if (etv->indexer)
{
traverse(etv->indexer->indexType);
traverse(etv->indexer->indexResultType);
}
}
}
else if (auto atv = get<AnyType>(ty))
visit(ty, *atv);
else if (auto nrt = get<NoRefineType>(ty))
visit(ty, *nrt);
else if (auto utv = get<UnionType>(ty))
{
if (visit(ty, *utv))
{
bool unionChanged = false;
for (TypeId optTy : utv->options)
{
traverse(optTy);
if (!get<UnionType>(follow(ty)))
{
unionChanged = true;
break;
}
}
if (unionChanged)
traverse(ty);
}
}
else if (auto itv = get<IntersectionType>(ty))
{
if (visit(ty, *itv))
{
bool intersectionChanged = false;
for (TypeId partTy : itv->parts)
{
traverse(partTy);
if (!get<IntersectionType>(follow(ty)))
{
intersectionChanged = true;
break;
}
}
if (intersectionChanged)
traverse(ty);
}
}
else if (auto ltv = get<LazyType>(ty))
{
if (TypeId unwrapped = ltv->unwrapped)
traverse(unwrapped);
// Visiting into LazyType that hasn't been unwrapped may necessarily cause infinite expansion, so we don't do that on purpose.
// Asserting also makes no sense, because the type _will_ happen here, most likely as a property of some ExternType
// that doesn't need to be expanded.
}
else if (auto stv = get<SingletonType>(ty))
visit(ty, *stv);
else if (auto btv = get<BlockedType>(ty))
visit(ty, *btv);
else if (auto utv = get<UnknownType>(ty))
visit(ty, *utv);
else if (auto ntv = get<NeverType>(ty))
visit(ty, *ntv);
else if (auto petv = get<PendingExpansionType>(ty))
{
if (visit(ty, *petv))
{
for (TypeId a : petv->typeArguments)
traverse(a);
for (TypePackId a : petv->packArguments)
traverse(a);
}
}
else if (auto ntv = get<NegationType>(ty))
{
if (visit(ty, *ntv))
traverse(ntv->ty);
}
else if (auto tfit = get<TypeFunctionInstanceType>(ty))
{
TypeFunctionDepthCounter tfdc{&typeFunctionDepth};
if (visit(ty, *tfit))
{
for (TypeId p : tfit->typeArguments)
traverse(p);
for (TypePackId p : tfit->packArguments)
traverse(p);
}
}
else
LUAU_ASSERT(!"GenericTypeVisitor::traverse(TypeId) is not exhaustive!");
visit_detail::unsee(seen, ty);
}
void traverse(TypePackId tp)
{
if (visit_detail::hasSeen(seen, tp))
{
cycle(tp);
return;
}
if (auto btv = get<BoundTypePack>(tp))
{
if (visit(tp, *btv))
traverse(btv->boundTo);
}
else if (auto ftv = get<FreeTypePack>(tp))
visit(tp, *ftv);
else if (auto gtv = get<GenericTypePack>(tp))
visit(tp, *gtv);
else if (auto etv = get<ErrorTypePack>(tp))
visit(tp, *etv);
else if (auto pack = get<TypePack>(tp))
{
bool res = visit(tp, *pack);
if (res)
{
for (TypeId ty : pack->head)
traverse(ty);
if (pack->tail)
traverse(*pack->tail);
}
}
else if (auto pack = get<VariadicTypePack>(tp))
{
bool res = visit(tp, *pack);
if (res)
traverse(pack->ty);
}
else if (auto btp = get<BlockedTypePack>(tp))
visit(tp, *btp);
else if (auto tfitp = get<TypeFunctionInstanceTypePack>(tp))
{
TypeFunctionDepthCounter tfdc{&typeFunctionDepth};
if (visit(tp, *tfitp))
{
for (TypeId t : tfitp->typeArguments)
traverse(t);
for (TypePackId t : tfitp->packArguments)
traverse(t);
}
}
else
LUAU_ASSERT(!"GenericTypeVisitor::traverse(TypePackId) is not exhaustive!");
visit_detail::unsee(seen, tp);
}
};
/** Visit each type under a given type. Skips over cycles and keeps recursion depth under control.
*
* The same type may be visited multiple times if there are multiple distinct paths to it. If this is undesirable, use
* TypeOnceVisitor.
*/
struct TypeVisitor : GenericTypeVisitor<std::unordered_set<void*>>
{
explicit TypeVisitor(bool skipBoundTypes = false)
: GenericTypeVisitor{{}, skipBoundTypes}
{
}
};
/// Visit each type under a given type. Each type will only be checked once even if there are multiple paths to it.
struct TypeOnceVisitor : GenericTypeVisitor<DenseHashSet<void*>>
{
explicit TypeOnceVisitor(bool skipBoundTypes = false)
: GenericTypeVisitor{DenseHashSet<void*>{nullptr}, skipBoundTypes}
{
}
};
} // namespace Luau

View file

@ -1,235 +0,0 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/DenseHash.h"
#include "Luau/TypeVar.h"
#include "Luau/TypePack.h"
LUAU_FASTFLAG(LuauCacheUnifyTableResults)
namespace Luau
{
namespace visit_detail
{
/**
* Apply f(tid, t, seen) if doing so would pass type checking, else apply f(tid, t)
*
* We do this to permit (but not require) TypeVar visitors to accept the seen set as an argument.
*/
template<typename F, typename A, typename B, typename C>
auto apply(A tid, const B& t, C& c, F& f) -> decltype(f(tid, t, c))
{
return f(tid, t, c);
}
template<typename A, typename B, typename C, typename F>
auto apply(A tid, const B& t, C&, F& f) -> decltype(f(tid, t))
{
return f(tid, t);
}
inline bool hasSeen(std::unordered_set<void*>& seen, const void* tv)
{
void* ttv = const_cast<void*>(tv);
return !seen.insert(ttv).second;
}
inline bool hasSeen(DenseHashSet<void*>& seen, const void* tv)
{
void* ttv = const_cast<void*>(tv);
if (seen.contains(ttv))
return true;
seen.insert(ttv);
return false;
}
inline void unsee(std::unordered_set<void*>& seen, const void* tv)
{
void* ttv = const_cast<void*>(tv);
seen.erase(ttv);
}
inline void unsee(DenseHashSet<void*>& seen, const void* tv)
{
// When DenseHashSet is used for 'visitOnce', where don't forget visited elements
}
template<typename F, typename Set>
void visit(TypePackId tp, F& f, Set& seen);
template<typename F, typename Set>
void visit(TypeId ty, F& f, Set& seen)
{
if (visit_detail::hasSeen(seen, ty))
{
f.cycle(ty);
return;
}
if (auto btv = get<BoundTypeVar>(ty))
{
if (apply(ty, *btv, seen, f))
visit(btv->boundTo, f, seen);
}
else if (auto ftv = get<FreeTypeVar>(ty))
apply(ty, *ftv, seen, f);
else if (auto gtv = get<GenericTypeVar>(ty))
apply(ty, *gtv, seen, f);
else if (auto etv = get<ErrorTypeVar>(ty))
apply(ty, *etv, seen, f);
else if (auto ptv = get<PrimitiveTypeVar>(ty))
apply(ty, *ptv, seen, f);
else if (auto ftv = get<FunctionTypeVar>(ty))
{
if (apply(ty, *ftv, seen, f))
{
visit(ftv->argTypes, f, seen);
visit(ftv->retType, f, seen);
}
}
else if (auto ttv = get<TableTypeVar>(ty))
{
// Some visitors want to see bound tables, that's why we visit the original type
if (apply(ty, *ttv, seen, f))
{
if (FFlag::LuauCacheUnifyTableResults && ttv->boundTo)
{
visit(*ttv->boundTo, f, seen);
}
else
{
for (auto& [_name, prop] : ttv->props)
visit(prop.type, f, seen);
if (ttv->indexer)
{
visit(ttv->indexer->indexType, f, seen);
visit(ttv->indexer->indexResultType, f, seen);
}
}
}
}
else if (auto mtv = get<MetatableTypeVar>(ty))
{
if (apply(ty, *mtv, seen, f))
{
visit(mtv->table, f, seen);
visit(mtv->metatable, f, seen);
}
}
else if (auto ctv = get<ClassTypeVar>(ty))
{
if (apply(ty, *ctv, seen, f))
{
for (const auto& [name, prop] : ctv->props)
visit(prop.type, f, seen);
if (ctv->parent)
visit(*ctv->parent, f, seen);
if (ctv->metatable)
visit(*ctv->metatable, f, seen);
}
}
else if (auto atv = get<AnyTypeVar>(ty))
apply(ty, *atv, seen, f);
else if (auto utv = get<UnionTypeVar>(ty))
{
if (apply(ty, *utv, seen, f))
{
for (TypeId optTy : utv->options)
visit(optTy, f, seen);
}
}
else if (auto itv = get<IntersectionTypeVar>(ty))
{
if (apply(ty, *itv, seen, f))
{
for (TypeId partTy : itv->parts)
visit(partTy, f, seen);
}
}
visit_detail::unsee(seen, ty);
}
template<typename F, typename Set>
void visit(TypePackId tp, F& f, Set& seen)
{
if (visit_detail::hasSeen(seen, tp))
{
f.cycle(tp);
return;
}
if (auto btv = get<BoundTypePack>(tp))
{
if (apply(tp, *btv, seen, f))
visit(btv->boundTo, f, seen);
}
else if (auto ftv = get<Unifiable::Free>(tp))
apply(tp, *ftv, seen, f);
else if (auto gtv = get<Unifiable::Generic>(tp))
apply(tp, *gtv, seen, f);
else if (auto etv = get<Unifiable::Error>(tp))
apply(tp, *etv, seen, f);
else if (auto pack = get<TypePack>(tp))
{
apply(tp, *pack, seen, f);
for (TypeId ty : pack->head)
visit(ty, f, seen);
if (pack->tail)
visit(*pack->tail, f, seen);
}
else if (auto pack = get<VariadicTypePack>(tp))
{
apply(tp, *pack, seen, f);
visit(pack->ty, f, seen);
}
visit_detail::unsee(seen, tp);
}
} // namespace visit_detail
template<typename TID, typename F>
void visitTypeVar(TID ty, F& f, std::unordered_set<void*>& seen)
{
visit_detail::visit(ty, f, seen);
}
template<typename TID, typename F>
void visitTypeVar(TID ty, F& f)
{
std::unordered_set<void*> seen;
visit_detail::visit(ty, f, seen);
}
template<typename TID, typename F>
void visitTypeVarOnce(TID ty, F& f, DenseHashSet<void*>& seen)
{
seen.clear();
visit_detail::visit(ty, f, seen);
}
} // namespace Luau

View file

@ -0,0 +1,101 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/Anyification.h"
#include "Luau/Common.h"
#include "Luau/Normalize.h"
#include "Luau/TxnLog.h"
namespace Luau
{
Anyification::Anyification(
TypeArena* arena,
NotNull<Scope> scope,
NotNull<BuiltinTypes> builtinTypes,
InternalErrorReporter* iceHandler,
TypeId anyType,
TypePackId anyTypePack
)
: Substitution(TxnLog::empty(), arena)
, scope(scope)
, builtinTypes(builtinTypes)
, iceHandler(iceHandler)
, anyType(anyType)
, anyTypePack(anyTypePack)
{
}
Anyification::Anyification(
TypeArena* arena,
const ScopePtr& scope,
NotNull<BuiltinTypes> builtinTypes,
InternalErrorReporter* iceHandler,
TypeId anyType,
TypePackId anyTypePack
)
: Anyification(arena, NotNull{scope.get()}, builtinTypes, iceHandler, anyType, anyTypePack)
{
}
bool Anyification::isDirty(TypeId ty)
{
if (ty->persistent)
return false;
if (const TableType* ttv = log->getMutable<TableType>(ty))
return (ttv->state == TableState::Free || ttv->state == TableState::Unsealed);
else if (log->getMutable<FreeType>(ty))
return true;
else
return false;
}
bool Anyification::isDirty(TypePackId tp)
{
if (tp->persistent)
return false;
if (log->getMutable<FreeTypePack>(tp))
return true;
else
return false;
}
TypeId Anyification::clean(TypeId ty)
{
LUAU_ASSERT(isDirty(ty));
if (const TableType* ttv = log->getMutable<TableType>(ty))
{
TableType clone = TableType{ttv->props, ttv->indexer, ttv->level, TableState::Sealed};
clone.definitionModuleName = ttv->definitionModuleName;
clone.definitionLocation = ttv->definitionLocation;
clone.name = ttv->name;
clone.syntheticName = ttv->syntheticName;
clone.tags = ttv->tags;
TypeId res = addType(std::move(clone));
return res;
}
else
return anyType;
}
TypePackId Anyification::clean(TypePackId tp)
{
LUAU_ASSERT(isDirty(tp));
return anyTypePack;
}
bool Anyification::ignoreChildren(TypeId ty)
{
if (get<ExternType>(ty))
return true;
return ty->persistent;
}
bool Anyification::ignoreChildren(TypePackId ty)
{
return ty->persistent;
}
} // namespace Luau

View file

@ -0,0 +1,62 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/ApplyTypeFunction.h"
namespace Luau
{
bool ApplyTypeFunction::isDirty(TypeId ty)
{
if (typeArguments.count(ty))
return true;
else if (const FreeType* ftv = get<FreeType>(ty))
{
if (ftv->forwardedTypeAlias)
encounteredForwardedType = true;
return false;
}
else
return false;
}
bool ApplyTypeFunction::isDirty(TypePackId tp)
{
if (typePackArguments.count(tp))
return true;
else
return false;
}
bool ApplyTypeFunction::ignoreChildren(TypeId ty)
{
if (get<GenericType>(ty))
return true;
else if (get<ExternType>(ty))
return true;
else
return false;
}
bool ApplyTypeFunction::ignoreChildren(TypePackId tp)
{
if (get<GenericTypePack>(tp))
return true;
else
return false;
}
TypeId ApplyTypeFunction::clean(TypeId ty)
{
TypeId& arg = typeArguments[ty];
LUAU_ASSERT(arg);
return arg;
}
TypePackId ApplyTypeFunction::clean(TypePackId tp)
{
TypePackId& arg = typePackArguments[tp];
LUAU_ASSERT(arg);
return arg;
}
} // namespace Luau

File diff suppressed because it is too large Load diff

View file

@ -4,19 +4,122 @@
#include "Luau/Module.h"
#include "Luau/Scope.h"
#include "Luau/TypeInfer.h"
#include "Luau/TypeVar.h"
#include "Luau/Type.h"
#include "Luau/ToString.h"
#include "Luau/Common.h"
#include <algorithm>
LUAU_FASTFLAG(LuauSolverV2)
namespace Luau
{
namespace
{
struct AutocompleteNodeFinder : public AstVisitor
{
const Position pos;
std::vector<AstNode*> ancestry;
explicit AutocompleteNodeFinder(Position pos, AstNode* root)
: pos(pos)
{
}
bool visit(AstExpr* expr) override
{
if (expr->location.begin <= pos && pos <= expr->location.end)
{
ancestry.push_back(expr);
return true;
}
return false;
}
bool visit(AstStat* stat) override
{
// Consider 'local myLocal = 4;|' and 'local myLocal = 4', where '|' is the cursor position. In both cases, the cursor position is equal
// to `AstStatLocal.location.end`. However, in the first case (semicolon), we are starting a new statement, whilst in the second case
// (no semicolon) we are still part of the AstStatLocal, hence the different comparison check.
if (stat->location.begin < pos && (stat->hasSemicolon ? pos < stat->location.end : pos <= stat->location.end))
{
ancestry.push_back(stat);
return true;
}
return false;
}
bool visit(AstType* type) override
{
if (type->location.begin < pos && pos <= type->location.end)
{
ancestry.push_back(type);
return true;
}
return false;
}
bool visit(AstTypeError* type) override
{
// For a missing type, match the whole range including the start position
if (type->isMissing && type->location.containsClosed(pos))
{
ancestry.push_back(type);
return true;
}
return false;
}
bool visit(class AstTypePack* typePack) override
{
return true;
}
bool visit(AstStatBlock* block) override
{
// If ancestry is empty, we are inspecting the root of the AST. Its extent is considered to be infinite.
if (ancestry.empty())
{
ancestry.push_back(block);
return true;
}
// AstExprIndexName nodes are nested outside-in, so we want the outermost node in the case of nested nodes.
// ex foo.bar.baz is represented in the AST as IndexName{ IndexName {foo, bar}, baz}
if (!ancestry.empty() && ancestry.back()->is<AstExprIndexName>())
return false;
// Type annotation error might intersect the block statement when the function header is being written,
// annotation takes priority
if (!ancestry.empty() && ancestry.back()->is<AstTypeError>())
return false;
// If the cursor is at the end of an expression or type and simultaneously at the beginning of a block,
// the expression or type wins out.
// The exception to this is if we are in a block under an AstExprFunction. In this case, we consider the position to
// be within the block.
if (block->location.begin == pos && !ancestry.empty())
{
if (ancestry.back()->asExpr() && !ancestry.back()->is<AstExprFunction>())
return false;
if (ancestry.back()->asType())
return false;
}
if (block->location.begin <= pos && pos <= block->location.end)
{
ancestry.push_back(block);
return true;
}
return false;
}
};
struct FindNode : public AstVisitor
{
const Position pos;
@ -49,6 +152,16 @@ struct FindNode : public AstVisitor
return false;
}
bool visit(AstStatFunction* node) override
{
visit(static_cast<AstNode*>(node));
if (node->name->location.contains(pos))
node->name->visit(this);
else if (node->func->location.contains(pos))
node->func->visit(this);
return false;
}
bool visit(AstStatBlock* block) override
{
visit(static_cast<AstNode*>(block));
@ -67,47 +180,97 @@ struct FindNode : public AstVisitor
}
};
struct FindFullAncestry final : public AstVisitor
{
std::vector<AstNode*> nodes;
Position pos;
explicit FindFullAncestry(Position pos)
: pos(pos)
{
}
bool visit(AstNode* node)
{
if (node->location.contains(pos))
{
nodes.push_back(node);
return true;
}
return false;
}
};
} // namespace
std::vector<AstNode*> findAstAncestryOfPosition(const SourceModule& source, Position pos)
FindFullAncestry::FindFullAncestry(Position pos, Position documentEnd, bool includeTypes)
: pos(pos)
, documentEnd(documentEnd)
, includeTypes(includeTypes)
{
FindFullAncestry finder(pos);
source.root->visit(&finder);
return std::move(finder.nodes);
}
bool FindFullAncestry::visit(AstType* type)
{
if (includeTypes)
return visit(static_cast<AstNode*>(type));
else
return false;
}
bool FindFullAncestry::visit(AstStatFunction* node)
{
visit(static_cast<AstNode*>(node));
if (node->name->location.contains(pos))
node->name->visit(this);
else if (node->func->location.contains(pos))
node->func->visit(this);
return false;
}
bool FindFullAncestry::visit(AstNode* node)
{
if (node->location.contains(pos))
{
nodes.push_back(node);
return true;
}
// Edge case: If we ask for the node at the position that is the very end of the document
// return the innermost AST element that ends at that position.
if (node->location.end == documentEnd && pos >= documentEnd)
{
nodes.push_back(node);
return true;
}
return false;
}
std::vector<AstNode*> findAncestryAtPositionForAutocomplete(const SourceModule& source, Position pos)
{
return findAncestryAtPositionForAutocomplete(source.root, pos);
}
std::vector<AstNode*> findAncestryAtPositionForAutocomplete(AstStatBlock* root, Position pos)
{
AutocompleteNodeFinder finder{pos, root};
root->visit(&finder);
return finder.ancestry;
}
std::vector<AstNode*> findAstAncestryOfPosition(const SourceModule& source, Position pos, bool includeTypes)
{
return findAstAncestryOfPosition(source.root, pos, includeTypes);
}
std::vector<AstNode*> findAstAncestryOfPosition(AstStatBlock* root, Position pos, bool includeTypes)
{
const Position end = root->location.end;
if (pos > end)
pos = end;
FindFullAncestry finder(pos, end, includeTypes);
root->visit(&finder);
return finder.nodes;
}
AstNode* findNodeAtPosition(const SourceModule& source, Position pos)
{
const Position end = source.root->location.end;
if (pos < source.root->location.begin)
return source.root;
return findNodeAtPosition(source.root, pos);
}
AstNode* findNodeAtPosition(AstStatBlock* root, Position pos)
{
const Position end = root->location.end;
if (pos < root->location.begin)
return root;
if (pos > end)
pos = end;
FindNode findNode{pos, end};
findNode.visit(source.root);
findNode.visit(root);
return findNode.best;
}
@ -122,7 +285,8 @@ AstExpr* findExprAtPosition(const SourceModule& source, Position pos)
ScopePtr findScopeAtPosition(const Module& module, Position pos)
{
LUAU_ASSERT(!module.scopes.empty());
if (module.scopes.empty())
return nullptr;
Location scopeLocation = module.scopes.front().first;
ScopePtr scope = module.scopes.front().second;
@ -164,10 +328,20 @@ std::optional<TypeId> findExpectedTypeAtPosition(const Module& module, const Sou
static std::optional<AstStatLocal*> findBindingLocalStatement(const SourceModule& source, const Binding& binding)
{
// Bindings coming from global sources (e.g., definition files) have a zero position.
// They cannot be defined from a local statement
if (binding.location == Location{{0, 0}, {0, 0}})
return std::nullopt;
std::vector<AstNode*> nodes = findAstAncestryOfPosition(source, binding.location.begin);
auto iter = std::find_if(nodes.rbegin(), nodes.rend(), [](AstNode* node) {
return node->is<AstStatLocal>();
});
auto iter = std::find_if(
nodes.rbegin(),
nodes.rend(),
[](AstNode* node)
{
return node->is<AstStatLocal>();
}
);
return iter != nodes.rend() ? std::make_optional((*iter)->as<AstStatLocal>()) : std::nullopt;
}
@ -186,14 +360,13 @@ std::optional<Binding> findBindingAtPosition(const Module& module, const SourceM
return std::nullopt;
ScopePtr currentScope = findScopeAtPosition(module, pos);
LUAU_ASSERT(currentScope);
while (currentScope)
{
auto iter = currentScope->bindings.find(name);
if (iter != currentScope->bindings.end() && iter->second.location.begin <= pos)
{
/* Ignore this binding if we're inside its definition. e.g. local abc = abc -- Will take the definition of abc from outer scope */
// Ignore this binding if we're inside its definition. e.g. local abc = abc -- Will take the definition of abc from outer scope
std::optional<AstStatLocal*> bindingStatement = findBindingLocalStatement(source, iter->second);
if (!bindingStatement || !(*bindingStatement)->location.contains(pos))
return iter->second;
@ -306,6 +479,71 @@ ExprOrLocal findExprOrLocalAtPosition(const SourceModule& source, Position pos)
return findVisitor.result;
}
static std::optional<DocumentationSymbol> checkOverloadedDocumentationSymbol(
const Module& module,
const TypeId ty,
const AstExpr* parentExpr,
const std::optional<DocumentationSymbol> documentationSymbol
)
{
if (!documentationSymbol)
return std::nullopt;
// This might be an overloaded function.
if (get<IntersectionType>(follow(ty)))
{
TypeId matchingOverload = nullptr;
if (parentExpr && parentExpr->is<AstExprCall>())
{
if (auto it = module.astOverloadResolvedTypes.find(parentExpr))
{
matchingOverload = *it;
}
}
if (matchingOverload)
{
std::string overloadSymbol = *documentationSymbol + "/overload/";
// Default toString options are fine for this purpose.
overloadSymbol += toString(matchingOverload);
return overloadSymbol;
}
}
return documentationSymbol;
}
static std::optional<DocumentationSymbol> getMetatableDocumentation(
const Module& module,
AstExpr* parentExpr,
const TableType* mtable,
const AstName& index
)
{
auto indexIt = mtable->props.find("__index");
if (indexIt == mtable->props.end())
return std::nullopt;
TypeId followed = follow(indexIt->second.type());
const TableType* ttv = get<TableType>(followed);
if (!ttv)
return std::nullopt;
auto propIt = ttv->props.find(index.value);
if (propIt == ttv->props.end())
return std::nullopt;
if (FFlag::LuauSolverV2)
{
if (auto ty = propIt->second.readTy)
return checkOverloadedDocumentationSymbol(module, *ty, parentExpr, propIt->second.documentationSymbol);
}
else
return checkOverloadedDocumentationSymbol(module, propIt->second.type(), parentExpr, propIt->second.documentationSymbol);
return std::nullopt;
}
std::optional<DocumentationSymbol> getDocumentationSymbolAtPosition(const SourceModule& source, const Module& module, Position position)
{
std::vector<AstNode*> ancestry = findAstAncestryOfPosition(source, position);
@ -314,33 +552,7 @@ std::optional<DocumentationSymbol> getDocumentationSymbolAtPosition(const Source
AstExpr* parentExpr = ancestry.size() >= 2 ? ancestry[ancestry.size() - 2]->asExpr() : nullptr;
if (std::optional<Binding> binding = findBindingAtPosition(module, source, position))
{
if (binding->documentationSymbol)
{
// This might be an overloaded function binding.
if (get<IntersectionTypeVar>(follow(binding->typeId)))
{
TypeId matchingOverload = nullptr;
if (parentExpr && parentExpr->is<AstExprCall>())
{
if (auto it = module.astOverloadResolvedTypes.find(parentExpr))
{
matchingOverload = *it;
}
}
if (matchingOverload)
{
std::string overloadSymbol = *binding->documentationSymbol + "/overload/";
// Default toString options are fine for this purpose.
overloadSymbol += toString(matchingOverload);
return overloadSymbol;
}
}
}
return binding->documentationSymbol;
}
return checkOverloadedDocumentationSymbol(module, binding->typeId, parentExpr, binding->documentationSymbol);
if (targetExpr)
{
@ -349,18 +561,44 @@ std::optional<DocumentationSymbol> getDocumentationSymbolAtPosition(const Source
if (auto it = module.astTypes.find(indexName->expr))
{
TypeId parentTy = follow(*it);
if (const TableTypeVar* ttv = get<TableTypeVar>(parentTy))
if (const TableType* ttv = get<TableType>(parentTy))
{
if (auto propIt = ttv->props.find(indexName->index.value); propIt != ttv->props.end())
{
return propIt->second.documentationSymbol;
if (FFlag::LuauSolverV2)
{
if (auto ty = propIt->second.readTy)
return checkOverloadedDocumentationSymbol(module, *ty, parentExpr, propIt->second.documentationSymbol);
}
else
return checkOverloadedDocumentationSymbol(module, propIt->second.type(), parentExpr, propIt->second.documentationSymbol);
}
}
else if (const ClassTypeVar* ctv = get<ClassTypeVar>(parentTy))
else if (const ExternType* etv = get<ExternType>(parentTy))
{
if (auto propIt = ctv->props.find(indexName->index.value); propIt != ctv->props.end())
while (etv)
{
return propIt->second.documentationSymbol;
if (auto propIt = etv->props.find(indexName->index.value); propIt != etv->props.end())
{
if (FFlag::LuauSolverV2)
{
if (auto ty = propIt->second.readTy)
return checkOverloadedDocumentationSymbol(module, *ty, parentExpr, propIt->second.documentationSymbol);
}
else
return checkOverloadedDocumentationSymbol(
module, propIt->second.type(), parentExpr, propIt->second.documentationSymbol
);
}
etv = etv->parent ? Luau::get<Luau::ExternType>(*etv->parent) : nullptr;
}
}
else if (const PrimitiveType* ptv = get<PrimitiveType>(parentTy); ptv && ptv->metatable)
{
if (auto mtable = get<TableType>(*ptv->metatable))
{
if (std::optional<std::string> docSymbol = getMetatableDocumentation(module, parentExpr, mtable, indexName->index))
return docSymbol;
}
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,27 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/AutocompleteTypes.h"
namespace Luau
{
struct Module;
struct FileResolver;
using ModulePtr = std::shared_ptr<Module>;
using ModuleName = std::string;
AutocompleteResult autocomplete_(
const ModulePtr& module,
NotNull<BuiltinTypes> builtinTypes,
TypeArena* typeArena,
std::vector<AstNode*>& ancestry,
Scope* globalScope,
const ScopePtr& scopeAtPosition,
Position position,
FileResolver* fileResolver,
StringCompletionCallback callback
);
} // namespace Luau

File diff suppressed because it is too large Load diff

748
Analysis/src/Clone.cpp Normal file
View file

@ -0,0 +1,748 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/Clone.h"
#include "Luau/Common.h"
#include "Luau/NotNull.h"
#include "Luau/Type.h"
#include "Luau/TypePack.h"
#include "Luau/Unifiable.h"
#include "Luau/VisitType.h"
LUAU_FASTFLAG(LuauSolverV2)
// For each `Luau::clone` call, we will clone only up to N amount of types _and_ packs, as controlled by this limit.
LUAU_FASTINTVARIABLE(LuauTypeCloneIterationLimit, 100'000)
LUAU_FASTFLAGVARIABLE(LuauClonedTableAndFunctionTypesMustHaveScopes)
LUAU_FASTFLAGVARIABLE(LuauDoNotClonePersistentBindings)
LUAU_FASTFLAG(LuauIncrementalAutocompleteDemandBasedCloning)
namespace Luau
{
namespace
{
using Kind = Variant<TypeId, TypePackId>;
template<typename T>
const T* get(const Kind& kind)
{
return get_if<T>(&kind);
}
class TypeCloner
{
protected:
NotNull<TypeArena> arena;
NotNull<BuiltinTypes> builtinTypes;
// A queue of kinds where we cloned it, but whose interior types hasn't
// been updated to point to new clones. Once all of its interior types
// has been updated, it gets removed from the queue.
std::vector<Kind> queue;
NotNull<SeenTypes> types;
NotNull<SeenTypePacks> packs;
TypeId forceTy = nullptr;
TypePackId forceTp = nullptr;
int steps = 0;
public:
TypeCloner(
NotNull<TypeArena> arena,
NotNull<BuiltinTypes> builtinTypes,
NotNull<SeenTypes> types,
NotNull<SeenTypePacks> packs,
TypeId forceTy,
TypePackId forceTp
)
: arena(arena)
, builtinTypes(builtinTypes)
, types(types)
, packs(packs)
, forceTy(forceTy)
, forceTp(forceTp)
{
}
virtual ~TypeCloner() = default;
TypeId clone(TypeId ty)
{
shallowClone(ty);
run();
if (hasExceededIterationLimit())
{
TypeId error = builtinTypes->errorRecoveryType();
(*types)[ty] = error;
return error;
}
return find(ty).value_or(builtinTypes->errorRecoveryType());
}
TypePackId clone(TypePackId tp)
{
shallowClone(tp);
run();
if (hasExceededIterationLimit())
{
TypePackId error = builtinTypes->errorRecoveryTypePack();
(*packs)[tp] = error;
return error;
}
return find(tp).value_or(builtinTypes->errorRecoveryTypePack());
}
private:
bool hasExceededIterationLimit() const
{
if (FInt::LuauTypeCloneIterationLimit == 0)
return false;
return steps + queue.size() >= size_t(FInt::LuauTypeCloneIterationLimit);
}
void run()
{
while (!queue.empty())
{
++steps;
if (hasExceededIterationLimit())
break;
Kind kind = queue.back();
queue.pop_back();
if (find(kind))
continue;
cloneChildren(kind);
}
}
protected:
std::optional<TypeId> find(TypeId ty) const
{
ty = follow(ty, FollowOption::DisableLazyTypeThunks);
if (auto it = types->find(ty); it != types->end())
return it->second;
else if (ty->persistent && ty != forceTy)
return ty;
return std::nullopt;
}
std::optional<TypePackId> find(TypePackId tp) const
{
tp = follow(tp);
if (auto it = packs->find(tp); it != packs->end())
return it->second;
else if (tp->persistent && tp != forceTp)
return tp;
return std::nullopt;
}
std::optional<Kind> find(Kind kind) const
{
if (auto ty = get<TypeId>(kind))
return find(*ty);
else if (auto tp = get<TypePackId>(kind))
return find(*tp);
else
{
LUAU_ASSERT(!"Unknown kind?");
return std::nullopt;
}
}
public:
virtual TypeId shallowClone(TypeId ty)
{
// We want to [`Luau::follow`] but without forcing the expansion of [`LazyType`]s.
ty = follow(ty, FollowOption::DisableLazyTypeThunks);
if (auto clone = find(ty))
return *clone;
else if (ty->persistent && ty != forceTy)
return ty;
TypeId target = arena->addType(ty->ty);
asMutable(target)->documentationSymbol = ty->documentationSymbol;
if (auto generic = getMutable<GenericType>(target))
generic->scope = nullptr;
else if (auto free = getMutable<FreeType>(target))
free->scope = nullptr;
else if (auto table = getMutable<TableType>(target))
table->scope = nullptr;
(*types)[ty] = target;
queue.push_back(target);
return target;
}
virtual TypePackId shallowClone(TypePackId tp)
{
tp = follow(tp);
if (auto clone = find(tp))
return *clone;
else if (tp->persistent && tp != forceTp)
return tp;
TypePackId target = arena->addTypePack(tp->ty);
if (auto generic = getMutable<GenericTypePack>(target))
generic->scope = nullptr;
else if (auto free = getMutable<FreeTypePack>(target))
free->scope = nullptr;
(*packs)[tp] = target;
queue.push_back(target);
return target;
}
private:
Property shallowClone(const Property& p)
{
if (FFlag::LuauSolverV2)
{
std::optional<TypeId> cloneReadTy;
if (auto ty = p.readTy)
cloneReadTy = shallowClone(*ty);
std::optional<TypeId> cloneWriteTy;
if (auto ty = p.writeTy)
cloneWriteTy = shallowClone(*ty);
Property cloned = Property::create(cloneReadTy, cloneWriteTy);
cloned.deprecated = p.deprecated;
cloned.deprecatedSuggestion = p.deprecatedSuggestion;
cloned.location = p.location;
cloned.tags = p.tags;
cloned.documentationSymbol = p.documentationSymbol;
cloned.typeLocation = p.typeLocation;
return cloned;
}
else
{
return Property{
shallowClone(p.type()),
p.deprecated,
p.deprecatedSuggestion,
p.location,
p.tags,
p.documentationSymbol,
p.typeLocation,
};
}
}
void cloneChildren(TypeId ty)
{
return visit(
[&](auto&& t)
{
return cloneChildren(&t);
},
asMutable(ty)->ty
);
}
void cloneChildren(TypePackId tp)
{
return visit(
[&](auto&& t)
{
return cloneChildren(&t);
},
asMutable(tp)->ty
);
}
void cloneChildren(Kind kind)
{
if (auto ty = get<TypeId>(kind))
return cloneChildren(*ty);
else if (auto tp = get<TypePackId>(kind))
return cloneChildren(*tp);
else
LUAU_ASSERT(!"Item holds neither TypeId nor TypePackId when enqueuing its children?");
}
void cloneChildren(ErrorType* t)
{
// noop.
}
void cloneChildren(BoundType* t)
{
t->boundTo = shallowClone(t->boundTo);
}
void cloneChildren(FreeType* t)
{
if (t->lowerBound)
t->lowerBound = shallowClone(t->lowerBound);
if (t->upperBound)
t->upperBound = shallowClone(t->upperBound);
}
void cloneChildren(GenericType* t)
{
// TOOD: clone upper bounds.
}
void cloneChildren(PrimitiveType* t)
{
// noop.
}
void cloneChildren(BlockedType* t)
{
// TODO: In the new solver, we should ice.
}
void cloneChildren(PendingExpansionType* t)
{
// TODO: In the new solver, we should ice.
}
void cloneChildren(SingletonType* t)
{
// noop.
}
void cloneChildren(FunctionType* t)
{
for (TypeId& g : t->generics)
g = shallowClone(g);
for (TypePackId& gp : t->genericPacks)
gp = shallowClone(gp);
t->argTypes = shallowClone(t->argTypes);
t->retTypes = shallowClone(t->retTypes);
}
void cloneChildren(TableType* t)
{
if (t->indexer)
{
t->indexer->indexType = shallowClone(t->indexer->indexType);
t->indexer->indexResultType = shallowClone(t->indexer->indexResultType);
}
for (auto& [_, p] : t->props)
p = shallowClone(p);
for (TypeId& ty : t->instantiatedTypeParams)
ty = shallowClone(ty);
for (TypePackId& tp : t->instantiatedTypePackParams)
tp = shallowClone(tp);
}
void cloneChildren(MetatableType* t)
{
t->table = shallowClone(t->table);
t->metatable = shallowClone(t->metatable);
}
void cloneChildren(ExternType* t)
{
for (auto& [_, p] : t->props)
p = shallowClone(p);
if (t->parent)
t->parent = shallowClone(*t->parent);
if (t->metatable)
t->metatable = shallowClone(*t->metatable);
if (t->indexer)
{
t->indexer->indexType = shallowClone(t->indexer->indexType);
t->indexer->indexResultType = shallowClone(t->indexer->indexResultType);
}
}
void cloneChildren(AnyType* t)
{
// noop.
}
void cloneChildren(NoRefineType* t)
{
// noop.
}
void cloneChildren(UnionType* t)
{
for (TypeId& ty : t->options)
ty = shallowClone(ty);
}
void cloneChildren(IntersectionType* t)
{
for (TypeId& ty : t->parts)
ty = shallowClone(ty);
}
virtual void cloneChildren(LazyType* t)
{
if (auto unwrapped = t->unwrapped.load())
t->unwrapped.store(shallowClone(unwrapped));
}
void cloneChildren(UnknownType* t)
{
// noop.
}
void cloneChildren(NeverType* t)
{
// noop.
}
void cloneChildren(NegationType* t)
{
t->ty = shallowClone(t->ty);
}
void cloneChildren(TypeFunctionInstanceType* t)
{
for (TypeId& ty : t->typeArguments)
ty = shallowClone(ty);
for (TypePackId& tp : t->packArguments)
tp = shallowClone(tp);
}
void cloneChildren(FreeTypePack* t)
{
// TODO: clone lower and upper bounds.
// TODO: In the new solver, we should ice.
}
void cloneChildren(GenericTypePack* t)
{
// TOOD: clone upper bounds.
}
void cloneChildren(BlockedTypePack* t)
{
// TODO: In the new solver, we should ice.
}
void cloneChildren(BoundTypePack* t)
{
t->boundTo = shallowClone(t->boundTo);
}
void cloneChildren(ErrorTypePack* t)
{
// noop.
}
void cloneChildren(VariadicTypePack* t)
{
t->ty = shallowClone(t->ty);
}
void cloneChildren(TypePack* t)
{
for (TypeId& ty : t->head)
ty = shallowClone(ty);
if (t->tail)
t->tail = shallowClone(*t->tail);
}
void cloneChildren(TypeFunctionInstanceTypePack* t)
{
for (TypeId& ty : t->typeArguments)
ty = shallowClone(ty);
for (TypePackId& tp : t->packArguments)
tp = shallowClone(tp);
}
};
class FragmentAutocompleteTypeCloner final : public TypeCloner
{
Scope* replacementForNullScope = nullptr;
public:
FragmentAutocompleteTypeCloner(
NotNull<TypeArena> arena,
NotNull<BuiltinTypes> builtinTypes,
NotNull<SeenTypes> types,
NotNull<SeenTypePacks> packs,
TypeId forceTy,
TypePackId forceTp,
Scope* replacementForNullScope
)
: TypeCloner(arena, builtinTypes, types, packs, forceTy, forceTp)
, replacementForNullScope(replacementForNullScope)
{
LUAU_ASSERT(replacementForNullScope);
}
TypeId shallowClone(TypeId ty) override
{
// We want to [`Luau::follow`] but without forcing the expansion of [`LazyType`]s.
ty = follow(ty, FollowOption::DisableLazyTypeThunks);
if (auto clone = find(ty))
return *clone;
else if (ty->persistent && ty != forceTy)
return ty;
TypeId target = arena->addType(ty->ty);
asMutable(target)->documentationSymbol = ty->documentationSymbol;
if (auto generic = getMutable<GenericType>(target))
generic->scope = nullptr;
else if (auto free = getMutable<FreeType>(target))
{
free->scope = replacementForNullScope;
}
else if (auto tt = getMutable<TableType>(target))
{
if (FFlag::LuauClonedTableAndFunctionTypesMustHaveScopes)
tt->scope = replacementForNullScope;
}
(*types)[ty] = target;
queue.emplace_back(target);
return target;
}
TypePackId shallowClone(TypePackId tp) override
{
tp = follow(tp);
if (auto clone = find(tp))
return *clone;
else if (tp->persistent && tp != forceTp)
return tp;
TypePackId target = arena->addTypePack(tp->ty);
if (auto generic = getMutable<GenericTypePack>(target))
generic->scope = nullptr;
else if (auto free = getMutable<FreeTypePack>(target))
free->scope = replacementForNullScope;
(*packs)[tp] = target;
queue.emplace_back(target);
return target;
}
void cloneChildren(LazyType* t) override
{
// Do not clone lazy types
if (!FFlag::LuauIncrementalAutocompleteDemandBasedCloning)
{
if (auto unwrapped = t->unwrapped.load())
t->unwrapped.store(shallowClone(unwrapped));
}
}
};
} // namespace
TypePackId shallowClone(TypePackId tp, TypeArena& dest, CloneState& cloneState, bool ignorePersistent)
{
if (tp->persistent && !ignorePersistent)
return tp;
TypeCloner cloner{
NotNull{&dest},
cloneState.builtinTypes,
NotNull{&cloneState.seenTypes},
NotNull{&cloneState.seenTypePacks},
nullptr,
ignorePersistent ? tp : nullptr
};
return cloner.shallowClone(tp);
}
TypeId shallowClone(TypeId typeId, TypeArena& dest, CloneState& cloneState, bool ignorePersistent)
{
if (typeId->persistent && !ignorePersistent)
return typeId;
TypeCloner cloner{
NotNull{&dest},
cloneState.builtinTypes,
NotNull{&cloneState.seenTypes},
NotNull{&cloneState.seenTypePacks},
ignorePersistent ? typeId : nullptr,
nullptr
};
return cloner.shallowClone(typeId);
}
TypePackId clone(TypePackId tp, TypeArena& dest, CloneState& cloneState)
{
if (tp->persistent)
return tp;
TypeCloner cloner{NotNull{&dest}, cloneState.builtinTypes, NotNull{&cloneState.seenTypes}, NotNull{&cloneState.seenTypePacks}, nullptr, nullptr};
return cloner.clone(tp);
}
TypeId clone(TypeId typeId, TypeArena& dest, CloneState& cloneState)
{
if (typeId->persistent)
return typeId;
TypeCloner cloner{NotNull{&dest}, cloneState.builtinTypes, NotNull{&cloneState.seenTypes}, NotNull{&cloneState.seenTypePacks}, nullptr, nullptr};
return cloner.clone(typeId);
}
TypeFun clone(const TypeFun& typeFun, TypeArena& dest, CloneState& cloneState)
{
TypeCloner cloner{NotNull{&dest}, cloneState.builtinTypes, NotNull{&cloneState.seenTypes}, NotNull{&cloneState.seenTypePacks}, nullptr, nullptr};
TypeFun copy = typeFun;
for (auto& param : copy.typeParams)
{
param.ty = cloner.clone(param.ty);
if (param.defaultValue)
param.defaultValue = cloner.clone(*param.defaultValue);
}
for (auto& param : copy.typePackParams)
{
param.tp = cloner.clone(param.tp);
if (param.defaultValue)
param.defaultValue = cloner.clone(*param.defaultValue);
}
copy.type = cloner.clone(copy.type);
return copy;
}
Binding clone(const Binding& binding, TypeArena& dest, CloneState& cloneState)
{
TypeCloner cloner{NotNull{&dest}, cloneState.builtinTypes, NotNull{&cloneState.seenTypes}, NotNull{&cloneState.seenTypePacks}, nullptr, nullptr};
Binding b;
b.deprecated = binding.deprecated;
b.deprecatedSuggestion = binding.deprecatedSuggestion;
b.documentationSymbol = binding.documentationSymbol;
b.location = binding.location;
b.typeId = cloner.clone(binding.typeId);
return b;
}
TypePackId cloneIncremental(TypePackId tp, TypeArena& dest, CloneState& cloneState, Scope* freshScopeForFreeTypes)
{
if (tp->persistent)
return tp;
FragmentAutocompleteTypeCloner cloner{
NotNull{&dest},
cloneState.builtinTypes,
NotNull{&cloneState.seenTypes},
NotNull{&cloneState.seenTypePacks},
nullptr,
nullptr,
freshScopeForFreeTypes
};
return cloner.clone(tp);
}
TypeId cloneIncremental(TypeId typeId, TypeArena& dest, CloneState& cloneState, Scope* freshScopeForFreeTypes)
{
if (typeId->persistent)
return typeId;
FragmentAutocompleteTypeCloner cloner{
NotNull{&dest},
cloneState.builtinTypes,
NotNull{&cloneState.seenTypes},
NotNull{&cloneState.seenTypePacks},
nullptr,
nullptr,
freshScopeForFreeTypes
};
return cloner.clone(typeId);
}
TypeFun cloneIncremental(const TypeFun& typeFun, TypeArena& dest, CloneState& cloneState, Scope* freshScopeForFreeTypes)
{
FragmentAutocompleteTypeCloner cloner{
NotNull{&dest},
cloneState.builtinTypes,
NotNull{&cloneState.seenTypes},
NotNull{&cloneState.seenTypePacks},
nullptr,
nullptr,
freshScopeForFreeTypes
};
TypeFun copy = typeFun;
for (auto& param : copy.typeParams)
{
param.ty = cloner.clone(param.ty);
if (param.defaultValue)
param.defaultValue = cloner.clone(*param.defaultValue);
}
for (auto& param : copy.typePackParams)
{
param.tp = cloner.clone(param.tp);
if (param.defaultValue)
param.defaultValue = cloner.clone(*param.defaultValue);
}
copy.type = cloner.clone(copy.type);
return copy;
}
Binding cloneIncremental(const Binding& binding, TypeArena& dest, CloneState& cloneState, Scope* freshScopeForFreeTypes)
{
FragmentAutocompleteTypeCloner cloner{
NotNull{&dest},
cloneState.builtinTypes,
NotNull{&cloneState.seenTypes},
NotNull{&cloneState.seenTypePacks},
nullptr,
nullptr,
freshScopeForFreeTypes
};
Binding b;
b.deprecated = binding.deprecated;
b.deprecatedSuggestion = binding.deprecatedSuggestion;
b.documentationSymbol = binding.documentationSymbol;
b.location = binding.location;
b.typeId = FFlag::LuauDoNotClonePersistentBindings && binding.typeId->persistent ? binding.typeId : cloner.clone(binding.typeId);
return b;
}
} // namespace Luau

Some files were not shown because too many files have changed in this diff Show more