mirror of
https://github.com/luau-lang/luau.git
synced 2025-05-04 10:33:46 +01:00
Compare commits
No commits in common. "master" and "0.634" have entirely different histories.
589 changed files with 19609 additions and 71520 deletions
|
@ -16,14 +16,10 @@ 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
|
||||
|
|
4
.github/workflows/benchmark.yml
vendored
4
.github/workflows/benchmark.yml
vendored
|
@ -63,10 +63,10 @@ jobs:
|
|||
}
|
||||
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=strict --fflags=DebugLuauDeferredConstraintResolution 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
|
||||
valgrind --tool=callgrind ./luau-analyze --mode=strict --fflags=DebugLuauDeferredConstraintResolution bench/other/regex.lua 2>&1 | filter regex-dcr | tee -a analyze-output.txt
|
||||
|
||||
- name: Run benchmark (compile)
|
||||
run: |
|
||||
|
|
16
.github/workflows/build.yml
vendored
16
.github/workflows/build.yml
vendored
|
@ -46,9 +46,9 @@ jobs:
|
|||
- name: make cli
|
||||
run: |
|
||||
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
|
||||
./luau tests/conformance/assert.lua
|
||||
./luau-analyze tests/conformance/assert.lua
|
||||
./luau-compile tests/conformance/assert.lua
|
||||
|
||||
windows:
|
||||
runs-on: windows-latest
|
||||
|
@ -81,12 +81,12 @@ jobs:
|
|||
shell: bash # necessary for fail-fast
|
||||
run: |
|
||||
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
|
||||
Debug/luau tests/conformance/assert.lua
|
||||
Debug/luau-analyze tests/conformance/assert.lua
|
||||
Debug/luau-compile tests/conformance/assert.lua
|
||||
|
||||
coverage:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-20.04 # needed for clang++-10 to avoid gcov compatibility issues
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: install
|
||||
|
@ -94,7 +94,7 @@ jobs:
|
|||
sudo apt install llvm
|
||||
- name: make coverage
|
||||
run: |
|
||||
CXX=clang++ make -j2 config=coverage native=1 coverage
|
||||
CXX=clang++-10 make -j2 config=coverage native=1 coverage
|
||||
- name: upload coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
|
|
6
.github/workflows/new-release.yml
vendored
6
.github/workflows/new-release.yml
vendored
|
@ -29,8 +29,8 @@ jobs:
|
|||
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}]
|
||||
matrix: # using ubuntu-20.04 to build a Linux binary targeting older glibc to improve compatibility
|
||||
os: [{name: ubuntu, version: ubuntu-20.04}, {name: macos, version: macos-latest}, {name: windows, version: windows-latest}]
|
||||
name: ${{matrix.os.name}}
|
||||
runs-on: ${{matrix.os.version}}
|
||||
steps:
|
||||
|
@ -38,7 +38,7 @@ jobs:
|
|||
- 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
|
||||
run: cmake --build . --target Luau.Repl.CLI Luau.Analyze.CLI Luau.Compile.CLI --config Release -j 2
|
||||
- name: pack
|
||||
if: matrix.os.name != 'windows'
|
||||
run: zip luau-${{matrix.os.name}}.zip luau*
|
||||
|
|
14
.github/workflows/release.yml
vendored
14
.github/workflows/release.yml
vendored
|
@ -13,8 +13,8 @@ on:
|
|||
jobs:
|
||||
build:
|
||||
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}]
|
||||
matrix: # using ubuntu-20.04 to build a Linux binary targeting older glibc to improve compatibility
|
||||
os: [{name: ubuntu, version: ubuntu-20.04}, {name: macos, version: macos-latest}, {name: windows, version: windows-latest}]
|
||||
name: ${{matrix.os.name}}
|
||||
runs-on: ${{matrix.os.version}}
|
||||
steps:
|
||||
|
@ -23,18 +23,17 @@ jobs:
|
|||
run: cmake . -DCMAKE_BUILD_TYPE=Release
|
||||
- name: build
|
||||
run: cmake --build . --target Luau.Repl.CLI Luau.Analyze.CLI Luau.Compile.CLI --config Release -j 2
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: matrix.os.name != 'windows'
|
||||
with:
|
||||
name: luau-${{matrix.os.name}}
|
||||
path: luau*
|
||||
overwrite: true
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: matrix.os.name == 'windows'
|
||||
with:
|
||||
name: luau-${{matrix.os.name}}
|
||||
path: Release\luau*.exe
|
||||
overwrite: true
|
||||
|
||||
web:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
@ -53,8 +52,7 @@ jobs:
|
|||
source emsdk/emsdk_env.sh
|
||||
emcmake cmake . -DLUAU_BUILD_WEB=ON -DCMAKE_BUILD_TYPE=Release
|
||||
make -j2 Luau.Web
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: Luau.Web.js
|
||||
path: Luau.Web.js
|
||||
overwrite: true
|
||||
|
|
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1,6 +1,5 @@
|
|||
/build/
|
||||
/build[.-]*/
|
||||
/out
|
||||
/cmake/
|
||||
/cmake[.-]*/
|
||||
/coverage/
|
||||
|
@ -13,8 +12,5 @@
|
|||
/luau
|
||||
/luau-tests
|
||||
/luau-analyze
|
||||
/luau-bytecode
|
||||
/luau-compile
|
||||
__pycache__
|
||||
.cache
|
||||
.clangd
|
||||
|
|
|
@ -19,22 +19,10 @@ 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
|
||||
);
|
||||
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;
|
||||
|
|
|
@ -61,22 +61,6 @@ private:
|
|||
AstLocal* local = nullptr;
|
||||
};
|
||||
|
||||
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);
|
||||
|
|
|
@ -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/Type.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
@ -16,8 +16,89 @@ struct Frontend;
|
|||
struct SourceModule;
|
||||
struct Module;
|
||||
struct TypeChecker;
|
||||
struct FileResolver;
|
||||
|
||||
using ModulePtr = std::shared_ptr<Module>;
|
||||
|
||||
enum class AutocompleteContext
|
||||
{
|
||||
Unknown,
|
||||
Expression,
|
||||
Statement,
|
||||
Property,
|
||||
Type,
|
||||
Keyword,
|
||||
String,
|
||||
};
|
||||
|
||||
enum class AutocompleteEntryKind
|
||||
{
|
||||
Property,
|
||||
Binding,
|
||||
Keyword,
|
||||
String,
|
||||
Type,
|
||||
Module,
|
||||
GeneratedFunction,
|
||||
};
|
||||
|
||||
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 ClassType*> containingClass = 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 ModuleName = std::string;
|
||||
using StringCompletionCallback =
|
||||
std::function<std::optional<AutocompleteEntryMap>(std::string tag, std::optional<const ClassType*> ctx, std::optional<std::string> contents)>;
|
||||
|
||||
AutocompleteResult autocomplete(Frontend& frontend, const ModuleName& moduleName, Position position, StringCompletionCallback callback);
|
||||
|
||||
constexpr char kGeneratedAnonymousFunctionEntryName[] = "function (anonymous autofilled)";
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -1,92 +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/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
|
|
@ -9,13 +9,10 @@
|
|||
namespace Luau
|
||||
{
|
||||
|
||||
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);
|
||||
|
@ -28,49 +25,30 @@ 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,
|
||||
bool checked = false
|
||||
);
|
||||
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,
|
||||
bool checked = false
|
||||
);
|
||||
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,
|
||||
bool checked = false
|
||||
);
|
||||
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,
|
||||
bool checked = false
|
||||
);
|
||||
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, MagicFunction fn);
|
||||
void attachDcrMagicFunction(TypeId ty, DcrMagicFunction fn);
|
||||
void attachDcrMagicRefinement(TypeId ty, DcrMagicRefinement fn);
|
||||
|
||||
void attachMagicFunction(TypeId ty, std::shared_ptr<MagicFunction> fn);
|
||||
Property makeProperty(TypeId ty, std::optional<std::string> documentationSymbol = std::nullopt);
|
||||
void assignPropDocumentationSymbols(TableType::Props& props, const std::string& baseName);
|
||||
|
||||
std::string getBuiltinDefinitionSource();
|
||||
std::string getTypeFunctionDefinitionSource();
|
||||
|
||||
void addGlobalBinding(GlobalTypes& globals, const std::string& name, TypeId ty, const std::string& packageName);
|
||||
void addGlobalBinding(GlobalTypes& globals, const std::string& name, Binding binding);
|
||||
|
@ -80,16 +58,4 @@ std::optional<Binding> tryGetGlobalBinding(GlobalTypes& globals, const std::stri
|
|||
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
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#include <Luau/NotNull.h>
|
||||
#include "Luau/TypeArena.h"
|
||||
#include "Luau/Type.h"
|
||||
#include "Luau/Scope.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
|
@ -23,26 +22,8 @@ struct CloneState
|
|||
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
|
||||
|
|
|
@ -50,7 +50,6 @@ struct GeneralizationConstraint
|
|||
TypeId sourceType;
|
||||
|
||||
std::vector<TypeId> interiorTypes;
|
||||
bool hasDeprecatedAttribute = false;
|
||||
};
|
||||
|
||||
// variables ~ iterate iterator
|
||||
|
@ -110,21 +109,6 @@ struct FunctionCheckConstraint
|
|||
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
|
||||
|
@ -272,25 +256,9 @@ 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>;
|
||||
using ConstraintV = Variant<SubtypeConstraint, PackSubtypeConstraint, GeneralizationConstraint, IterableConstraint, NameConstraint,
|
||||
TypeAliasExpansionConstraint, FunctionCallConstraint, FunctionCheckConstraint, PrimitiveTypeConstraint, HasPropConstraint, HasIndexerConstraint,
|
||||
AssignPropConstraint, AssignIndexConstraint, UnpackConstraint, ReduceConstraint, ReducePackConstraint, EqualityConstraint>;
|
||||
|
||||
struct Constraint
|
||||
{
|
||||
|
|
|
@ -3,23 +3,23 @@
|
|||
|
||||
#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 "Luau/Variant.h"
|
||||
#include "Luau/Normalize.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -28,7 +28,6 @@ struct Scope;
|
|||
using ScopePtr = std::shared_ptr<Scope>;
|
||||
|
||||
struct DcrLogger;
|
||||
struct TypeFunctionRuntime;
|
||||
|
||||
struct Inference
|
||||
{
|
||||
|
@ -92,11 +91,9 @@ struct ConstraintGenerator
|
|||
// 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};
|
||||
// Constraints that do not go to the solver right away. Other constraints
|
||||
// will enqueue them during solving.
|
||||
std::vector<ConstraintPtr> unqueuedConstraints;
|
||||
|
||||
// The private scope of type aliases for which the type parameters belong to.
|
||||
DenseHashMap<const AstStatTypeAlias*, ScopePtr> astTypeAliasDefiningScopes{nullptr};
|
||||
|
@ -111,48 +108,23 @@ struct ConstraintGenerator
|
|||
|
||||
// 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);
|
||||
ConstraintGenerator(ModulePtr module, NotNull<Normalizer> normalizer, NotNull<ModuleResolver> moduleResolver, NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<InternalErrorReporter> ice, const ScopePtr& globalScope, std::function<void(const ModuleName&, const ScopePtr&)> prepareModuleScope,
|
||||
DcrLogger* logger, NotNull<DataFlowGraph> dfg, std::vector<RequireCycle> requireCycles);
|
||||
|
||||
/**
|
||||
* The entry point to the ConstraintGenerator. This will construct a set
|
||||
|
@ -161,29 +133,20 @@ struct ConstraintGenerator
|
|||
*/
|
||||
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;
|
||||
std::vector<std::vector<TypeId>> interiorTypes;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
TypeId freshType(const ScopePtr& scope);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
TypePackId freshTypePack(const ScopePtr& scope);
|
||||
|
||||
/**
|
||||
* Allocate a new TypePack with the given head and tail.
|
||||
|
@ -232,29 +195,13 @@ private:
|
|||
};
|
||||
|
||||
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 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);
|
||||
|
@ -270,19 +217,14 @@ private:
|
|||
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, AstStatDeclareClass* declareClass);
|
||||
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
|
||||
);
|
||||
const ScopePtr& scope, AstExpr* expr, const std::vector<std::optional<TypeId>>& expectedTypes = {}, bool generalize = true);
|
||||
|
||||
InferencePack checkPack(const ScopePtr& scope, AstExprCall* call);
|
||||
|
||||
|
@ -296,15 +238,10 @@ private:
|
|||
* @return the type of the expression.
|
||||
*/
|
||||
Inference check(
|
||||
const ScopePtr& scope,
|
||||
AstExpr* expr,
|
||||
std::optional<TypeId> expectedType = {},
|
||||
bool forceSingleton = false,
|
||||
bool generalize = true
|
||||
);
|
||||
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, AstExprConstantBool* bool_, 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);
|
||||
|
@ -313,25 +250,11 @@ private:
|
|||
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
|
||||
);
|
||||
std::tuple<TypeId, TypeId, RefinementId> checkBinary(const ScopePtr& scope, AstExprBinary* binary, std::optional<TypeId> expectedType);
|
||||
|
||||
void visitLValue(const ScopePtr& scope, AstExpr* expr, TypeId rhsType);
|
||||
void visitLValue(const ScopePtr& scope, AstExprLocal* local, TypeId rhsType);
|
||||
|
@ -353,11 +276,7 @@ private:
|
|||
};
|
||||
|
||||
FunctionSignature checkFunctionSignature(
|
||||
const ScopePtr& parent,
|
||||
AstExprFunction* fn,
|
||||
std::optional<TypeId> expectedType = {},
|
||||
std::optional<Location> originalName = {}
|
||||
);
|
||||
const ScopePtr& parent, AstExprFunction* fn, std::optional<TypeId> expectedType = {}, std::optional<Location> originalName = {});
|
||||
|
||||
/**
|
||||
* Checks the body of a function expression.
|
||||
|
@ -366,11 +285,6 @@ private:
|
|||
*/
|
||||
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.
|
||||
|
@ -380,11 +294,6 @@ private:
|
|||
**/
|
||||
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.
|
||||
|
@ -394,9 +303,6 @@ private:
|
|||
**/
|
||||
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.
|
||||
|
@ -417,11 +323,7 @@ private:
|
|||
* privateTypeBindings map.
|
||||
**/
|
||||
std::vector<std::pair<Name, GenericTypeDefinition>> createGenerics(
|
||||
const ScopePtr& scope,
|
||||
AstArray<AstGenericType*> generics,
|
||||
bool useCache = false,
|
||||
bool addTypes = true
|
||||
);
|
||||
const ScopePtr& scope, AstArray<AstGenericType> generics, bool useCache = false, bool addTypes = true);
|
||||
|
||||
/**
|
||||
* Creates generic type packs given a list of AST definitions, resolving
|
||||
|
@ -434,11 +336,7 @@ private:
|
|||
* privateTypePackBindings map.
|
||||
**/
|
||||
std::vector<std::pair<Name, GenericTypePackDefinition>> createGenericPacks(
|
||||
const ScopePtr& scope,
|
||||
AstArray<AstGenericTypePack*> generics,
|
||||
bool useCache = false,
|
||||
bool addTypes = true
|
||||
);
|
||||
const ScopePtr& scope, AstArray<AstGenericTypePack> packs, bool useCache = false, bool addTypes = true);
|
||||
|
||||
Inference flattenPack(const ScopePtr& scope, Location location, InferencePack pack);
|
||||
|
||||
|
@ -449,7 +347,6 @@ private:
|
|||
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.
|
||||
*
|
||||
|
@ -474,14 +371,12 @@ private:
|
|||
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);
|
||||
const TypeFunction& family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments, const ScopePtr& scope, Location location);
|
||||
};
|
||||
|
||||
/** Borrow a vector of pointers from a vector of owning pointers to constraints.
|
||||
*/
|
||||
std::vector<NotNull<Constraint>> borrowConstraints(const std::vector<ConstraintPtr>& constraints);
|
||||
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -1,32 +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/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;
|
||||
};
|
||||
|
||||
}
|
|
@ -3,10 +3,7 @@
|
|||
#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"
|
||||
|
@ -15,7 +12,6 @@
|
|||
#include "Luau/ToString.h"
|
||||
#include "Luau/Type.h"
|
||||
#include "Luau/TypeCheckLimits.h"
|
||||
#include "Luau/TypeFunction.h"
|
||||
#include "Luau/TypeFwd.h"
|
||||
#include "Luau/Variant.h"
|
||||
|
||||
|
@ -60,43 +56,17 @@ 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;
|
||||
|
@ -121,11 +91,8 @@ struct ConstraintSolver
|
|||
// 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{{}};
|
||||
// Irreducible/uninhabited type families or type pack families.
|
||||
DenseHashSet<const void*> uninhabitedTypeFamilies{{}};
|
||||
|
||||
// The set of types that will definitely be unchanged by generalization.
|
||||
DenseHashSet<TypeId> generalizedTypes_{nullptr};
|
||||
|
@ -140,35 +107,11 @@ struct ConstraintSolver
|
|||
DcrLogger* logger;
|
||||
TypeCheckLimits limits;
|
||||
|
||||
DenseHashMap<TypeId, const Constraint*> typeFunctionsToFinalize{nullptr};
|
||||
DenseHashMap<TypeId, const Constraint*> typeFamiliesToFinalize{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
|
||||
);
|
||||
explicit ConstraintSolver(NotNull<Normalizer> normalizer, NotNull<Scope> rootScope, std::vector<NotNull<Constraint>> constraints,
|
||||
ModuleName moduleName, NotNull<ModuleResolver> moduleResolver, std::vector<RequireCycle> requireCycles, DcrLogger* logger,
|
||||
TypeCheckLimits limits);
|
||||
|
||||
// Randomize the order in which to dispatch constraints
|
||||
void randomize(unsigned seed);
|
||||
|
@ -181,19 +124,14 @@ struct ConstraintSolver
|
|||
|
||||
|
||||
/**
|
||||
* Attempts to perform one final reduction on type functions after every constraint has been completed
|
||||
* Attempts to perform one final reduction on type families after every constraint has been completed
|
||||
*
|
||||
**/
|
||||
void finalizeTypeFunctions();
|
||||
void finalizeTypeFamilies();
|
||||
|
||||
bool isDone() const;
|
||||
bool isDone();
|
||||
|
||||
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.
|
||||
*
|
||||
|
@ -219,27 +157,20 @@ public:
|
|||
*/
|
||||
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 SubtypeConstraint& c, NotNull<const Constraint> constraint, bool force);
|
||||
bool tryDispatch(const PackSubtypeConstraint& c, NotNull<const Constraint> constraint, bool force);
|
||||
bool tryDispatch(const GeneralizationConstraint& c, NotNull<const Constraint> constraint, bool force);
|
||||
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
|
||||
);
|
||||
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);
|
||||
|
@ -247,33 +178,19 @@ public:
|
|||
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);
|
||||
bool tryDispatch(const EqualityConstraint& c, NotNull<const Constraint> constraint, bool force);
|
||||
|
||||
// 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);
|
||||
bool tryDispatchIterableFunction(TypeId nextTy, TypeId tableTy, const IterableConstraint& c, NotNull<const Constraint> constraint, bool force);
|
||||
|
||||
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
|
||||
);
|
||||
std::pair<std::vector<TypeId>, std::optional<TypeId>> lookupTableProp(NotNull<const Constraint> constraint, TypeId subjectType,
|
||||
const std::string& propName, ValueContext context, bool inConditional = false, bool suppressSimplification = false);
|
||||
std::pair<std::vector<TypeId>, std::optional<TypeId>> 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
|
||||
|
@ -324,10 +241,10 @@ public:
|
|||
// 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);
|
||||
bool blockOnPendingTypes(TypePackId target, NotNull<const Constraint> constraint);
|
||||
|
||||
void unblock(NotNull<const Constraint> progressed);
|
||||
void unblock(TypeId ty, Location location);
|
||||
void unblock(TypeId progressed, 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);
|
||||
|
@ -335,18 +252,18 @@ public:
|
|||
/**
|
||||
* @returns true if the TypeId is in a blocked state.
|
||||
*/
|
||||
bool isBlocked(TypeId ty) const;
|
||||
bool isBlocked(TypeId ty);
|
||||
|
||||
/**
|
||||
* @returns true if the TypePackId is in a blocked state.
|
||||
*/
|
||||
bool isBlocked(TypePackId tp) const;
|
||||
bool isBlocked(TypePackId tp);
|
||||
|
||||
/**
|
||||
* Returns whether the constraint is blocked on anything.
|
||||
* @param constraint the constraint to check.
|
||||
*/
|
||||
bool isBlocked(NotNull<const Constraint> constraint) const;
|
||||
bool isBlocked(NotNull<const Constraint> constraint);
|
||||
|
||||
/** Pushes a new solver constraint to the solver.
|
||||
* @param cv the body of the constraint.
|
||||
|
@ -362,7 +279,7 @@ public:
|
|||
* @param location the location where the require is taking place; used for
|
||||
* error locations.
|
||||
**/
|
||||
TypeId resolveModule(const ModuleInfo& info, const Location& location);
|
||||
TypeId resolveModule(const ModuleInfo& module, const Location& location);
|
||||
|
||||
void reportError(TypeErrorData&& data, const Location& location);
|
||||
void reportError(TypeError e);
|
||||
|
@ -383,7 +300,7 @@ public:
|
|||
* @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);
|
||||
std::optional<TypeId> generalizeFreeType(NotNull<Scope> scope, TypeId type, bool avoidSealingTables = false);
|
||||
|
||||
/**
|
||||
* Checks the existing set of constraints to see if there exist any that contain
|
||||
|
@ -428,32 +345,22 @@ public:
|
|||
|
||||
/**
|
||||
* 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.
|
||||
* At the time of writing, this pertains only to type families.
|
||||
* @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;
|
||||
void throwTimeLimitError();
|
||||
void throwUserCancelError();
|
||||
|
||||
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
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#include "Luau/ControlFlow.h"
|
||||
#include "Luau/DenseHash.h"
|
||||
#include "Luau/Def.h"
|
||||
#include "Luau/NotNull.h"
|
||||
#include "Luau/Symbol.h"
|
||||
#include "Luau/TypedAllocator.h"
|
||||
|
||||
|
@ -36,8 +35,8 @@ struct DataFlowGraph
|
|||
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;
|
||||
// Look up for the rvalue def for a compound assignment.
|
||||
std::optional<DefId> getRValueDefForCompoundAssign(const AstExpr* expr) const;
|
||||
|
||||
DefId getDef(const AstLocal* local) const;
|
||||
|
||||
|
@ -47,13 +46,13 @@ struct DataFlowGraph
|
|||
const RefinementKey* getRefinementKey(const AstExpr* expr) const;
|
||||
|
||||
private:
|
||||
DataFlowGraph(NotNull<DefArena> defArena, NotNull<RefinementKeyArena> keyArena);
|
||||
DataFlowGraph() = default;
|
||||
|
||||
DataFlowGraph(const DataFlowGraph&) = delete;
|
||||
DataFlowGraph& operator=(const DataFlowGraph&) = delete;
|
||||
|
||||
NotNull<DefArena> defArena;
|
||||
NotNull<RefinementKeyArena> keyArena;
|
||||
DefArena defArena;
|
||||
RefinementKeyArena keyArena;
|
||||
|
||||
DenseHashMap<const AstExpr*, const Def*> astDefs{nullptr};
|
||||
|
||||
|
@ -64,7 +63,12 @@ private:
|
|||
// All keys in this maps are really only statements that ambiently declares a symbol.
|
||||
DenseHashMap<const AstStat*, const Def*> declaredDefs{nullptr};
|
||||
|
||||
// Compound assignments are in a weird situation where the local being assigned to is also being used at its
|
||||
// previous type implicitly in an rvalue position. This map provides the previous binding.
|
||||
DenseHashMap<const AstExpr*, const Def*> compoundAssignDefs{nullptr};
|
||||
|
||||
DenseHashMap<const AstExpr*, const RefinementKey*> astRefinementKeys{nullptr};
|
||||
|
||||
friend struct DataFlowGraphBuilder;
|
||||
};
|
||||
|
||||
|
@ -101,37 +105,25 @@ struct DataFlowResult
|
|||
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
|
||||
);
|
||||
static DataFlowGraph build(AstStatBlock* root, NotNull<struct InternalErrorReporter> handle);
|
||||
|
||||
private:
|
||||
DataFlowGraphBuilder(NotNull<DefArena> defArena, NotNull<RefinementKeyArena> keyArena);
|
||||
DataFlowGraphBuilder() = default;
|
||||
|
||||
DataFlowGraphBuilder(const DataFlowGraphBuilder&) = delete;
|
||||
DataFlowGraphBuilder& operator=(const DataFlowGraphBuilder&) = delete;
|
||||
|
||||
DataFlowGraph graph;
|
||||
NotNull<DefArena> defArena;
|
||||
NotNull<RefinementKeyArena> keyArena;
|
||||
NotNull<DefArena> defArena{&graph.defArena};
|
||||
NotNull<RefinementKeyArena> keyArena{&graph.keyArena};
|
||||
|
||||
struct InternalErrorReporter* handle = nullptr;
|
||||
DfgScope* moduleScope = 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;
|
||||
|
@ -142,81 +134,80 @@ private:
|
|||
DenseHashMap<Symbol, FunctionCapture> captures{Symbol{}};
|
||||
void resolveCaptures();
|
||||
|
||||
DfgScope* makeChildScope(DfgScope::ScopeType scopeType = DfgScope::Linear);
|
||||
DfgScope* childScope(DfgScope* scope, 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);
|
||||
DefId lookup(DfgScope* scope, Symbol symbol);
|
||||
DefId lookup(DfgScope* scope, DefId def, const std::string& key);
|
||||
|
||||
ControlFlow visit(AstStatBlock* b);
|
||||
ControlFlow visitBlockWithoutChildScope(AstStatBlock* b);
|
||||
ControlFlow visit(DfgScope* scope, AstStatBlock* b);
|
||||
ControlFlow visitBlockWithoutChildScope(DfgScope* scope, 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);
|
||||
ControlFlow visit(DfgScope* scope, AstStat* s);
|
||||
ControlFlow visit(DfgScope* scope, AstStatIf* i);
|
||||
ControlFlow visit(DfgScope* scope, AstStatWhile* w);
|
||||
ControlFlow visit(DfgScope* scope, AstStatRepeat* r);
|
||||
ControlFlow visit(DfgScope* scope, AstStatBreak* b);
|
||||
ControlFlow visit(DfgScope* scope, AstStatContinue* c);
|
||||
ControlFlow visit(DfgScope* scope, AstStatReturn* r);
|
||||
ControlFlow visit(DfgScope* scope, AstStatExpr* e);
|
||||
ControlFlow visit(DfgScope* scope, AstStatLocal* l);
|
||||
ControlFlow visit(DfgScope* scope, AstStatFor* f);
|
||||
ControlFlow visit(DfgScope* scope, AstStatForIn* f);
|
||||
ControlFlow visit(DfgScope* scope, AstStatAssign* a);
|
||||
ControlFlow visit(DfgScope* scope, AstStatCompoundAssign* c);
|
||||
ControlFlow visit(DfgScope* scope, AstStatFunction* f);
|
||||
ControlFlow visit(DfgScope* scope, AstStatLocalFunction* l);
|
||||
ControlFlow visit(DfgScope* scope, AstStatTypeAlias* t);
|
||||
ControlFlow visit(DfgScope* scope, AstStatDeclareGlobal* d);
|
||||
ControlFlow visit(DfgScope* scope, AstStatDeclareFunction* d);
|
||||
ControlFlow visit(DfgScope* scope, AstStatDeclareClass* d);
|
||||
ControlFlow visit(DfgScope* scope, 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);
|
||||
DataFlowResult visitExpr(DfgScope* scope, AstExpr* e);
|
||||
DataFlowResult visitExpr(DfgScope* scope, AstExprGroup* group);
|
||||
DataFlowResult visitExpr(DfgScope* scope, AstExprLocal* l);
|
||||
DataFlowResult visitExpr(DfgScope* scope, AstExprGlobal* g);
|
||||
DataFlowResult visitExpr(DfgScope* scope, AstExprCall* c);
|
||||
DataFlowResult visitExpr(DfgScope* scope, AstExprIndexName* i);
|
||||
DataFlowResult visitExpr(DfgScope* scope, AstExprIndexExpr* i);
|
||||
DataFlowResult visitExpr(DfgScope* scope, AstExprFunction* f);
|
||||
DataFlowResult visitExpr(DfgScope* scope, AstExprTable* t);
|
||||
DataFlowResult visitExpr(DfgScope* scope, AstExprUnary* u);
|
||||
DataFlowResult visitExpr(DfgScope* scope, AstExprBinary* b);
|
||||
DataFlowResult visitExpr(DfgScope* scope, AstExprTypeAssertion* t);
|
||||
DataFlowResult visitExpr(DfgScope* scope, AstExprIfElse* i);
|
||||
DataFlowResult visitExpr(DfgScope* scope, AstExprInterpString* i);
|
||||
DataFlowResult visitExpr(DfgScope* scope, 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 visitLValue(DfgScope* scope, AstExpr* e, DefId incomingDef, bool isCompoundAssignment = false);
|
||||
DefId visitLValue(DfgScope* scope, AstExprLocal* l, DefId incomingDef, bool isCompoundAssignment);
|
||||
DefId visitLValue(DfgScope* scope, AstExprGlobal* g, DefId incomingDef, bool isCompoundAssignment);
|
||||
DefId visitLValue(DfgScope* scope, AstExprIndexName* i, DefId incomingDef);
|
||||
DefId visitLValue(DfgScope* scope, AstExprIndexExpr* i, DefId incomingDef);
|
||||
DefId visitLValue(DfgScope* scope, 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 visitType(DfgScope* scope, AstType* t);
|
||||
void visitType(DfgScope* scope, AstTypeReference* r);
|
||||
void visitType(DfgScope* scope, AstTypeTable* t);
|
||||
void visitType(DfgScope* scope, AstTypeFunction* f);
|
||||
void visitType(DfgScope* scope, AstTypeTypeof* t);
|
||||
void visitType(DfgScope* scope, AstTypeUnion* u);
|
||||
void visitType(DfgScope* scope, AstTypeIntersection* i);
|
||||
void visitType(DfgScope* scope, AstTypeError* error);
|
||||
|
||||
void visitTypePack(AstTypePack* p);
|
||||
void visitTypePack(AstTypePackExplicit* e);
|
||||
void visitTypePack(AstTypePackVariadic* v);
|
||||
void visitTypePack(AstTypePackGeneric* g);
|
||||
void visitTypePack(DfgScope* scope, AstTypePack* p);
|
||||
void visitTypePack(DfgScope* scope, AstTypePackExplicit* e);
|
||||
void visitTypePack(DfgScope* scope, AstTypePackVariadic* v);
|
||||
void visitTypePack(DfgScope* scope, AstTypePackGeneric* g);
|
||||
|
||||
void visitTypeList(AstTypeList l);
|
||||
void visitTypeList(DfgScope* scope, AstTypeList l);
|
||||
|
||||
void visitGenerics(AstArray<AstGenericType*> g);
|
||||
void visitGenericPacks(AstArray<AstGenericTypePack*> g);
|
||||
void visitGenerics(DfgScope* scope, AstArray<AstGenericType> g);
|
||||
void visitGenericPacks(DfgScope* scope, AstArray<AstGenericTypePack> g);
|
||||
};
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -126,11 +126,7 @@ struct DcrLogger
|
|||
|
||||
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
|
||||
);
|
||||
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);
|
||||
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
#include "Luau/NotNull.h"
|
||||
#include "Luau/TypedAllocator.h"
|
||||
#include "Luau/Variant.h"
|
||||
#include "Luau/Location.h"
|
||||
#include "Luau/Symbol.h"
|
||||
|
||||
#include <string>
|
||||
#include <optional>
|
||||
|
||||
|
@ -14,7 +13,6 @@ namespace Luau
|
|||
|
||||
struct Def;
|
||||
using DefId = NotNull<const Def>;
|
||||
struct AstLocal;
|
||||
|
||||
/**
|
||||
* A cell is a "single-object" value.
|
||||
|
@ -66,8 +64,6 @@ struct Def
|
|||
using V = Variant<struct Cell, struct Phi>;
|
||||
|
||||
V v;
|
||||
Symbol name;
|
||||
Location location;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
@ -83,7 +79,7 @@ struct DefArena
|
|||
{
|
||||
TypedAllocator<Def> allocator;
|
||||
|
||||
DefId freshCell(Symbol sym, Location location, bool subscripted = false);
|
||||
DefId freshCell(bool subscripted = false);
|
||||
DefId phi(DefId a, DefId b);
|
||||
DefId phi(const std::vector<DefId>& defs);
|
||||
};
|
||||
|
|
|
@ -62,12 +62,7 @@ struct DiffPathNodeLeaf
|
|||
// 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
|
||||
)
|
||||
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)
|
||||
|
@ -164,11 +159,7 @@ struct DifferEnvironment
|
|||
DenseHashMap<TypePackId, TypePackId> genericTpMatchedPairs;
|
||||
|
||||
DifferEnvironment(
|
||||
TypeId rootLeft,
|
||||
TypeId rootRight,
|
||||
std::optional<std::string> externalSymbolLeft,
|
||||
std::optional<std::string> externalSymbolRight
|
||||
)
|
||||
TypeId rootLeft, TypeId rootRight, std::optional<std::string> externalSymbolLeft, std::optional<std::string> externalSymbolRight)
|
||||
: rootLeft(rootLeft)
|
||||
, rootRight(rootRight)
|
||||
, externalSymbolLeft(externalSymbolLeft)
|
||||
|
|
|
@ -1,50 +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/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
|
|
@ -1,376 +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/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
|
|
@ -194,11 +194,6 @@ struct InternalError
|
|||
bool operator==(const InternalError& rhs) const;
|
||||
};
|
||||
|
||||
struct ConstraintSolvingIncompleteError
|
||||
{
|
||||
bool operator==(const ConstraintSolvingIncompleteError& rhs) const;
|
||||
};
|
||||
|
||||
struct CannotCallNonFunction
|
||||
{
|
||||
TypeId ty;
|
||||
|
@ -332,11 +327,11 @@ struct TypePackMismatch
|
|||
bool operator==(const TypePackMismatch& rhs) const;
|
||||
};
|
||||
|
||||
struct DynamicPropertyLookupOnExternTypesUnsafe
|
||||
struct DynamicPropertyLookupOnClassesUnsafe
|
||||
{
|
||||
TypeId ty;
|
||||
|
||||
bool operator==(const DynamicPropertyLookupOnExternTypesUnsafe& rhs) const;
|
||||
bool operator==(const DynamicPropertyLookupOnClassesUnsafe& rhs) const;
|
||||
};
|
||||
|
||||
struct UninhabitedTypeFunction
|
||||
|
@ -448,71 +443,15 @@ struct UnexpectedTypePackInSubtyping
|
|||
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>;
|
||||
using TypeErrorData =
|
||||
Variant<TypeMismatch, UnknownSymbol, UnknownProperty, NotATable, CannotExtendTable, OnlyTablesCanHaveMethods, DuplicateTypeDefinition,
|
||||
CountMismatch, FunctionDoesNotTakeSelf, FunctionRequiresSelf, OccursCheckFailed, UnknownRequire, IncorrectGenericParameterCount, SyntaxError,
|
||||
CodeTooComplex, UnificationTooComplex, UnknownPropButFoundLikeProp, GenericError, InternalError, CannotCallNonFunction, ExtraInformation,
|
||||
DeprecatedApiUsed, ModuleHasCyclicDependency, IllegalRequire, FunctionExitsWithoutReturning, DuplicateGenericParameter, CannotAssignToNever,
|
||||
CannotInferBinaryOperation, MissingProperties, SwappedGenericTypeParameter, OptionalValueAccess, MissingUnionProperty, TypesAreUnrelated,
|
||||
NormalizationTooComplex, TypePackMismatch, DynamicPropertyLookupOnClassesUnsafe, UninhabitedTypeFunction, UninhabitedTypePackFunction,
|
||||
WhereClauseNeeded, PackWhereClauseNeeded, CheckedFunctionCallError, NonStrictFunctionDefinitionError, PropertyAccessViolation,
|
||||
CheckedFunctionIncorrectArgs, UnexpectedTypeInSubtyping, UnexpectedTypePackInSubtyping, ExplicitFunctionAnnotationRecommended>;
|
||||
|
||||
struct TypeErrorSummary
|
||||
{
|
||||
|
|
|
@ -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 <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -20,7 +18,7 @@ struct SourceCode
|
|||
None,
|
||||
Module,
|
||||
Script,
|
||||
Local_DEPRECATED
|
||||
Local
|
||||
};
|
||||
|
||||
std::string source;
|
||||
|
@ -33,71 +31,8 @@ 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;
|
||||
|
@ -116,10 +51,6 @@ struct FileResolver
|
|||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<RequireSuggestions> getRequireSuggestions(const ModuleName& requirer, const std::optional<std::string>& pathString) const;
|
||||
|
||||
std::shared_ptr<RequireSuggester> requireSuggester;
|
||||
};
|
||||
|
||||
struct NullFileResolver : FileResolver
|
||||
|
|
|
@ -1,196 +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/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
|
|
@ -7,7 +7,6 @@
|
|||
#include "Luau/ModuleResolver.h"
|
||||
#include "Luau/RequireTracer.h"
|
||||
#include "Luau/Scope.h"
|
||||
#include "Luau/Set.h"
|
||||
#include "Luau/TypeCheckLimits.h"
|
||||
#include "Luau/Variant.h"
|
||||
|
||||
|
@ -31,7 +30,6 @@ struct ModuleResolver;
|
|||
struct ParseResult;
|
||||
struct HotComment;
|
||||
struct BuildQueueItem;
|
||||
struct BuildQueueWorkState;
|
||||
struct FrontendCancellationToken;
|
||||
|
||||
struct LoadDefinitionFileResult
|
||||
|
@ -44,6 +42,21 @@ struct LoadDefinitionFileResult
|
|||
|
||||
std::optional<Mode> parseMode(const std::vector<HotComment>& 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);
|
||||
|
||||
struct SourceNode
|
||||
{
|
||||
bool hasDirtySourceModule() const
|
||||
|
@ -56,32 +69,13 @@ struct SourceNode
|
|||
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::string humanReadableName;
|
||||
DenseHashSet<ModuleName> requireSet{{}};
|
||||
std::vector<std::pair<ModuleName, Location>> requireLocations;
|
||||
Set<ModuleName> dependents{{}};
|
||||
|
||||
bool dirtySourceModule = true;
|
||||
bool dirtyModule = true;
|
||||
bool dirtyModuleForAutocomplete = true;
|
||||
|
||||
bool invalidModuleDependency = true;
|
||||
bool invalidModuleDependencyForAutocomplete = true;
|
||||
|
||||
double autocompleteLimitsMult = 1.0;
|
||||
};
|
||||
|
||||
|
@ -112,10 +106,6 @@ struct FrontendOptions
|
|||
|
||||
// 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
|
||||
|
@ -136,7 +126,7 @@ 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 setModule(const ModuleName& moduleName, ModulePtr module);
|
||||
void clearModules();
|
||||
|
||||
private:
|
||||
|
@ -170,13 +160,9 @@ struct Frontend
|
|||
// Parse and typecheck module graph
|
||||
CheckResult check(const ModuleName& name, std::optional<FrontendOptions> optionOverride = {}); // new shininess
|
||||
|
||||
bool allModuleDependenciesValid(const ModuleName& name, bool forAutocomplete = false) 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
|
||||
|
@ -197,62 +183,33 @@ struct Frontend
|
|||
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
|
||||
);
|
||||
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::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:
|
||||
ModulePtr check(
|
||||
const SourceModule& sourceModule,
|
||||
Mode mode,
|
||||
std::vector<RequireCycle> requireCycles,
|
||||
std::optional<ScopePtr> environmentScope,
|
||||
bool forAutocomplete,
|
||||
bool recordJsonLog,
|
||||
TypeCheckLimits typeCheckLimits
|
||||
);
|
||||
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,
|
||||
const ModuleName& root,
|
||||
bool forAutocomplete,
|
||||
std::function<bool(const ModuleName&)> canSkip = {}
|
||||
);
|
||||
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 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);
|
||||
|
||||
|
@ -289,36 +246,14 @@ public:
|
|||
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, 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
|
||||
);
|
||||
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, std::function<void(const ModuleName&, const ScopePtr&)> prepareModuleScope, FrontendOptions options,
|
||||
TypeCheckLimits limits, bool recordJsonLog, std::function<void(const ModuleName&, std::string)> writeJsonLog);
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -8,75 +8,6 @@
|
|||
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
|
||||
std::optional<TypeId> generalize(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, NotNull<Scope> scope,
|
||||
NotNull<DenseHashSet<TypeId>> bakedTypes, TypeId ty, /* avoid sealing tables*/ bool avoidSealingTables = false);
|
||||
}
|
||||
|
|
|
@ -19,9 +19,7 @@ struct GlobalTypes
|
|||
|
||||
TypeArena globalTypes;
|
||||
SourceModule globalNames; // names for symbols entered into globalScope
|
||||
|
||||
ScopePtr globalScope; // shared by all modules
|
||||
ScopePtr globalTypeFunctionScope; // shared by all modules
|
||||
};
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -1,16 +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/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
|
|
@ -67,19 +67,6 @@ public:
|
|||
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();
|
||||
|
|
|
@ -17,15 +17,8 @@ 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
|
||||
)
|
||||
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)
|
||||
|
@ -35,15 +28,8 @@ struct ReplaceGenerics : Substitution
|
|||
{
|
||||
}
|
||||
|
||||
void resetState(
|
||||
const TxnLog* log,
|
||||
TypeArena* arena,
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
TypeLevel level,
|
||||
Scope* scope,
|
||||
const std::vector<TypeId>& generics,
|
||||
const std::vector<TypePackId>& 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;
|
||||
|
||||
|
@ -60,7 +46,7 @@ struct ReplaceGenerics : Substitution
|
|||
};
|
||||
|
||||
// A substitution which replaces generic functions by monomorphic functions
|
||||
struct Instantiation final : Substitution
|
||||
struct Instantiation : Substitution
|
||||
{
|
||||
Instantiation(const TxnLog* log, TypeArena* arena, NotNull<BuiltinTypes> builtinTypes, TypeLevel level, Scope* scope)
|
||||
: Substitution(log, arena)
|
||||
|
@ -133,9 +119,9 @@ struct GenericTypeFinder : TypeOnceVisitor
|
|||
return false;
|
||||
}
|
||||
|
||||
bool visit(TypeId ty, const Luau::ExternType&) override
|
||||
bool visit(TypeId ty, const Luau::ClassType&) override
|
||||
{
|
||||
// During function instantiation, extern types are not traversed even if they have generics
|
||||
// During function instantiation, classes are not traversed even if they have generics
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
@ -155,11 +141,6 @@ struct GenericTypeFinder : TypeOnceVisitor
|
|||
* limits to be exceeded.
|
||||
*/
|
||||
std::optional<TypeId> instantiate(
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<TypeArena> arena,
|
||||
NotNull<TypeCheckLimits> limits,
|
||||
NotNull<Scope> scope,
|
||||
TypeId ty
|
||||
);
|
||||
NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, NotNull<TypeCheckLimits> limits, NotNull<Scope> scope, TypeId ty);
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -53,7 +53,7 @@ struct Replacer : Substitution
|
|||
};
|
||||
|
||||
// A substitution which replaces generic functions by monomorphic functions
|
||||
struct Instantiation2 final : Substitution
|
||||
struct Instantiation2 : Substitution
|
||||
{
|
||||
// Mapping from generic types to free types to be used in instantiation.
|
||||
DenseHashMap<TypeId, TypeId> genericSubstitutions{nullptr};
|
||||
|
@ -75,16 +75,8 @@ struct Instantiation2 final : Substitution
|
|||
};
|
||||
|
||||
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
|
||||
);
|
||||
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
|
||||
|
|
|
@ -25,14 +25,8 @@ struct LintResult
|
|||
std::vector<LintWarning> warnings;
|
||||
};
|
||||
|
||||
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<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);
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#include "Luau/ParseResult.h"
|
||||
#include "Luau/Scope.h"
|
||||
#include "Luau/TypeArena.h"
|
||||
#include "Luau/DataFlowGraph.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
@ -18,12 +17,6 @@
|
|||
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>;
|
||||
|
@ -59,7 +52,6 @@ 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);
|
||||
|
||||
|
@ -73,9 +65,6 @@ struct Module
|
|||
{
|
||||
~Module();
|
||||
|
||||
// TODO: Clip this when we clip FFlagLuauSolverV2
|
||||
bool checkedInNewSolver = false;
|
||||
|
||||
ModuleName name;
|
||||
std::string humanReadableName;
|
||||
|
||||
|
@ -85,7 +74,6 @@ struct Module
|
|||
// 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
|
||||
|
||||
|
@ -138,11 +126,6 @@ struct Module
|
|||
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;
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ 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
|
||||
|
|
|
@ -1,30 +1,19 @@
|
|||
// 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"
|
||||
#include "Luau/DataFlowGraph.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
|
||||
);
|
||||
void checkNonStrict(NotNull<BuiltinTypes> builtinTypes, NotNull<InternalErrorReporter> ice, NotNull<UnifierSharedState> unifierState,
|
||||
NotNull<const DataFlowGraph> dfg, NotNull<TypeCheckLimits> limits, const SourceModule& sourceModule, Module* module);
|
||||
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// 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"
|
||||
|
@ -22,22 +21,10 @@ 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
|
||||
);
|
||||
bool isSubtype(TypeId subTy, TypeId superTy, NotNull<Scope> scope, NotNull<BuiltinTypes> builtinTypes, InternalErrorReporter& ice);
|
||||
bool isSubtype(TypePackId subTy, TypePackId superTy, NotNull<Scope> scope, NotNull<BuiltinTypes> builtinTypes, InternalErrorReporter& ice);
|
||||
bool isConsistentSubtype(TypeId subTy, TypeId superTy, NotNull<Scope> scope, NotNull<BuiltinTypes> builtinTypes, InternalErrorReporter& ice);
|
||||
bool isConsistentSubtype(TypePackId subTy, TypePackId superTy, NotNull<Scope> scope, NotNull<BuiltinTypes> builtinTypes, InternalErrorReporter& ice);
|
||||
|
||||
class TypeIds
|
||||
{
|
||||
|
@ -72,7 +59,6 @@ public:
|
|||
const_iterator begin() const;
|
||||
const_iterator end() const;
|
||||
iterator erase(const_iterator it);
|
||||
void erase(TypeId ty);
|
||||
|
||||
size_t size() const;
|
||||
bool empty() const;
|
||||
|
@ -181,7 +167,7 @@ struct NormalizedStringType
|
|||
|
||||
bool isSubtype(const NormalizedStringType& subStr, const NormalizedStringType& superStr);
|
||||
|
||||
struct NormalizedExternType
|
||||
struct NormalizedClassType
|
||||
{
|
||||
/** Has the following structure:
|
||||
*
|
||||
|
@ -192,7 +178,7 @@ struct NormalizedExternType
|
|||
*
|
||||
* Each TypeId is a class type.
|
||||
*/
|
||||
std::unordered_map<TypeId, TypeIds> externTypes;
|
||||
std::unordered_map<TypeId, TypeIds> classes;
|
||||
|
||||
/**
|
||||
* In order to maintain a consistent insertion order, we use this vector to
|
||||
|
@ -245,7 +231,7 @@ enum class NormalizationResult
|
|||
};
|
||||
|
||||
// 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)
|
||||
// * P is a union of primitive types (including singletons, classes 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
|
||||
|
@ -260,7 +246,7 @@ struct NormalizedType
|
|||
// This type is either never, boolean type, or a boolean singleton.
|
||||
TypeId booleans;
|
||||
|
||||
NormalizedExternType externTypes;
|
||||
NormalizedClassType classes;
|
||||
|
||||
// The error part of the type.
|
||||
// This type is either never or the error type.
|
||||
|
@ -333,7 +319,7 @@ struct NormalizedType
|
|||
// 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 hasClasses() const;
|
||||
bool hasErrors() const;
|
||||
bool hasNils() const;
|
||||
bool hasNumbers() const;
|
||||
|
@ -349,7 +335,6 @@ struct NormalizedType
|
|||
};
|
||||
|
||||
|
||||
using SeenTablePropPairs = Set<std::pair<TypeId, TypeId>, TypeIdPairHash>;
|
||||
|
||||
class Normalizer
|
||||
{
|
||||
|
@ -391,10 +376,10 @@ public:
|
|||
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 unionClassesWithClass(TypeIds& heres, TypeId there);
|
||||
void unionClasses(TypeIds& heres, const TypeIds& theres);
|
||||
void unionClassesWithClass(NormalizedClassType& heres, TypeId there);
|
||||
void unionClasses(NormalizedClassType& heres, const NormalizedClassType& theres);
|
||||
void unionStrings(NormalizedStringType& here, const NormalizedStringType& there);
|
||||
std::optional<TypePackId> unionOfTypePacks(TypePackId here, TypePackId there);
|
||||
std::optional<TypeId> unionOfFunctions(TypeId here, TypeId there);
|
||||
|
@ -404,13 +389,7 @@ public:
|
|||
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
|
||||
);
|
||||
NormalizationResult unionNormalWithTy(NormalizedType& here, TypeId there, Set<TypeId>& seenSetTypes, int ignoreSmallerTyvars = -1);
|
||||
|
||||
// ------- Negations
|
||||
std::optional<NormalizedType> negateNormal(const NormalizedType& here);
|
||||
|
@ -423,30 +402,20 @@ public:
|
|||
// ------- 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 intersectClasses(NormalizedClassType& heres, const NormalizedClassType& theres);
|
||||
void intersectClassesWithClass(NormalizedClassType& 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);
|
||||
std::optional<TypeId> intersectionOfTables(TypeId here, TypeId there, Set<TypeId>& seenSet);
|
||||
void intersectTablesWithTable(TypeIds& heres, TypeId there, 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 intersectTyvarsWithTy(NormalizedTyvars& here, TypeId there, 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
|
||||
);
|
||||
NormalizationResult intersectNormalWithTy(NormalizedType& here, TypeId there, Set<TypeId>& seenSetTypes);
|
||||
NormalizationResult normalizeIntersections(const std::vector<TypeId>& intersections, NormalizedType& outType, Set<TypeId>& seenSet);
|
||||
|
||||
// Check for inhabitance
|
||||
NormalizationResult isInhabited(TypeId ty);
|
||||
|
@ -456,7 +425,7 @@ public:
|
|||
|
||||
// Check for intersections being inhabited
|
||||
NormalizationResult isIntersectionInhabited(TypeId left, TypeId right);
|
||||
NormalizationResult isIntersectionInhabited(TypeId left, TypeId right, SeenTablePropPairs& seenTablePropPairs, Set<TypeId>& seenSet);
|
||||
NormalizationResult isIntersectionInhabited(TypeId left, TypeId right, Set<TypeId>& seenSet);
|
||||
|
||||
// -------- Convert back from a normalized type to a type
|
||||
TypeId typeFromNormal(const NormalizedType& norm);
|
||||
|
|
|
@ -2,13 +2,12 @@
|
|||
#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"
|
||||
#include "Luau/Location.h"
|
||||
#include "Luau/Error.h"
|
||||
#include "Luau/Subtyping.h"
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -32,23 +31,12 @@ struct OverloadResolver
|
|||
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
|
||||
);
|
||||
OverloadResolver(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, NotNull<Normalizer> normalizer, 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;
|
||||
|
@ -70,21 +58,11 @@ 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
|
||||
);
|
||||
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
|
||||
);
|
||||
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);
|
||||
};
|
||||
|
@ -107,21 +85,11 @@ struct SolveResult
|
|||
DenseHashMap<TypeId, std::vector<TypeId>> expandedFreeTypes{nullptr};
|
||||
};
|
||||
|
||||
// Helper utility, presently used for binary operator type functions.
|
||||
// Helper utility, presently used for binary operator type families.
|
||||
//
|
||||
// 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
|
||||
);
|
||||
SolveResult solveFunctionCall(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, NotNull<Normalizer> normalizer,
|
||||
NotNull<InternalErrorReporter> iceReporter, NotNull<TypeCheckLimits> limits, NotNull<Scope> scope, const Location& location, TypeId fn,
|
||||
TypePackId argsPack);
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -1,68 +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 <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
|
|
@ -16,7 +16,7 @@ struct Scope;
|
|||
|
||||
void quantify(TypeId ty, TypeLevel level);
|
||||
|
||||
// TODO: This is eerily similar to the pattern that NormalizedExternType
|
||||
// TODO: This is eerily similar to the pattern that NormalizedClassType
|
||||
// implements. We could, and perhaps should, merge them together.
|
||||
template<typename K, typename V>
|
||||
struct OrderedMap
|
||||
|
@ -31,4 +31,13 @@ struct OrderedMap
|
|||
}
|
||||
};
|
||||
|
||||
struct QuantifierResult
|
||||
{
|
||||
TypeId result;
|
||||
OrderedMap<TypeId, TypeId> insertedGenerics;
|
||||
OrderedMap<TypePackId, TypePackId> insertedGenericPacks;
|
||||
};
|
||||
|
||||
std::optional<QuantifierResult> quantify(TypeArena* arena, TypeId ty, Scope* scope);
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -53,7 +53,6 @@ struct Proposition
|
|||
{
|
||||
const RefinementKey* key;
|
||||
TypeId discriminantTy;
|
||||
bool implicitFromCall;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
@ -70,7 +69,6 @@ struct RefinementArena
|
|||
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;
|
||||
|
|
|
@ -11,12 +11,14 @@
|
|||
namespace Luau
|
||||
{
|
||||
|
||||
class AstNode;
|
||||
class AstStat;
|
||||
class AstExpr;
|
||||
class AstStatBlock;
|
||||
struct AstLocal;
|
||||
|
||||
struct RequireTraceResult
|
||||
{
|
||||
DenseHashMap<const AstNode*, ModuleInfo> exprs{nullptr};
|
||||
DenseHashMap<const AstExpr*, ModuleInfo> exprs{nullptr};
|
||||
|
||||
std::vector<std::pair<ModuleName, Location>> requireList;
|
||||
};
|
||||
|
|
|
@ -35,12 +35,12 @@ struct Scope
|
|||
explicit Scope(TypePackId returnType); // root scope
|
||||
explicit Scope(const ScopePtr& parent, int subLevel = 0); // child scope. Parent must not be nullptr.
|
||||
|
||||
ScopePtr parent; // null for the root
|
||||
const 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 = nullptr;
|
||||
TypePackId returnType;
|
||||
std::optional<TypePackId> varargPack;
|
||||
|
||||
TypeLevel level;
|
||||
|
@ -59,8 +59,6 @@ struct Scope
|
|||
|
||||
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);
|
||||
|
@ -73,7 +71,6 @@ struct Scope
|
|||
|
||||
// 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) const;
|
||||
std::optional<std::pair<Symbol, Binding>> linearSearchForBindingPair(const std::string& name, bool traverseScopeChain) const;
|
||||
|
||||
RefinementMap refinements;
|
||||
|
||||
|
@ -88,19 +85,12 @@ struct Scope
|
|||
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
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include "Luau/Common.h"
|
||||
#include "Luau/DenseHash.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
|
|
@ -19,13 +19,10 @@ struct SimplifyResult
|
|||
DenseHashSet<TypeId> blockedTypes;
|
||||
};
|
||||
|
||||
SimplifyResult simplifyIntersection(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, TypeId left, TypeId right);
|
||||
SimplifyResult simplifyIntersection(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, TypeId ty, TypeId discriminant);
|
||||
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);
|
||||
SimplifyResult simplifyUnion(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, TypeId ty, TypeId discriminant);
|
||||
|
||||
enum class Relation
|
||||
{
|
||||
|
|
|
@ -86,7 +86,6 @@ struct TarjanNode
|
|||
struct Tarjan
|
||||
{
|
||||
Tarjan();
|
||||
virtual ~Tarjan() = default;
|
||||
|
||||
// Vertices (types and type packs) are indexed, using pre-order traversal.
|
||||
DenseHashMap<TypeId, int> typeToIndex{nullptr};
|
||||
|
@ -122,7 +121,7 @@ struct Tarjan
|
|||
void visitChildren(TypePackId tp, int index);
|
||||
|
||||
void visitChild(TypeId ty);
|
||||
void visitChild(TypePackId tp);
|
||||
void visitChild(TypePackId ty);
|
||||
|
||||
template<typename Ty>
|
||||
void visitChild(std::optional<Ty> ty)
|
||||
|
@ -133,7 +132,7 @@ struct Tarjan
|
|||
|
||||
// Visit the root vertex.
|
||||
TarjanResult visitRoot(TypeId ty);
|
||||
TarjanResult visitRoot(TypePackId tp);
|
||||
TarjanResult visitRoot(TypePackId ty);
|
||||
|
||||
// Used to reuse the object for a new operation
|
||||
void clearTarjan(const TxnLog* log);
|
||||
|
@ -151,12 +150,26 @@ struct Tarjan
|
|||
void visitSCC(int index);
|
||||
|
||||
// Each subclass can decide to ignore some nodes.
|
||||
virtual bool ignoreChildren(TypeId ty);
|
||||
virtual bool ignoreChildren(TypePackId ty);
|
||||
virtual bool ignoreChildren(TypeId ty)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool ignoreChildren(TypePackId ty)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Some subclasses might ignore children visit, but not other actions like replacing the children
|
||||
virtual bool ignoreChildrenVisit(TypeId ty);
|
||||
virtual bool ignoreChildrenVisit(TypePackId ty);
|
||||
virtual bool ignoreChildrenVisit(TypeId ty)
|
||||
{
|
||||
return ignoreChildren(ty);
|
||||
}
|
||||
|
||||
virtual bool ignoreChildrenVisit(TypePackId ty)
|
||||
{
|
||||
return ignoreChildren(ty);
|
||||
}
|
||||
|
||||
// Subclasses should say which vertices are dirty,
|
||||
// and what to do with dirty vertices.
|
||||
|
@ -171,7 +184,6 @@ struct Tarjan
|
|||
struct Substitution : Tarjan
|
||||
{
|
||||
protected:
|
||||
explicit Substitution(TypeArena* arena);
|
||||
Substitution(const TxnLog* log_, TypeArena* arena);
|
||||
|
||||
/*
|
||||
|
@ -220,23 +232,28 @@ public:
|
|||
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(T tv)
|
||||
TypeId addType(const T& tv)
|
||||
{
|
||||
return arena->addType(std::move(tv));
|
||||
return arena->addType(tv);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
TypePackId addTypePack(T tp)
|
||||
TypePackId addTypePack(const T& tp)
|
||||
{
|
||||
return arena->addTypePack(TypePackVar{std::move(tp)});
|
||||
return arena->addTypePack(TypePackVar{tp});
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename Ty>
|
||||
std::optional<Ty> replace(std::optional<Ty> ty);
|
||||
std::optional<Ty> replace(std::optional<Ty> ty)
|
||||
{
|
||||
if (ty)
|
||||
return replace(*ty);
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
// 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 "Luau/TypeFunction.h"
|
||||
#include "Luau/TypeCheckLimits.h"
|
||||
#include "Luau/DenseHash.h"
|
||||
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
@ -22,7 +21,7 @@ struct InternalErrorReporter;
|
|||
|
||||
class TypeIds;
|
||||
class Normalizer;
|
||||
struct NormalizedExternType;
|
||||
struct NormalizedClassType;
|
||||
struct NormalizedFunctionType;
|
||||
struct NormalizedStringType;
|
||||
struct NormalizedType;
|
||||
|
@ -97,22 +96,6 @@ struct SubtypingEnvironment
|
|||
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.
|
||||
|
@ -121,7 +104,7 @@ struct SubtypingEnvironment
|
|||
DenseHashMap<TypePackId, TypePackId> mappedGenericPacks{nullptr};
|
||||
|
||||
/*
|
||||
* See the test cyclic_tables_are_assumed_to_be_compatible_with_extern_types for
|
||||
* See the test cyclic_tables_are_assumed_to_be_compatible_with_classes for
|
||||
* details.
|
||||
*
|
||||
* An empty value is equivalent to a nonexistent key.
|
||||
|
@ -129,17 +112,20 @@ struct SubtypingEnvironment
|
|||
DenseHashMap<TypeId, TypeId> substitutions{nullptr};
|
||||
|
||||
DenseHashMap<std::pair<TypeId, TypeId>, SubtypingResult, TypePairHash> ephemeralCache{{}};
|
||||
|
||||
/// 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);
|
||||
};
|
||||
|
||||
struct Subtyping
|
||||
{
|
||||
NotNull<BuiltinTypes> builtinTypes;
|
||||
NotNull<TypeArena> arena;
|
||||
NotNull<Simplifier> simplifier;
|
||||
NotNull<Normalizer> normalizer;
|
||||
NotNull<TypeFunctionRuntime> typeFunctionRuntime;
|
||||
NotNull<InternalErrorReporter> iceReporter;
|
||||
|
||||
NotNull<Scope> scope;
|
||||
TypeCheckLimits limits;
|
||||
|
||||
enum class Variance
|
||||
|
@ -154,14 +140,8 @@ struct Subtyping
|
|||
|
||||
SeenSet seenTypes{{}};
|
||||
|
||||
Subtyping(
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<TypeArena> typeArena,
|
||||
NotNull<Simplifier> simplifier,
|
||||
NotNull<Normalizer> normalizer,
|
||||
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
|
||||
NotNull<InternalErrorReporter> iceReporter
|
||||
);
|
||||
Subtyping(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> typeArena, NotNull<Normalizer> normalizer,
|
||||
NotNull<InternalErrorReporter> iceReporter, NotNull<Scope> scope);
|
||||
|
||||
Subtyping(const Subtyping&) = delete;
|
||||
Subtyping& operator=(const Subtyping&) = delete;
|
||||
|
@ -179,124 +159,68 @@ struct Subtyping
|
|||
// TODO cyclic types
|
||||
// TODO recursion limits
|
||||
|
||||
SubtypingResult isSubtype(TypeId subTy, TypeId superTy, NotNull<Scope> scope);
|
||||
SubtypingResult isSubtype(TypePackId subTy, TypePackId superTy, NotNull<Scope> scope);
|
||||
SubtypingResult isSubtype(TypeId subTy, TypeId superTy);
|
||||
SubtypingResult isSubtype(TypePackId subTy, TypePackId superTy);
|
||||
|
||||
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);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypeId subTy, TypeId superTy);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypePackId subTy, TypePackId superTy);
|
||||
|
||||
template<typename SubTy, typename SuperTy>
|
||||
SubtypingResult isContravariantWith(SubtypingEnvironment& env, SubTy&& subTy, SuperTy&& superTy, NotNull<Scope> scope);
|
||||
SubtypingResult isContravariantWith(SubtypingEnvironment& env, SubTy&& subTy, SuperTy&& superTy);
|
||||
|
||||
template<typename SubTy, typename SuperTy>
|
||||
SubtypingResult isInvariantWith(SubtypingEnvironment& env, SubTy&& subTy, SuperTy&& superTy, NotNull<Scope> scope);
|
||||
SubtypingResult isInvariantWith(SubtypingEnvironment& env, SubTy&& subTy, SuperTy&& superTy);
|
||||
|
||||
template<typename SubTy, typename SuperTy>
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TryPair<const SubTy*, const SuperTy*>& pair, NotNull<Scope> scope);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TryPair<const SubTy*, const SuperTy*>& pair);
|
||||
|
||||
template<typename SubTy, typename SuperTy>
|
||||
SubtypingResult isContravariantWith(SubtypingEnvironment& env, const TryPair<const SubTy*, const SuperTy*>& pair, NotNull<Scope>);
|
||||
SubtypingResult isContravariantWith(SubtypingEnvironment& env, const TryPair<const SubTy*, const SuperTy*>& pair);
|
||||
|
||||
template<typename SubTy, typename SuperTy>
|
||||
SubtypingResult isInvariantWith(SubtypingEnvironment& env, const TryPair<const SubTy*, const SuperTy*>& pair, NotNull<Scope>);
|
||||
SubtypingResult isInvariantWith(SubtypingEnvironment& env, const TryPair<const SubTy*, const SuperTy*>& pair);
|
||||
|
||||
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, TypeId subTy, const UnionType* superUnion);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const UnionType* subUnion, TypeId superTy);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const IntersectionType* superIntersection);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const IntersectionType* subIntersection, TypeId superTy);
|
||||
|
||||
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 NegationType* subNegation, TypeId superTy);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeId subTy, const NegationType* superNegation);
|
||||
|
||||
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 PrimitiveType* subPrim, const PrimitiveType* superPrim);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const SingletonType* subSingleton, const PrimitiveType* superPrim);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const SingletonType* subSingleton, const SingletonType* superSingleton);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TableType* subTable, const TableType* superTable);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const MetatableType* superMt);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const TableType* superTable);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const ClassType* subClass, const ClassType* superClass);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const ClassType* subClass, TypeId superTy, const TableType* superTable);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const FunctionType* subFunction, const FunctionType* superFunction);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const PrimitiveType* subPrim, const TableType* superTable);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const SingletonType* subSingleton, const TableType* superTable);
|
||||
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TableIndexer& subIndexer, const TableIndexer& superIndexer);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const Property& subProperty, const Property& superProperty, const std::string& name);
|
||||
|
||||
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>);
|
||||
SubtypingEnvironment& env, const std::shared_ptr<const NormalizedType>& subNorm, const std::shared_ptr<const NormalizedType>& superNorm);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const NormalizedClassType& subClass, const NormalizedClassType& superClass);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const NormalizedClassType& subClass, const TypeIds& superTables);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const NormalizedStringType& subString, const NormalizedStringType& superString);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const NormalizedStringType& subString, const TypeIds& superTables);
|
||||
SubtypingResult isCovariantWith(
|
||||
SubtypingEnvironment& env, const NormalizedFunctionType& subFunction, const NormalizedFunctionType& superFunction);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeIds& subTypes, const TypeIds& superTypes);
|
||||
|
||||
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
|
||||
);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const VariadicTypePack* subVariadic, const VariadicTypePack* superVariadic);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeFunctionInstanceType* subFamilyInstance, const TypeId superTy);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeId subTy, const TypeFunctionInstanceType* superFamilyInstance);
|
||||
|
||||
bool bindGeneric(SubtypingEnvironment& env, TypeId subTp, TypeId superTp);
|
||||
bool bindGeneric(SubtypingEnvironment& env, TypePackId subTp, TypePackId superTp);
|
||||
|
@ -304,7 +228,7 @@ private:
|
|||
template<typename T, typename Container>
|
||||
TypeId makeAggregateType(const Container& container, TypeId orElse);
|
||||
|
||||
std::pair<TypeId, ErrorVec> handleTypeFunctionReductionResult(const TypeFunctionInstanceType* functionInstance, NotNull<Scope> scope);
|
||||
std::pair<TypeId, ErrorVec> handleTypeFunctionReductionResult(const TypeFunctionInstanceType* familyInstance);
|
||||
|
||||
[[noreturn]] void unexpected(TypeId ty);
|
||||
[[noreturn]] void unexpected(TypePackId tp);
|
||||
|
|
|
@ -6,27 +6,15 @@
|
|||
#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
|
||||
);
|
||||
TypeId matchLiteralType(NotNull<DenseHashMap<const AstExpr*, TypeId>> astTypes, NotNull<DenseHashMap<const AstExpr*, TypeId>> astExpectedTypes,
|
||||
NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, NotNull<Unifier2> unifier, TypeId expectedType, TypeId exprType,
|
||||
const AstExpr* expr, std::vector<TypeId>& toBlock);
|
||||
} // namespace Luau
|
||||
|
|
|
@ -44,8 +44,6 @@ struct ToStringOptions
|
|||
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.
|
||||
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);
|
||||
size_t compositeTypesSingleLineLimit = 5; // The number of type elements permitted on a single line when printing type unions/intersections
|
||||
|
|
|
@ -65,10 +65,11 @@ T* getMutable(PendingTypePack* pending)
|
|||
// Log of what TypeIds we are rebinding, to be committed later.
|
||||
struct TxnLog
|
||||
{
|
||||
explicit TxnLog()
|
||||
explicit TxnLog(bool useScopes = false)
|
||||
: typeVarChanges(nullptr)
|
||||
, typePackChanges(nullptr)
|
||||
, ownedSeen()
|
||||
, useScopes(useScopes)
|
||||
, sharedSeen(&ownedSeen)
|
||||
{
|
||||
}
|
||||
|
@ -192,6 +193,16 @@ struct TxnLog
|
|||
// The pointer returned lives until `commit` or `clear` is called.
|
||||
PendingTypePack* changeLevel(TypePackId tp, TypeLevel newLevel);
|
||||
|
||||
// Queues the replacement of a type's scope with the provided scope.
|
||||
//
|
||||
// The pointer returned lives until `commit` or `clear` is called.
|
||||
PendingType* changeScope(TypeId ty, NotNull<Scope> scope);
|
||||
|
||||
// Queues the replacement of a type pack's scope with the provided scope.
|
||||
//
|
||||
// The pointer returned lives until `commit` or `clear` is called.
|
||||
PendingTypePack* changeScope(TypePackId tp, NotNull<Scope> scope);
|
||||
|
||||
// Queues a replacement of a table type with another table type with a new
|
||||
// indexer.
|
||||
//
|
||||
|
|
|
@ -5,11 +5,10 @@
|
|||
|
||||
#include "Luau/Ast.h"
|
||||
#include "Luau/Common.h"
|
||||
#include "Luau/Refinement.h"
|
||||
#include "Luau/DenseHash.h"
|
||||
#include "Luau/NotNull.h"
|
||||
#include "Luau/Polarity.h"
|
||||
#include "Luau/Predicate.h"
|
||||
#include "Luau/Refinement.h"
|
||||
#include "Luau/Unifiable.h"
|
||||
#include "Luau/Variant.h"
|
||||
#include "Luau/VecDeque.h"
|
||||
|
@ -20,6 +19,7 @@
|
|||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
LUAU_FASTINT(LuauTableTypeMaximumStringifierLength)
|
||||
|
@ -31,12 +31,9 @@ namespace Luau
|
|||
struct TypeArena;
|
||||
struct Scope;
|
||||
using ScopePtr = std::shared_ptr<Scope>;
|
||||
struct Module;
|
||||
|
||||
struct TypeFunction;
|
||||
struct Constraint;
|
||||
struct Subtyping;
|
||||
struct TypeChecker2;
|
||||
|
||||
/**
|
||||
* There are three kinds of type variables:
|
||||
|
@ -69,11 +66,11 @@ using Name = std::string;
|
|||
// A free type is one whose exact shape has yet to be fully determined.
|
||||
struct FreeType
|
||||
{
|
||||
// New constructors
|
||||
explicit FreeType(TypeLevel level, TypeId lowerBound, TypeId upperBound);
|
||||
// This one got promoted to explicit
|
||||
explicit FreeType(Scope* scope, TypeId lowerBound, TypeId upperBound, Polarity polarity = Polarity::Unknown);
|
||||
explicit FreeType(Scope* scope, TypeLevel level, TypeId lowerBound, TypeId upperBound);
|
||||
explicit FreeType(TypeLevel level);
|
||||
explicit FreeType(Scope* scope);
|
||||
FreeType(Scope* scope, TypeLevel level);
|
||||
|
||||
FreeType(Scope* scope, TypeId lowerBound, TypeId upperBound);
|
||||
|
||||
int index;
|
||||
TypeLevel level;
|
||||
|
@ -87,8 +84,6 @@ struct FreeType
|
|||
// Only used under local type inference
|
||||
TypeId lowerBound = nullptr;
|
||||
TypeId upperBound = nullptr;
|
||||
|
||||
Polarity polarity = Polarity::Unknown;
|
||||
};
|
||||
|
||||
struct GenericType
|
||||
|
@ -97,8 +92,8 @@ struct GenericType
|
|||
GenericType();
|
||||
|
||||
explicit GenericType(TypeLevel level);
|
||||
explicit GenericType(const Name& name, Polarity polarity = Polarity::Unknown);
|
||||
explicit GenericType(Scope* scope, Polarity polarity = Polarity::Unknown);
|
||||
explicit GenericType(const Name& name);
|
||||
explicit GenericType(Scope* scope);
|
||||
|
||||
GenericType(TypeLevel level, const Name& name);
|
||||
GenericType(Scope* scope, const Name& name);
|
||||
|
@ -108,8 +103,6 @@ struct GenericType
|
|||
Scope* scope = nullptr;
|
||||
Name name;
|
||||
bool explicitName = false;
|
||||
|
||||
Polarity polarity = Polarity::Unknown;
|
||||
};
|
||||
|
||||
// When an equality constraint is found, it is then "bound" to that type,
|
||||
|
@ -135,14 +128,14 @@ struct BlockedType
|
|||
BlockedType();
|
||||
int index;
|
||||
|
||||
const Constraint* getOwner() const;
|
||||
void setOwner(const Constraint* newOwner);
|
||||
void replaceOwner(const Constraint* newOwner);
|
||||
Constraint* getOwner() const;
|
||||
void setOwner(Constraint* newOwner);
|
||||
void replaceOwner(Constraint* newOwner);
|
||||
|
||||
private:
|
||||
// The constraint that is intended to unblock this type. Other constraints
|
||||
// should block on this constraint if present.
|
||||
const Constraint* owner = nullptr;
|
||||
Constraint* owner = nullptr;
|
||||
};
|
||||
|
||||
struct PrimitiveType
|
||||
|
@ -283,15 +276,20 @@ struct WithPredicate
|
|||
}
|
||||
};
|
||||
|
||||
using MagicFunction = std::function<std::optional<WithPredicate<TypePackId>>(
|
||||
struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>)>;
|
||||
|
||||
struct MagicFunctionCallContext
|
||||
{
|
||||
NotNull<struct ConstraintSolver> solver;
|
||||
NotNull<const Constraint> constraint;
|
||||
NotNull<const AstExprCall> callSite;
|
||||
const class AstExprCall* callSite;
|
||||
TypePackId arguments;
|
||||
TypePackId result;
|
||||
};
|
||||
|
||||
using DcrMagicFunction = std::function<bool(MagicFunctionCallContext)>;
|
||||
|
||||
struct MagicRefinementContext
|
||||
{
|
||||
NotNull<Scope> scope;
|
||||
|
@ -299,38 +297,7 @@ struct MagicRefinementContext
|
|||
std::vector<std::optional<TypeId>> discriminantTypes;
|
||||
};
|
||||
|
||||
struct MagicFunctionTypeCheckContext
|
||||
{
|
||||
NotNull<TypeChecker2> typechecker;
|
||||
NotNull<BuiltinTypes> builtinTypes;
|
||||
const class AstExprCall* callSite;
|
||||
TypePackId arguments;
|
||||
NotNull<Scope> checkScope;
|
||||
};
|
||||
|
||||
struct MagicFunction
|
||||
{
|
||||
virtual std::optional<WithPredicate<TypePackId>>
|
||||
handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) = 0;
|
||||
|
||||
// Callback to allow custom typechecking of builtin function calls whose argument types
|
||||
// will only be resolved after constraint solving. For example, the arguments to string.format
|
||||
// have types that can only be decided after parsing the format string and unifying
|
||||
// with the passed in values, but the correctness of the call can only be decided after
|
||||
// all the types have been finalized.
|
||||
virtual bool infer(const MagicFunctionCallContext&) = 0;
|
||||
virtual void refine(const MagicRefinementContext&) {}
|
||||
|
||||
// If a magic function needs to do its own special typechecking, do it here.
|
||||
// Returns true if magic typechecking was performed. Return false if the
|
||||
// default typechecking logic should run.
|
||||
virtual bool typeCheck(const MagicFunctionTypeCheckContext&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual ~MagicFunction() {}
|
||||
};
|
||||
using DcrMagicRefinement = void (*)(const MagicRefinementContext&);
|
||||
|
||||
struct FunctionType
|
||||
{
|
||||
|
@ -338,34 +305,19 @@ struct FunctionType
|
|||
FunctionType(TypePackId argTypes, TypePackId retTypes, std::optional<FunctionDefinition> defn = {}, bool hasSelf = false);
|
||||
|
||||
// Global polymorphic function
|
||||
FunctionType(
|
||||
std::vector<TypeId> generics,
|
||||
std::vector<TypePackId> genericPacks,
|
||||
TypePackId argTypes,
|
||||
TypePackId retTypes,
|
||||
std::optional<FunctionDefinition> defn = {},
|
||||
bool hasSelf = false
|
||||
);
|
||||
FunctionType(std::vector<TypeId> generics, std::vector<TypePackId> genericPacks, TypePackId argTypes, TypePackId retTypes,
|
||||
std::optional<FunctionDefinition> defn = {}, bool hasSelf = false);
|
||||
|
||||
// Local monomorphic function
|
||||
FunctionType(TypeLevel level, TypePackId argTypes, TypePackId retTypes, std::optional<FunctionDefinition> defn = {}, bool hasSelf = false);
|
||||
FunctionType(
|
||||
TypeLevel level,
|
||||
TypePackId argTypes,
|
||||
TypePackId retTypes,
|
||||
std::optional<FunctionDefinition> defn = {},
|
||||
bool hasSelf = false
|
||||
);
|
||||
TypeLevel level, Scope* scope, TypePackId argTypes, TypePackId retTypes, std::optional<FunctionDefinition> defn = {}, bool hasSelf = false);
|
||||
|
||||
// Local polymorphic function
|
||||
FunctionType(
|
||||
TypeLevel level,
|
||||
std::vector<TypeId> generics,
|
||||
std::vector<TypePackId> genericPacks,
|
||||
TypePackId argTypes,
|
||||
TypePackId retTypes,
|
||||
std::optional<FunctionDefinition> defn = {},
|
||||
bool hasSelf = false
|
||||
);
|
||||
FunctionType(TypeLevel level, std::vector<TypeId> generics, std::vector<TypePackId> genericPacks, TypePackId argTypes, TypePackId retTypes,
|
||||
std::optional<FunctionDefinition> defn = {}, bool hasSelf = false);
|
||||
FunctionType(TypeLevel level, Scope* scope, std::vector<TypeId> generics, std::vector<TypePackId> genericPacks, TypePackId argTypes,
|
||||
TypePackId retTypes, std::optional<FunctionDefinition> defn = {}, bool hasSelf = false);
|
||||
|
||||
std::optional<FunctionDefinition> definition;
|
||||
/// These should all be generic
|
||||
|
@ -374,16 +326,17 @@ struct FunctionType
|
|||
std::vector<std::optional<FunctionArgument>> argNames;
|
||||
Tags tags;
|
||||
TypeLevel level;
|
||||
Scope* scope = nullptr;
|
||||
TypePackId argTypes;
|
||||
TypePackId retTypes;
|
||||
std::shared_ptr<MagicFunction> magic = nullptr;
|
||||
|
||||
MagicFunction magicFunction = nullptr;
|
||||
DcrMagicFunction dcrMagicFunction = nullptr;
|
||||
DcrMagicRefinement dcrMagicRefinement = nullptr;
|
||||
bool hasSelf;
|
||||
// `hasNoFreeOrGenericTypes` should be true if and only if the type does not have any free or generic types present inside it.
|
||||
// this flag is used as an optimization to exit early from procedures that manipulate free or generic types.
|
||||
bool hasNoFreeOrGenericTypes = false;
|
||||
bool isCheckedFunction = false;
|
||||
bool isDeprecatedFunction = false;
|
||||
};
|
||||
|
||||
enum class TableState
|
||||
|
@ -445,24 +398,16 @@ struct Property
|
|||
// DEPRECATED
|
||||
// TODO: Kill all constructors in favor of `Property::rw(TypeId read, TypeId write)` and friends.
|
||||
Property();
|
||||
Property(
|
||||
TypeId readTy,
|
||||
bool deprecated = false,
|
||||
const std::string& deprecatedSuggestion = "",
|
||||
std::optional<Location> location = std::nullopt,
|
||||
const Tags& tags = {},
|
||||
const std::optional<std::string>& documentationSymbol = std::nullopt,
|
||||
std::optional<Location> typeLocation = std::nullopt
|
||||
);
|
||||
Property(TypeId readTy, bool deprecated = false, const std::string& deprecatedSuggestion = "", std::optional<Location> location = std::nullopt,
|
||||
const Tags& tags = {}, const std::optional<std::string>& documentationSymbol = std::nullopt,
|
||||
std::optional<Location> typeLocation = std::nullopt);
|
||||
|
||||
// DEPRECATED: Should only be called in non-RWP! We assert that the `readTy` is not nullopt.
|
||||
// TODO: Kill once we don't have non-RWP.
|
||||
TypeId type() const;
|
||||
void setType(TypeId ty);
|
||||
|
||||
// If this property has a present `writeTy`, set it equal to the `readTy`.
|
||||
// This is to ensure that if we normalize a property that has divergent
|
||||
// read and write types, we make them converge (for now).
|
||||
// Sets the write type of this property to the read type.
|
||||
void makeShared();
|
||||
|
||||
bool isShared() const;
|
||||
|
@ -507,6 +452,9 @@ struct TableType
|
|||
std::optional<TypeId> boundTo;
|
||||
Tags tags;
|
||||
|
||||
// Methods of this table that have an untyped self will use the same shared self type.
|
||||
std::optional<TypeId> selfTy;
|
||||
|
||||
// We track the number of as-yet-unadded properties to unsealed tables.
|
||||
// Some constraints will use this information to decide whether or not they
|
||||
// are able to dispatch.
|
||||
|
@ -532,15 +480,15 @@ struct ClassUserData
|
|||
virtual ~ClassUserData() {}
|
||||
};
|
||||
|
||||
/** The type of an external userdata exposed to Luau.
|
||||
/** The type of a class.
|
||||
*
|
||||
* Extern types behave like tables in many ways, but there are some important differences:
|
||||
* Classes behave like tables in many ways, but there are some important differences:
|
||||
*
|
||||
* The properties of a class are always exactly known.
|
||||
* Extern types optionally have a parent type.
|
||||
* Two different extern types that share the same properties are nevertheless distinct and mutually incompatible.
|
||||
* Classes optionally have a parent class.
|
||||
* Two different classes that share the same properties are nevertheless distinct and mutually incompatible.
|
||||
*/
|
||||
struct ExternType
|
||||
struct ClassType
|
||||
{
|
||||
using Props = TableType::Props;
|
||||
|
||||
|
@ -551,19 +499,10 @@ struct ExternType
|
|||
Tags tags;
|
||||
std::shared_ptr<ClassUserData> userData;
|
||||
ModuleName definitionModuleName;
|
||||
std::optional<Location> definitionLocation;
|
||||
std::optional<TableIndexer> indexer;
|
||||
|
||||
ExternType(
|
||||
Name name,
|
||||
Props props,
|
||||
std::optional<TypeId> parent,
|
||||
std::optional<TypeId> metatable,
|
||||
Tags tags,
|
||||
std::shared_ptr<ClassUserData> userData,
|
||||
ModuleName definitionModuleName,
|
||||
std::optional<Location> definitionLocation
|
||||
)
|
||||
ClassType(Name name, Props props, std::optional<TypeId> parent, std::optional<TypeId> metatable, Tags tags,
|
||||
std::shared_ptr<ClassUserData> userData, ModuleName definitionModuleName)
|
||||
: name(name)
|
||||
, props(props)
|
||||
, parent(parent)
|
||||
|
@ -571,21 +510,11 @@ struct ExternType
|
|||
, tags(tags)
|
||||
, userData(userData)
|
||||
, definitionModuleName(definitionModuleName)
|
||||
, definitionLocation(definitionLocation)
|
||||
{
|
||||
}
|
||||
|
||||
ExternType(
|
||||
Name name,
|
||||
Props props,
|
||||
std::optional<TypeId> parent,
|
||||
std::optional<TypeId> metatable,
|
||||
Tags tags,
|
||||
std::shared_ptr<ClassUserData> userData,
|
||||
ModuleName definitionModuleName,
|
||||
std::optional<Location> definitionLocation,
|
||||
std::optional<TableIndexer> indexer
|
||||
)
|
||||
ClassType(Name name, Props props, std::optional<TypeId> parent, std::optional<TypeId> metatable, Tags tags,
|
||||
std::shared_ptr<ClassUserData> userData, ModuleName definitionModuleName, std::optional<TableIndexer> indexer)
|
||||
: name(name)
|
||||
, props(props)
|
||||
, parent(parent)
|
||||
|
@ -593,24 +522,11 @@ struct ExternType
|
|||
, tags(tags)
|
||||
, userData(userData)
|
||||
, definitionModuleName(definitionModuleName)
|
||||
, definitionLocation(definitionLocation)
|
||||
, indexer(indexer)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Data required to initialize a user-defined function and its environment
|
||||
struct UserDefinedFunctionData
|
||||
{
|
||||
// Store a weak module reference to ensure the lifetime requirements are preserved
|
||||
std::weak_ptr<Module> owner;
|
||||
|
||||
// References to AST elements are owned by the Module allocator which also stores this type
|
||||
AstStatTypeFunction* definition = nullptr;
|
||||
|
||||
DenseHashMap<Name, std::pair<AstStatTypeFunction*, size_t>> environment{""};
|
||||
};
|
||||
|
||||
/**
|
||||
* An instance of a type function that has not yet been reduced to a more concrete
|
||||
* type. The constraint solver receives a constraint to reduce each
|
||||
|
@ -620,45 +536,27 @@ struct UserDefinedFunctionData
|
|||
*/
|
||||
struct TypeFunctionInstanceType
|
||||
{
|
||||
NotNull<const TypeFunction> function;
|
||||
NotNull<const TypeFunction> family;
|
||||
|
||||
std::vector<TypeId> typeArguments;
|
||||
std::vector<TypePackId> packArguments;
|
||||
|
||||
std::optional<AstName> userFuncName; // Name of the user-defined type function; only available for UDTFs
|
||||
UserDefinedFunctionData userFuncData;
|
||||
|
||||
TypeFunctionInstanceType(
|
||||
NotNull<const TypeFunction> function,
|
||||
std::vector<TypeId> typeArguments,
|
||||
std::vector<TypePackId> packArguments,
|
||||
std::optional<AstName> userFuncName,
|
||||
UserDefinedFunctionData userFuncData
|
||||
)
|
||||
: function(function)
|
||||
TypeFunctionInstanceType(NotNull<const TypeFunction> family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments)
|
||||
: family(family)
|
||||
, typeArguments(typeArguments)
|
||||
, packArguments(packArguments)
|
||||
, userFuncName(userFuncName)
|
||||
, userFuncData(userFuncData)
|
||||
{
|
||||
}
|
||||
|
||||
TypeFunctionInstanceType(const TypeFunction& function, std::vector<TypeId> typeArguments)
|
||||
: function{&function}
|
||||
TypeFunctionInstanceType(const TypeFunction& family, std::vector<TypeId> typeArguments)
|
||||
: family{&family}
|
||||
, typeArguments(typeArguments)
|
||||
, packArguments{}
|
||||
{
|
||||
}
|
||||
|
||||
TypeFunctionInstanceType(const TypeFunction& function, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments)
|
||||
: function{&function}
|
||||
, typeArguments(typeArguments)
|
||||
, packArguments(packArguments)
|
||||
{
|
||||
}
|
||||
|
||||
TypeFunctionInstanceType(NotNull<const TypeFunction> function, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments)
|
||||
: function{function}
|
||||
TypeFunctionInstanceType(const TypeFunction& family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments)
|
||||
: family{&family}
|
||||
, typeArguments(typeArguments)
|
||||
, packArguments(packArguments)
|
||||
{
|
||||
|
@ -690,11 +588,6 @@ struct AnyType
|
|||
{
|
||||
};
|
||||
|
||||
// A special, trivial type for the refinement system that is always eliminated from intersections.
|
||||
struct NoRefineType
|
||||
{
|
||||
};
|
||||
|
||||
// `T | U`
|
||||
struct UnionType
|
||||
{
|
||||
|
@ -762,29 +655,11 @@ struct NegationType
|
|||
TypeId ty;
|
||||
};
|
||||
|
||||
using ErrorType = Unifiable::Error<TypeId>;
|
||||
using ErrorType = Unifiable::Error;
|
||||
|
||||
using TypeVariant = Unifiable::Variant<
|
||||
TypeId,
|
||||
FreeType,
|
||||
GenericType,
|
||||
PrimitiveType,
|
||||
SingletonType,
|
||||
BlockedType,
|
||||
PendingExpansionType,
|
||||
FunctionType,
|
||||
TableType,
|
||||
MetatableType,
|
||||
ExternType,
|
||||
AnyType,
|
||||
UnionType,
|
||||
IntersectionType,
|
||||
LazyType,
|
||||
UnknownType,
|
||||
NeverType,
|
||||
NegationType,
|
||||
NoRefineType,
|
||||
TypeFunctionInstanceType>;
|
||||
using TypeVariant =
|
||||
Unifiable::Variant<TypeId, FreeType, GenericType, PrimitiveType, SingletonType, BlockedType, PendingExpansionType, FunctionType, TableType,
|
||||
MetatableType, ClassType, AnyType, UnionType, IntersectionType, LazyType, UnknownType, NeverType, NegationType, TypeFunctionInstanceType>;
|
||||
|
||||
struct Type final
|
||||
{
|
||||
|
@ -829,13 +704,6 @@ struct Type final
|
|||
Type& operator=(const TypeVariant& rhs);
|
||||
Type& operator=(TypeVariant&& rhs);
|
||||
|
||||
Type(Type&&) = default;
|
||||
Type& operator=(Type&&) = default;
|
||||
|
||||
Type clone() const;
|
||||
|
||||
private:
|
||||
Type(const Type&) = default;
|
||||
Type& operator=(const Type& rhs);
|
||||
};
|
||||
|
||||
|
@ -868,9 +736,6 @@ struct TypeFun
|
|||
*/
|
||||
TypeId type;
|
||||
|
||||
// The location of where this TypeFun was defined, if available
|
||||
std::optional<Location> definitionLocation;
|
||||
|
||||
TypeFun() = default;
|
||||
|
||||
explicit TypeFun(TypeId ty)
|
||||
|
@ -878,23 +743,16 @@ struct TypeFun
|
|||
{
|
||||
}
|
||||
|
||||
TypeFun(std::vector<GenericTypeDefinition> typeParams, TypeId type, std::optional<Location> definitionLocation = std::nullopt)
|
||||
TypeFun(std::vector<GenericTypeDefinition> typeParams, TypeId type)
|
||||
: typeParams(std::move(typeParams))
|
||||
, type(type)
|
||||
, definitionLocation(definitionLocation)
|
||||
{
|
||||
}
|
||||
|
||||
TypeFun(
|
||||
std::vector<GenericTypeDefinition> typeParams,
|
||||
std::vector<GenericTypePackDefinition> typePackParams,
|
||||
TypeId type,
|
||||
std::optional<Location> definitionLocation = std::nullopt
|
||||
)
|
||||
TypeFun(std::vector<GenericTypeDefinition> typeParams, std::vector<GenericTypePackDefinition> typePackParams, TypeId type)
|
||||
: typeParams(std::move(typeParams))
|
||||
, typePackParams(std::move(typePackParams))
|
||||
, type(type)
|
||||
, definitionLocation(definitionLocation)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -986,7 +844,7 @@ public:
|
|||
const TypeId threadType;
|
||||
const TypeId bufferType;
|
||||
const TypeId functionType;
|
||||
const TypeId externType;
|
||||
const TypeId classType;
|
||||
const TypeId tableType;
|
||||
const TypeId emptyTableType;
|
||||
const TypeId trueType;
|
||||
|
@ -995,10 +853,8 @@ public:
|
|||
const TypeId unknownType;
|
||||
const TypeId neverType;
|
||||
const TypeId errorType;
|
||||
const TypeId noRefineType;
|
||||
const TypeId falsyType;
|
||||
const TypeId truthyType;
|
||||
const TypeId notNilType;
|
||||
|
||||
const TypeId optionalNumberType;
|
||||
const TypeId optionalStringType;
|
||||
|
@ -1019,10 +875,10 @@ TypeLevel* getMutableLevel(TypeId ty);
|
|||
|
||||
std::optional<TypeLevel> getLevel(TypePackId tp);
|
||||
|
||||
const Property* lookupExternTypeProp(const ExternType* cls, const Name& name);
|
||||
const Property* lookupClassProp(const ClassType* cls, const Name& name);
|
||||
|
||||
// Whether `cls` is a subclass of `parent`
|
||||
bool isSubclass(const ExternType* cls, const ExternType* parent);
|
||||
bool isSubclass(const ClassType* cls, const ClassType* parent);
|
||||
|
||||
Type* asMutable(TypeId ty);
|
||||
|
||||
|
@ -1199,15 +1055,11 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
TypeId freshType(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, Scope* scope, Polarity polarity = Polarity::Unknown);
|
||||
TypeId freshType(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, Scope* scope);
|
||||
|
||||
using TypeIdPredicate = std::function<std::optional<TypeId>(TypeId)>;
|
||||
std::vector<TypeId> filterMap(TypeId type, TypeIdPredicate predicate);
|
||||
|
||||
// A tag to mark a type which doesn't derive directly from the root type as overriding the return of `typeof`.
|
||||
// Any classes which derive from this type will have typeof return this type.
|
||||
static constexpr char kTypeofRootTag[] = "typeofRoot";
|
||||
|
||||
void attachTag(TypeId ty, const std::string& tagName);
|
||||
void attachTag(Property& prop, const std::string& tagName);
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// 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"
|
||||
|
@ -33,11 +32,11 @@ struct TypeArena
|
|||
|
||||
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);
|
||||
TypeId freshType(TypeLevel level);
|
||||
TypeId freshType(Scope* scope);
|
||||
TypeId freshType(Scope* scope, TypeLevel level);
|
||||
|
||||
TypePackId freshTypePack(Scope* scope, Polarity polarity = Polarity::Unknown);
|
||||
TypePackId freshTypePack(Scope* scope);
|
||||
|
||||
TypePackId addTypePack(std::initializer_list<TypeId> types);
|
||||
TypePackId addTypePack(std::vector<TypeId> types, std::optional<TypePackId> tail = {});
|
||||
|
@ -50,10 +49,10 @@ struct TypeArena
|
|||
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 = {});
|
||||
TypeId addTypeFunction(const TypeFunction& family, std::initializer_list<TypeId> types);
|
||||
TypeId addTypeFunction(const TypeFunction& family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments = {});
|
||||
TypePackId addTypePackFunction(const TypePackFunction& family, std::initializer_list<TypeId> types);
|
||||
TypePackId addTypePackFunction(const TypePackFunction& family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments = {});
|
||||
};
|
||||
|
||||
void freeze(TypeArena& arena);
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace Luau
|
|||
struct TypeRehydrationOptions
|
||||
{
|
||||
std::unordered_set<std::string> bannedNames;
|
||||
bool expandExternTypeProps = false;
|
||||
bool expandClassProps = false;
|
||||
};
|
||||
|
||||
void attachTypeData(SourceModule& source, Module& result);
|
||||
|
|
|
@ -2,18 +2,7 @@
|
|||
|
||||
#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
|
||||
{
|
||||
|
@ -24,220 +13,8 @@ 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);
|
||||
};
|
||||
void check(NotNull<BuiltinTypes> builtinTypes, NotNull<UnifierSharedState> sharedState, NotNull<TypeCheckLimits> limits, DcrLogger* logger,
|
||||
const SourceModule& sourceModule, Module* module);
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -1,71 +1,29 @@
|
|||
// 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/ConstraintSolver.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;
|
||||
|
||||
|
@ -74,26 +32,24 @@ struct TypeFunctionContext
|
|||
// 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)
|
||||
: arena(cs->arena)
|
||||
, builtins(cs->builtinTypes)
|
||||
, scope(scope)
|
||||
, normalizer(cs->normalizer)
|
||||
, ice(NotNull{&cs->iceReporter})
|
||||
, limits(NotNull{&cs->limits})
|
||||
, solver(cs.get())
|
||||
, constraint(constraint.get())
|
||||
{
|
||||
}
|
||||
|
||||
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
|
||||
)
|
||||
TypeFunctionContext(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtins, NotNull<Scope> scope, NotNull<Normalizer> normalizer,
|
||||
NotNull<InternalErrorReporter> ice, NotNull<TypeCheckLimits> limits)
|
||||
: arena(arena)
|
||||
, builtins(builtins)
|
||||
, scope(scope)
|
||||
, simplifier(simplifier)
|
||||
, normalizer(normalizer)
|
||||
, typeFunctionRuntime(typeFunctionRuntime)
|
||||
, ice(ice)
|
||||
, limits(limits)
|
||||
, solver(nullptr)
|
||||
|
@ -101,17 +57,7 @@ struct TypeFunctionContext
|
|||
{
|
||||
}
|
||||
|
||||
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,
|
||||
NotNull<Constraint> pushConstraint(ConstraintV&& c);
|
||||
};
|
||||
|
||||
/// Represents a reduction result, which may have successfully reduced the type,
|
||||
|
@ -120,25 +66,19 @@ enum class Reduction
|
|||
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;
|
||||
/// Whether the result is uninhabited: whether we know, unambiguously and
|
||||
/// permanently, whether this type function reduction results in an
|
||||
/// uninhabitable type. This will trigger an error to be reported.
|
||||
bool uninhabited;
|
||||
/// 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>
|
||||
|
@ -155,9 +95,6 @@ struct TypeFunction
|
|||
|
||||
/// 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
|
||||
|
@ -170,20 +107,15 @@ struct TypePackFunction
|
|||
|
||||
/// 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};
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -218,8 +150,6 @@ struct BuiltinTypeFunctions
|
|||
{
|
||||
BuiltinTypeFunctions();
|
||||
|
||||
TypeFunction userFunc;
|
||||
|
||||
TypeFunction notFunc;
|
||||
TypeFunction lenFunc;
|
||||
TypeFunction unmFunc;
|
||||
|
@ -251,11 +181,6 @@ struct BuiltinTypeFunctions
|
|||
TypeFunction indexFunc;
|
||||
TypeFunction rawgetFunc;
|
||||
|
||||
TypeFunction setmetatableFunc;
|
||||
TypeFunction getmetatableFunc;
|
||||
|
||||
TypeFunction weakoptionalFunc;
|
||||
|
||||
void addToScope(NotNull<TypeArena> arena, NotNull<Scope> scope) const;
|
||||
};
|
||||
|
||||
|
|
|
@ -29,13 +29,13 @@ struct TypeFunctionReductionGuessResult
|
|||
struct TypeFunctionInferenceResult
|
||||
{
|
||||
std::vector<TypeId> operandInference;
|
||||
TypeId functionResultInference;
|
||||
TypeId familyResultInference;
|
||||
};
|
||||
|
||||
struct TypeFunctionReductionGuesser
|
||||
{
|
||||
// Tracks our hypothesis about what a type function reduces to
|
||||
DenseHashMap<TypeId, TypeId> functionReducesTo{nullptr};
|
||||
DenseHashMap<TypeId, TypeId> familyReducesTo{nullptr};
|
||||
// Tracks our constraints on type function operands
|
||||
DenseHashMap<TypeId, TypeId> substitutable{nullptr};
|
||||
// List of instances to try progress
|
||||
|
@ -57,14 +57,14 @@ 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 isNumericBinopFamily(const TypeFunctionInstanceType& instance);
|
||||
bool isComparisonFamily(const TypeFunctionInstanceType& instance);
|
||||
bool isOrAndFamily(const TypeFunctionInstanceType& instance);
|
||||
bool isNotFamily(const TypeFunctionInstanceType& instance);
|
||||
bool isLenFamily(const TypeFunctionInstanceType& instance);
|
||||
bool isUnaryMinus(const TypeFunctionInstanceType& instance);
|
||||
|
||||
// Operand is assignable if it looks like a cyclic type function instance, or a generic type
|
||||
// Operand is assignable if it looks like a cyclic family instance, or a generic type
|
||||
bool operandIsAssignable(TypeId ty);
|
||||
std::optional<TypeId> tryAssignOperandType(TypeId ty);
|
||||
|
||||
|
@ -75,11 +75,11 @@ private:
|
|||
|
||||
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);
|
||||
TypeFunctionInferenceResult inferNumericBinopFamily(const TypeFunctionInstanceType* instance);
|
||||
TypeFunctionInferenceResult inferComparisonFamily(const TypeFunctionInstanceType* instance);
|
||||
TypeFunctionInferenceResult inferOrAndFamily(const TypeFunctionInstanceType* instance);
|
||||
TypeFunctionInferenceResult inferNotFamily(const TypeFunctionInstanceType* instance);
|
||||
TypeFunctionInferenceResult inferLenFamily(const TypeFunctionInstanceType* instance);
|
||||
TypeFunctionInferenceResult inferUnaryMinusFamily(const TypeFunctionInstanceType* instance);
|
||||
};
|
||||
} // namespace Luau
|
||||
|
|
|
@ -1,296 +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/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
|
|
@ -1,44 +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/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
|
|
@ -29,7 +29,7 @@ struct SingletonType;
|
|||
struct FunctionType;
|
||||
struct TableType;
|
||||
struct MetatableType;
|
||||
struct ExternType;
|
||||
struct ClassType;
|
||||
struct AnyType;
|
||||
struct UnionType;
|
||||
struct IntersectionType;
|
||||
|
|
|
@ -62,11 +62,7 @@ struct HashBoolNamePair
|
|||
struct TypeChecker
|
||||
{
|
||||
explicit TypeChecker(
|
||||
const ScopePtr& globalScope,
|
||||
ModuleResolver* resolver,
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
InternalErrorReporter* iceHandler
|
||||
);
|
||||
const ScopePtr& globalScope, ModuleResolver* resolver, NotNull<BuiltinTypes> builtinTypes, InternalErrorReporter* iceHandler);
|
||||
TypeChecker(const TypeChecker&) = delete;
|
||||
TypeChecker& operator=(const TypeChecker&) = delete;
|
||||
|
||||
|
@ -89,23 +85,18 @@ struct TypeChecker
|
|||
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 AstStatDeclareClass& declaredClass);
|
||||
ControlFlow check(const ScopePtr& scope, const AstStatDeclareFunction& declaredFunction);
|
||||
|
||||
void prototype(const ScopePtr& scope, const AstStatTypeAlias& typealias, int subLevel = 0);
|
||||
void prototype(const ScopePtr& scope, const AstStatDeclareExternType& declaredExternType);
|
||||
void prototype(const ScopePtr& scope, const AstStatDeclareClass& declaredClass);
|
||||
|
||||
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);
|
||||
|
||||
WithPredicate<TypeId> checkExpr(
|
||||
const ScopePtr& scope,
|
||||
const AstExpr& expr,
|
||||
std::optional<TypeId> expectedType = std::nullopt,
|
||||
bool forceSingleton = false
|
||||
);
|
||||
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);
|
||||
|
@ -116,32 +107,17 @@ struct TypeChecker
|
|||
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 = {}
|
||||
);
|
||||
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, ValueContext ctx);
|
||||
|
@ -154,79 +130,34 @@ struct TypeChecker
|
|||
TypeId checkLValueBinding(const ScopePtr& scope, const AstExprIndexExpr& expr, ValueContext ctx);
|
||||
|
||||
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
|
||||
);
|
||||
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,
|
||||
const AstExpr& funName,
|
||||
Unifier& state,
|
||||
TypePackId paramPack,
|
||||
TypePackId argPack,
|
||||
const std::vector<Location>& argLocations
|
||||
);
|
||||
void checkArgumentList(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
|
||||
);
|
||||
const ScopePtr& scope, const AstExprCall& expr, TypeId selfType, TypeId actualFunctionType, TypeId functionType, TypePackId retPack);
|
||||
|
||||
std::vector<std::optional<TypeId>> getExpectedTypesForCall(const std::vector<TypeId>& overloads, size_t argumentCount, bool selfCall);
|
||||
|
||||
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
|
||||
);
|
||||
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 = {}
|
||||
);
|
||||
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);
|
||||
|
@ -244,13 +175,8 @@ struct TypeChecker
|
|||
*/
|
||||
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
|
||||
);
|
||||
bool unify(TypePackId subTy, TypePackId superTy, const ScopePtr& scope, const Location& location,
|
||||
CountMismatch::Context ctx = CountMismatch::Context::Arg);
|
||||
|
||||
/** Attempt to unify the types.
|
||||
* If this fails, and the subTy type can be instantiated, do so and try unification again.
|
||||
|
@ -387,23 +313,12 @@ private:
|
|||
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.
|
||||
GenericTypeDefinitions createGenericTypes(
|
||||
const ScopePtr& scope,
|
||||
std::optional<TypeLevel> levelOpt,
|
||||
const AstNode& node,
|
||||
const AstArray<AstGenericType*>& genericNames,
|
||||
const AstArray<AstGenericTypePack*>& genericPackNames,
|
||||
bool useCache = false
|
||||
);
|
||||
GenericTypeDefinitions createGenericTypes(const ScopePtr& scope, std::optional<TypeLevel> levelOpt, const AstNode& node,
|
||||
const AstArray<AstGenericType>& genericNames, const AstArray<AstGenericTypePack>& genericPackNames, bool useCache = false);
|
||||
|
||||
public:
|
||||
void resolve(const PredicateVec& predicates, const ScopePtr& scope, bool sense);
|
||||
|
@ -487,7 +402,7 @@ private:
|
|||
/**
|
||||
* A set of incorrect class definitions which is used to avoid a second-pass analysis.
|
||||
*/
|
||||
DenseHashSet<const AstStatDeclareExternType*> incorrectExternTypeDefinitions{nullptr};
|
||||
DenseHashSet<const AstStatDeclareClass*> incorrectClassDefinitions{nullptr};
|
||||
|
||||
std::vector<std::pair<TypeId, ScopePtr>> deferredQuantification;
|
||||
};
|
||||
|
|
|
@ -1,12 +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/Common.h"
|
||||
#include "Luau/NotNull.h"
|
||||
#include "Luau/Polarity.h"
|
||||
#include "Luau/TypeFwd.h"
|
||||
#include "Luau/Unifiable.h"
|
||||
#include "Luau/Variant.h"
|
||||
#include "Luau/TypeFwd.h"
|
||||
#include "Luau/NotNull.h"
|
||||
#include "Luau/Common.h"
|
||||
|
||||
#include <optional>
|
||||
#include <set>
|
||||
|
@ -27,14 +26,12 @@ struct TypeFunctionInstanceTypePack;
|
|||
struct FreeTypePack
|
||||
{
|
||||
explicit FreeTypePack(TypeLevel level);
|
||||
explicit FreeTypePack(Scope* scope, Polarity polarity = Polarity::Unknown);
|
||||
explicit FreeTypePack(Scope* scope);
|
||||
FreeTypePack(Scope* scope, TypeLevel level);
|
||||
|
||||
int index;
|
||||
TypeLevel level;
|
||||
Scope* scope = nullptr;
|
||||
|
||||
Polarity polarity = Polarity::Unknown;
|
||||
};
|
||||
|
||||
struct GenericTypePack
|
||||
|
@ -43,7 +40,7 @@ struct GenericTypePack
|
|||
GenericTypePack();
|
||||
explicit GenericTypePack(TypeLevel level);
|
||||
explicit GenericTypePack(const Name& name);
|
||||
explicit GenericTypePack(Scope* scope, Polarity polarity = Polarity::Unknown);
|
||||
explicit GenericTypePack(Scope* scope);
|
||||
GenericTypePack(TypeLevel level, const Name& name);
|
||||
GenericTypePack(Scope* scope, const Name& name);
|
||||
|
||||
|
@ -52,12 +49,10 @@ struct GenericTypePack
|
|||
Scope* scope = nullptr;
|
||||
Name name;
|
||||
bool explicitName = false;
|
||||
|
||||
Polarity polarity = Polarity::Unknown;
|
||||
};
|
||||
|
||||
using BoundTypePack = Unifiable::Bound<TypePackId>;
|
||||
using ErrorTypePack = Unifiable::Error<TypePackId>;
|
||||
using ErrorTypePack = Unifiable::Error;
|
||||
|
||||
using TypePackVariant =
|
||||
Unifiable::Variant<TypePackId, FreeTypePack, GenericTypePack, TypePack, VariadicTypePack, BlockedTypePack, TypeFunctionInstanceTypePack>;
|
||||
|
@ -97,7 +92,7 @@ struct BlockedTypePack
|
|||
*/
|
||||
struct TypeFunctionInstanceTypePack
|
||||
{
|
||||
NotNull<const TypePackFunction> function;
|
||||
NotNull<const TypePackFunction> family;
|
||||
|
||||
std::vector<TypeId> typeArguments;
|
||||
std::vector<TypePackId> packArguments;
|
||||
|
@ -105,9 +100,9 @@ struct TypeFunctionInstanceTypePack
|
|||
|
||||
struct TypePackVar
|
||||
{
|
||||
explicit TypePackVar(const TypePackVariant& tp);
|
||||
explicit TypePackVar(TypePackVariant&& tp);
|
||||
TypePackVar(TypePackVariant&& tp, bool persistent);
|
||||
explicit TypePackVar(const TypePackVariant& ty);
|
||||
explicit TypePackVar(TypePackVariant&& ty);
|
||||
TypePackVar(TypePackVariant&& ty, bool persistent);
|
||||
|
||||
bool operator==(const TypePackVar& rhs) const;
|
||||
|
||||
|
@ -174,7 +169,6 @@ struct TypePackIterator
|
|||
|
||||
private:
|
||||
TypePackId currentTypePack = nullptr;
|
||||
TypePackId tailCycleCheck = nullptr;
|
||||
const TypePack* tp = nullptr;
|
||||
size_t currentIndex = 0;
|
||||
|
||||
|
@ -185,8 +179,6 @@ TypePackIterator begin(TypePackId tp);
|
|||
TypePackIterator begin(TypePackId tp, const TxnLog* log);
|
||||
TypePackIterator end(TypePackId tp);
|
||||
|
||||
TypePackId getTail(TypePackId tp);
|
||||
|
||||
using SeenSet = std::set<std::pair<const void*, const void*>>;
|
||||
|
||||
bool areEqual(SeenSet& seen, const TypePackVar& lhs, const TypePackVar& rhs);
|
||||
|
|
|
@ -42,27 +42,15 @@ struct Property
|
|||
/// 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,
|
||||
|
@ -215,9 +203,6 @@ using Path = TypePath::Path;
|
|||
/// 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);
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ struct InConditionalContext
|
|||
TypeContext* typeContext;
|
||||
TypeContext oldValue;
|
||||
|
||||
explicit InConditionalContext(TypeContext* c)
|
||||
InConditionalContext(TypeContext* c)
|
||||
: typeContext(c)
|
||||
, oldValue(*c)
|
||||
{
|
||||
|
@ -56,35 +56,14 @@ struct InConditionalContext
|
|||
using ScopePtr = std::shared_ptr<struct Scope>;
|
||||
|
||||
std::optional<Property> findTableProperty(
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
ErrorVec& errors,
|
||||
TypeId ty,
|
||||
const std::string& name,
|
||||
Location location
|
||||
);
|
||||
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
|
||||
);
|
||||
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
|
||||
);
|
||||
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
|
||||
);
|
||||
NotNull<BuiltinTypes> builtinTypes, ErrorVec& errors, TypeId ty, const std::string& name, ValueContext context, Location location);
|
||||
|
||||
bool occursCheck(TypeId needle, TypeId haystack);
|
||||
|
||||
|
@ -94,12 +73,7 @@ std::pair<size_t, std::optional<size_t>> getParameterExtents(const TxnLog* log,
|
|||
// 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 = {}
|
||||
);
|
||||
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
|
||||
|
@ -248,47 +222,4 @@ std::optional<Ty> follow(std::optional<Ty> ty)
|
|||
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
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
#include "Luau/Variant.h"
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace Luau
|
||||
|
@ -95,29 +94,19 @@ struct Bound
|
|||
Id boundTo;
|
||||
};
|
||||
|
||||
template<typename Id>
|
||||
struct Error
|
||||
{
|
||||
// This constructor has to be public, since it's used in Type and TypePack,
|
||||
// but shouldn't be called directly. Please use errorRecoveryType() instead.
|
||||
explicit Error();
|
||||
|
||||
explicit Error(Id synthetic)
|
||||
: synthetic{synthetic}
|
||||
{
|
||||
}
|
||||
Error();
|
||||
|
||||
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 = Luau::Variant<Bound<Id>, Error<Id>, Value...>;
|
||||
using Variant = Luau::Variant<Bound<Id>, Error, Value...>;
|
||||
|
||||
} // namespace Luau::Unifiable
|
||||
|
|
|
@ -93,6 +93,10 @@ struct Unifier
|
|||
|
||||
Unifier(NotNull<Normalizer> normalizer, NotNull<Scope> scope, const Location& location, Variance variance, TxnLog* parentLog = nullptr);
|
||||
|
||||
// Configure the Unifier to test for scope subsumption via embedded Scope
|
||||
// pointers rather than TypeLevels.
|
||||
void enableNewSolver();
|
||||
|
||||
// Test whether the two type vars unify. Never commits the result.
|
||||
ErrorVec canUnify(TypeId subTy, TypeId superTy);
|
||||
ErrorVec canUnify(TypePackId subTy, TypePackId superTy, bool isFunctionCall = false);
|
||||
|
@ -102,21 +106,11 @@ struct Unifier
|
|||
* Populate the transaction log with the set of TypeIds that need to be reset to undo the unification attempt.
|
||||
*/
|
||||
void tryUnify(
|
||||
TypeId subTy,
|
||||
TypeId superTy,
|
||||
bool isFunctionCall = false,
|
||||
bool isIntersection = false,
|
||||
const LiteralProperties* aliasableMap = nullptr
|
||||
);
|
||||
TypeId subTy, TypeId superTy, bool isFunctionCall = false, bool isIntersection = false, const LiteralProperties* aliasableMap = nullptr);
|
||||
|
||||
private:
|
||||
void tryUnify_(
|
||||
TypeId subTy,
|
||||
TypeId superTy,
|
||||
bool isFunctionCall = false,
|
||||
bool isIntersection = false,
|
||||
const LiteralProperties* aliasableMap = nullptr
|
||||
);
|
||||
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.
|
||||
|
@ -126,21 +120,15 @@ private:
|
|||
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 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 tryUnifyWithClass(TypeId subTy, TypeId superTy, bool reversed);
|
||||
void tryUnifyNegations(TypeId subTy, TypeId superTy);
|
||||
|
||||
TypePackId tryApplyOverloadedFunction(TypeId function, const NormalizedFunctionType& overloads, TypePackId args);
|
||||
|
@ -165,6 +153,7 @@ private:
|
|||
|
||||
std::optional<TypeId> findTablePropertyRespectingMeta(TypeId lhsType, Name name);
|
||||
|
||||
TxnLog combineLogsIntoIntersection(std::vector<TxnLog> logs);
|
||||
TxnLog combineLogsIntoUnion(std::vector<TxnLog> logs);
|
||||
|
||||
public:
|
||||
|
@ -174,7 +163,7 @@ public:
|
|||
bool occursCheck(TypePackId needle, TypePackId haystack, bool reversed);
|
||||
bool occursCheck(DenseHashSet<TypePackId>& seen, TypePackId needle, TypePackId haystack);
|
||||
|
||||
std::unique_ptr<Unifier> makeChildUnifier();
|
||||
Unifier makeChildUnifier();
|
||||
|
||||
void reportError(TypeError err);
|
||||
LUAU_NOINLINE void reportError(Location location, TypeErrorData data);
|
||||
|
@ -190,6 +179,11 @@ private:
|
|||
|
||||
// Available after regular type pack unification errors
|
||||
std::optional<int> firstPackErrorPos;
|
||||
|
||||
// If true, we do a bunch of small things differently to work better with
|
||||
// the new type inference engine. Most notably, we use the Scope hierarchy
|
||||
// directly rather than using TypeLevels.
|
||||
bool useNewSolver = false;
|
||||
};
|
||||
|
||||
void promoteTypeLevels(TxnLog& log, const TypeArena* arena, TypeLevel minLevel, Scope* outerScope, bool useScope, TypePackId tp);
|
||||
|
|
|
@ -44,27 +44,16 @@ struct Unifier2
|
|||
// 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;
|
||||
DenseHashSet<const void*>* uninhabitedTypeFamilies;
|
||||
|
||||
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
|
||||
);
|
||||
Unifier2(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, NotNull<Scope> scope, NotNull<InternalErrorReporter> ice,
|
||||
DenseHashSet<const void*>* uninhabitedTypeFamilies);
|
||||
|
||||
/** Attempt to commit the subtype relation subTy <: superTy to the type
|
||||
* graph.
|
||||
|
@ -93,9 +82,6 @@ struct Unifier2
|
|||
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);
|
||||
|
||||
|
@ -119,9 +105,6 @@ private:
|
|||
// 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
|
||||
|
|
|
@ -49,26 +49,6 @@ struct UnifierSharedState
|
|||
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
|
||||
|
|
|
@ -19,7 +19,7 @@ class Variant
|
|||
static_assert(std::disjunction_v<std::is_reference<Ts>...> == false, "variant does not allow references as an alternative type");
|
||||
static_assert(std::disjunction_v<std::is_array<Ts>...> == false, "variant does not allow arrays as an alternative type");
|
||||
|
||||
public:
|
||||
private:
|
||||
template<typename T>
|
||||
static constexpr int getTypeId()
|
||||
{
|
||||
|
@ -35,7 +35,6 @@ public:
|
|||
return -1;
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename T, typename... Tail>
|
||||
struct First
|
||||
{
|
||||
|
@ -240,9 +239,8 @@ auto visit(Visitor&& vis, const Variant<Ts...>& var)
|
|||
static_assert(std::conjunction_v<std::is_invocable<Visitor, Ts>...>, "visitor must accept every alternative as an argument");
|
||||
|
||||
using Result = std::invoke_result_t<Visitor, typename Variant<Ts...>::first_alternative>;
|
||||
static_assert(
|
||||
std::conjunction_v<std::is_same<Result, std::invoke_result_t<Visitor, Ts>>...>, "visitor result type must be consistent between alternatives"
|
||||
);
|
||||
static_assert(std::conjunction_v<std::is_same<Result, std::invoke_result_t<Visitor, Ts>>...>,
|
||||
"visitor result type must be consistent between alternatives");
|
||||
|
||||
if constexpr (std::is_same_v<Result, void>)
|
||||
{
|
||||
|
@ -268,9 +266,8 @@ auto visit(Visitor&& vis, Variant<Ts...>& var)
|
|||
static_assert(std::conjunction_v<std::is_invocable<Visitor, Ts&>...>, "visitor must accept every alternative as an argument");
|
||||
|
||||
using Result = std::invoke_result_t<Visitor, typename Variant<Ts...>::first_alternative&>;
|
||||
static_assert(
|
||||
std::conjunction_v<std::is_same<Result, std::invoke_result_t<Visitor, Ts&>>...>, "visitor result type must be consistent between alternatives"
|
||||
);
|
||||
static_assert(std::conjunction_v<std::is_same<Result, std::invoke_result_t<Visitor, Ts&>>...>,
|
||||
"visitor result type must be consistent between alternatives");
|
||||
|
||||
if constexpr (std::is_same_v<Result, void>)
|
||||
{
|
|
@ -10,7 +10,8 @@
|
|||
#include "Type.h"
|
||||
|
||||
LUAU_FASTINT(LuauVisitRecursionLimit)
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauBoundLazyTypes2)
|
||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -85,8 +86,6 @@ struct GenericTypeVisitor
|
|||
{
|
||||
}
|
||||
|
||||
virtual ~GenericTypeVisitor() {}
|
||||
|
||||
virtual void cycle(TypeId) {}
|
||||
virtual void cycle(TypePackId) {}
|
||||
|
||||
|
@ -126,7 +125,7 @@ struct GenericTypeVisitor
|
|||
{
|
||||
return visit(ty);
|
||||
}
|
||||
virtual bool visit(TypeId ty, const ExternType& etv)
|
||||
virtual bool visit(TypeId ty, const ClassType& ctv)
|
||||
{
|
||||
return visit(ty);
|
||||
}
|
||||
|
@ -134,10 +133,6 @@ struct GenericTypeVisitor
|
|||
{
|
||||
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);
|
||||
|
@ -191,7 +186,7 @@ struct GenericTypeVisitor
|
|||
{
|
||||
return visit(tp);
|
||||
}
|
||||
virtual bool visit(TypePackId tp, const ErrorTypePack& etp)
|
||||
virtual bool visit(TypePackId tp, const Unifiable::Error& etp)
|
||||
{
|
||||
return visit(tp);
|
||||
}
|
||||
|
@ -231,12 +226,12 @@ struct GenericTypeVisitor
|
|||
}
|
||||
else if (auto ftv = get<FreeType>(ty))
|
||||
{
|
||||
if (FFlag::LuauSolverV2)
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
if (visit(ty, *ftv))
|
||||
{
|
||||
// TODO: Replace these if statements with assert()s when we
|
||||
// delete FFlag::LuauSolverV2.
|
||||
// delete FFlag::DebugLuauDeferredConstraintResolution.
|
||||
//
|
||||
// When the old solver is used, these pointers are always
|
||||
// unused. When the new solver is used, they are never null.
|
||||
|
@ -281,7 +276,7 @@ struct GenericTypeVisitor
|
|||
{
|
||||
for (auto& [_name, prop] : ttv->props)
|
||||
{
|
||||
if (FFlag::LuauSolverV2)
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
if (auto ty = prop.readTy)
|
||||
traverse(*ty);
|
||||
|
@ -313,13 +308,13 @@ struct GenericTypeVisitor
|
|||
traverse(mtv->metatable);
|
||||
}
|
||||
}
|
||||
else if (auto etv = get<ExternType>(ty))
|
||||
else if (auto ctv = get<ClassType>(ty))
|
||||
{
|
||||
if (visit(ty, *etv))
|
||||
if (visit(ty, *ctv))
|
||||
{
|
||||
for (const auto& [name, prop] : etv->props)
|
||||
for (const auto& [name, prop] : ctv->props)
|
||||
{
|
||||
if (FFlag::LuauSolverV2)
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
if (auto ty = prop.readTy)
|
||||
traverse(*ty);
|
||||
|
@ -335,23 +330,21 @@ struct GenericTypeVisitor
|
|||
traverse(prop.type());
|
||||
}
|
||||
|
||||
if (etv->parent)
|
||||
traverse(*etv->parent);
|
||||
if (ctv->parent)
|
||||
traverse(*ctv->parent);
|
||||
|
||||
if (etv->metatable)
|
||||
traverse(*etv->metatable);
|
||||
if (ctv->metatable)
|
||||
traverse(*ctv->metatable);
|
||||
|
||||
if (etv->indexer)
|
||||
if (ctv->indexer)
|
||||
{
|
||||
traverse(etv->indexer->indexType);
|
||||
traverse(etv->indexer->indexResultType);
|
||||
traverse(ctv->indexer->indexType);
|
||||
traverse(ctv->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))
|
||||
|
@ -396,7 +389,7 @@ struct GenericTypeVisitor
|
|||
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
|
||||
// Asserting also makes no sense, because the type _will_ happen here, most likely as a property of some ClassType
|
||||
// that doesn't need to be expanded.
|
||||
}
|
||||
else if (auto stv = get<SingletonType>(ty))
|
||||
|
@ -462,7 +455,7 @@ struct GenericTypeVisitor
|
|||
else if (auto gtv = get<GenericTypePack>(tp))
|
||||
visit(tp, *gtv);
|
||||
|
||||
else if (auto etv = get<ErrorTypePack>(tp))
|
||||
else if (auto etv = get<Unifiable::Error>(tp))
|
||||
visit(tp, *etv);
|
||||
|
||||
else if (auto pack = get<TypePack>(tp))
|
||||
|
|
|
@ -9,14 +9,8 @@
|
|||
namespace Luau
|
||||
{
|
||||
|
||||
Anyification::Anyification(
|
||||
TypeArena* arena,
|
||||
NotNull<Scope> scope,
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
InternalErrorReporter* iceHandler,
|
||||
TypeId anyType,
|
||||
TypePackId anyTypePack
|
||||
)
|
||||
Anyification::Anyification(TypeArena* arena, NotNull<Scope> scope, NotNull<BuiltinTypes> builtinTypes, InternalErrorReporter* iceHandler,
|
||||
TypeId anyType, TypePackId anyTypePack)
|
||||
: Substitution(TxnLog::empty(), arena)
|
||||
, scope(scope)
|
||||
, builtinTypes(builtinTypes)
|
||||
|
@ -26,14 +20,8 @@ Anyification::Anyification(
|
|||
{
|
||||
}
|
||||
|
||||
Anyification::Anyification(
|
||||
TypeArena* arena,
|
||||
const ScopePtr& scope,
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
InternalErrorReporter* iceHandler,
|
||||
TypeId anyType,
|
||||
TypePackId 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)
|
||||
{
|
||||
}
|
||||
|
@ -88,7 +76,7 @@ TypePackId Anyification::clean(TypePackId tp)
|
|||
|
||||
bool Anyification::ignoreChildren(TypeId ty)
|
||||
{
|
||||
if (get<ExternType>(ty))
|
||||
if (get<ClassType>(ty))
|
||||
return true;
|
||||
|
||||
return ty->persistent;
|
||||
|
|
|
@ -31,7 +31,7 @@ bool ApplyTypeFunction::ignoreChildren(TypeId ty)
|
|||
{
|
||||
if (get<GenericType>(ty))
|
||||
return true;
|
||||
else if (get<ExternType>(ty))
|
||||
else if (get<ClassType>(ty))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -11,7 +11,8 @@
|
|||
|
||||
#include <algorithm>
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
|
||||
LUAU_FASTFLAGVARIABLE(LuauFixBindingForGlobalPos, false);
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -41,15 +42,11 @@ struct AutocompleteNodeFinder : public AstVisitor
|
|||
|
||||
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))
|
||||
if (stat->location.begin < pos && pos <= stat->location.end)
|
||||
{
|
||||
ancestry.push_back(stat);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -180,52 +177,60 @@ struct FindNode : public AstVisitor
|
|||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
FindFullAncestry::FindFullAncestry(Position pos, Position documentEnd, bool includeTypes)
|
||||
: pos(pos)
|
||||
, documentEnd(documentEnd)
|
||||
, includeTypes(includeTypes)
|
||||
struct FindFullAncestry final : public AstVisitor
|
||||
{
|
||||
}
|
||||
std::vector<AstNode*> nodes;
|
||||
Position pos;
|
||||
Position documentEnd;
|
||||
bool includeTypes = false;
|
||||
|
||||
bool FindFullAncestry::visit(AstType* type)
|
||||
{
|
||||
if (includeTypes)
|
||||
return visit(static_cast<AstNode*>(type));
|
||||
else
|
||||
explicit FindFullAncestry(Position pos, Position documentEnd, bool includeTypes = false)
|
||||
: pos(pos)
|
||||
, documentEnd(documentEnd)
|
||||
, includeTypes(includeTypes)
|
||||
{
|
||||
}
|
||||
|
||||
bool visit(AstType* type) override
|
||||
{
|
||||
if (includeTypes)
|
||||
return visit(static_cast<AstNode*>(type));
|
||||
else
|
||||
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 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)
|
||||
bool visit(AstNode* node) override
|
||||
{
|
||||
nodes.push_back(node);
|
||||
return true;
|
||||
}
|
||||
if (node->location.contains(pos))
|
||||
{
|
||||
nodes.push_back(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
std::vector<AstNode*> findAncestryAtPositionForAutocomplete(const SourceModule& source, Position pos)
|
||||
{
|
||||
|
@ -330,18 +335,13 @@ static std::optional<AstStatLocal*> findBindingLocalStatement(const SourceModule
|
|||
{
|
||||
// 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}})
|
||||
if (FFlag::LuauFixBindingForGlobalPos && 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;
|
||||
}
|
||||
|
||||
|
@ -480,11 +480,7 @@ ExprOrLocal findExprOrLocalAtPosition(const SourceModule& source, Position pos)
|
|||
}
|
||||
|
||||
static std::optional<DocumentationSymbol> checkOverloadedDocumentationSymbol(
|
||||
const Module& module,
|
||||
const TypeId ty,
|
||||
const AstExpr* parentExpr,
|
||||
const std::optional<DocumentationSymbol> documentationSymbol
|
||||
)
|
||||
const Module& module, const TypeId ty, const AstExpr* parentExpr, const std::optional<DocumentationSymbol> documentationSymbol)
|
||||
{
|
||||
if (!documentationSymbol)
|
||||
return std::nullopt;
|
||||
|
@ -513,37 +509,6 @@ static std::optional<DocumentationSymbol> checkOverloadedDocumentationSymbol(
|
|||
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);
|
||||
|
@ -565,7 +530,7 @@ std::optional<DocumentationSymbol> getDocumentationSymbolAtPosition(const Source
|
|||
{
|
||||
if (auto propIt = ttv->props.find(indexName->index.value); propIt != ttv->props.end())
|
||||
{
|
||||
if (FFlag::LuauSolverV2)
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
if (auto ty = propIt->second.readTy)
|
||||
return checkOverloadedDocumentationSymbol(module, *ty, parentExpr, propIt->second.documentationSymbol);
|
||||
|
@ -574,31 +539,17 @@ std::optional<DocumentationSymbol> getDocumentationSymbolAtPosition(const Source
|
|||
return checkOverloadedDocumentationSymbol(module, propIt->second.type(), parentExpr, propIt->second.documentationSymbol);
|
||||
}
|
||||
}
|
||||
else if (const ExternType* etv = get<ExternType>(parentTy))
|
||||
else if (const ClassType* ctv = get<ClassType>(parentTy))
|
||||
{
|
||||
while (etv)
|
||||
if (auto propIt = ctv->props.find(indexName->index.value); propIt != ctv->props.end())
|
||||
{
|
||||
if (auto propIt = etv->props.find(indexName->index.value); propIt != etv->props.end())
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
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
|
||||
);
|
||||
if (auto ty = propIt->second.readTy)
|
||||
return checkOverloadedDocumentationSymbol(module, *ty, 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;
|
||||
else
|
||||
return checkOverloadedDocumentationSymbol(module, propIt->second.type(), parentExpr, propIt->second.documentationSymbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,27 +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/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
|
@ -1,20 +1,16 @@
|
|||
// 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)
|
||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
|
||||
|
||||
// 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
|
||||
{
|
||||
|
||||
|
@ -31,8 +27,6 @@ const T* get(const Kind& kind)
|
|||
|
||||
class TypeCloner
|
||||
{
|
||||
|
||||
protected:
|
||||
NotNull<TypeArena> arena;
|
||||
NotNull<BuiltinTypes> builtinTypes;
|
||||
|
||||
|
@ -44,31 +38,17 @@ protected:
|
|||
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
|
||||
)
|
||||
TypeCloner(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, NotNull<SeenTypes> types, NotNull<SeenTypePacks> packs)
|
||||
: arena(arena)
|
||||
, builtinTypes(builtinTypes)
|
||||
, types(types)
|
||||
, packs(packs)
|
||||
, forceTy(forceTy)
|
||||
, forceTp(forceTp)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~TypeCloner() = default;
|
||||
|
||||
TypeId clone(TypeId ty)
|
||||
{
|
||||
shallowClone(ty);
|
||||
|
@ -127,13 +107,12 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
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)
|
||||
else if (ty->persistent)
|
||||
return ty;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
@ -143,7 +122,7 @@ protected:
|
|||
tp = follow(tp);
|
||||
if (auto it = packs->find(tp); it != packs->end())
|
||||
return it->second;
|
||||
else if (tp->persistent && tp != forceTp)
|
||||
else if (tp->persistent)
|
||||
return tp;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
@ -161,15 +140,15 @@ protected:
|
|||
}
|
||||
}
|
||||
|
||||
public:
|
||||
virtual TypeId shallowClone(TypeId ty)
|
||||
private:
|
||||
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)
|
||||
else if (ty->persistent)
|
||||
return ty;
|
||||
|
||||
TypeId target = arena->addType(ty->ty);
|
||||
|
@ -179,6 +158,8 @@ public:
|
|||
generic->scope = nullptr;
|
||||
else if (auto free = getMutable<FreeType>(target))
|
||||
free->scope = nullptr;
|
||||
else if (auto fn = getMutable<FunctionType>(target))
|
||||
fn->scope = nullptr;
|
||||
else if (auto table = getMutable<TableType>(target))
|
||||
table->scope = nullptr;
|
||||
|
||||
|
@ -187,13 +168,13 @@ public:
|
|||
return target;
|
||||
}
|
||||
|
||||
virtual TypePackId shallowClone(TypePackId tp)
|
||||
TypePackId shallowClone(TypePackId tp)
|
||||
{
|
||||
tp = follow(tp);
|
||||
|
||||
if (auto clone = find(tp))
|
||||
return *clone;
|
||||
else if (tp->persistent && tp != forceTp)
|
||||
else if (tp->persistent)
|
||||
return tp;
|
||||
|
||||
TypePackId target = arena->addTypePack(tp->ty);
|
||||
|
@ -208,10 +189,9 @@ public:
|
|||
return target;
|
||||
}
|
||||
|
||||
private:
|
||||
Property shallowClone(const Property& p)
|
||||
{
|
||||
if (FFlag::LuauSolverV2)
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
std::optional<TypeId> cloneReadTy;
|
||||
if (auto ty = p.readTy)
|
||||
|
@ -247,23 +227,19 @@ private:
|
|||
void cloneChildren(TypeId ty)
|
||||
{
|
||||
return visit(
|
||||
[&](auto&& t)
|
||||
{
|
||||
[&](auto&& t) {
|
||||
return cloneChildren(&t);
|
||||
},
|
||||
asMutable(ty)->ty
|
||||
);
|
||||
asMutable(ty)->ty);
|
||||
}
|
||||
|
||||
void cloneChildren(TypePackId tp)
|
||||
{
|
||||
return visit(
|
||||
[&](auto&& t)
|
||||
{
|
||||
[&](auto&& t) {
|
||||
return cloneChildren(&t);
|
||||
},
|
||||
asMutable(tp)->ty
|
||||
);
|
||||
asMutable(tp)->ty);
|
||||
}
|
||||
|
||||
void cloneChildren(Kind kind)
|
||||
|
@ -276,7 +252,8 @@ private:
|
|||
LUAU_ASSERT(!"Item holds neither TypeId nor TypePackId when enqueuing its children?");
|
||||
}
|
||||
|
||||
void cloneChildren(ErrorType* t)
|
||||
// ErrorType and ErrorTypePack is an alias to this type.
|
||||
void cloneChildren(Unifiable::Error* t)
|
||||
{
|
||||
// noop.
|
||||
}
|
||||
|
@ -355,7 +332,7 @@ private:
|
|||
t->metatable = shallowClone(t->metatable);
|
||||
}
|
||||
|
||||
void cloneChildren(ExternType* t)
|
||||
void cloneChildren(ClassType* t)
|
||||
{
|
||||
for (auto& [_, p] : t->props)
|
||||
p = shallowClone(p);
|
||||
|
@ -378,11 +355,6 @@ private:
|
|||
// noop.
|
||||
}
|
||||
|
||||
void cloneChildren(NoRefineType* t)
|
||||
{
|
||||
// noop.
|
||||
}
|
||||
|
||||
void cloneChildren(UnionType* t)
|
||||
{
|
||||
for (TypeId& ty : t->options)
|
||||
|
@ -395,7 +367,7 @@ private:
|
|||
ty = shallowClone(ty);
|
||||
}
|
||||
|
||||
virtual void cloneChildren(LazyType* t)
|
||||
void cloneChildren(LazyType* t)
|
||||
{
|
||||
if (auto unwrapped = t->unwrapped.load())
|
||||
t->unwrapped.store(shallowClone(unwrapped));
|
||||
|
@ -446,11 +418,6 @@ private:
|
|||
t->boundTo = shallowClone(t->boundTo);
|
||||
}
|
||||
|
||||
void cloneChildren(ErrorTypePack* t)
|
||||
{
|
||||
// noop.
|
||||
}
|
||||
|
||||
void cloneChildren(VariadicTypePack* t)
|
||||
{
|
||||
t->ty = shallowClone(t->ty);
|
||||
|
@ -475,131 +442,14 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
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};
|
||||
TypeCloner cloner{NotNull{&dest}, cloneState.builtinTypes, NotNull{&cloneState.seenTypes}, NotNull{&cloneState.seenTypePacks}};
|
||||
return cloner.clone(tp);
|
||||
}
|
||||
|
||||
|
@ -608,13 +458,13 @@ 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};
|
||||
TypeCloner cloner{NotNull{&dest}, cloneState.builtinTypes, NotNull{&cloneState.seenTypes}, NotNull{&cloneState.seenTypePacks}};
|
||||
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};
|
||||
TypeCloner cloner{NotNull{&dest}, cloneState.builtinTypes, NotNull{&cloneState.seenTypes}, NotNull{&cloneState.seenTypePacks}};
|
||||
|
||||
TypeFun copy = typeFun;
|
||||
|
||||
|
@ -639,110 +489,4 @@ TypeFun clone(const TypeFun& typeFun, TypeArena& dest, CloneState& cloneState)
|
|||
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
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
#include "Luau/Constraint.h"
|
||||
#include "Luau/VisitType.h"
|
||||
|
||||
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -20,7 +18,7 @@ struct ReferenceCountInitializer : TypeOnceVisitor
|
|||
|
||||
DenseHashSet<TypeId>* result;
|
||||
|
||||
explicit ReferenceCountInitializer(DenseHashSet<TypeId>* result)
|
||||
ReferenceCountInitializer(DenseHashSet<TypeId>* result)
|
||||
: result(result)
|
||||
{
|
||||
}
|
||||
|
@ -43,16 +41,11 @@ struct ReferenceCountInitializer : TypeOnceVisitor
|
|||
return false;
|
||||
}
|
||||
|
||||
bool visit(TypeId ty, const ExternType&) override
|
||||
bool visit(TypeId ty, const ClassType&) override
|
||||
{
|
||||
// ExternTypes never contain free types.
|
||||
// ClassTypes never contain free types.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool visit(TypeId, const TypeFunctionInstanceType&) override
|
||||
{
|
||||
return FFlag::DebugLuauGreedyGeneralization;
|
||||
}
|
||||
};
|
||||
|
||||
bool isReferenceCountedType(const TypeId typ)
|
||||
|
@ -104,11 +97,6 @@ DenseHashSet<TypeId> Constraint::getMaybeMutatedFreeTypes() const
|
|||
{
|
||||
rci.traverse(fchc->argsPack);
|
||||
}
|
||||
else if (auto fcc = get<FunctionCallConstraint>(*this); fcc && FFlag::DebugLuauGreedyGeneralization)
|
||||
{
|
||||
rci.traverse(fcc->fn);
|
||||
rci.traverse(fcc->argsPack);
|
||||
}
|
||||
else if (auto ptc = get<PrimitiveTypeConstraint>(*this))
|
||||
{
|
||||
rci.traverse(ptc->freeType);
|
||||
|
@ -116,15 +104,12 @@ DenseHashSet<TypeId> Constraint::getMaybeMutatedFreeTypes() const
|
|||
else if (auto hpc = get<HasPropConstraint>(*this))
|
||||
{
|
||||
rci.traverse(hpc->resultType);
|
||||
if (FFlag::DebugLuauGreedyGeneralization)
|
||||
rci.traverse(hpc->subjectType);
|
||||
// `HasPropConstraints` should not mutate `subjectType`.
|
||||
}
|
||||
else if (auto hic = get<HasIndexerConstraint>(*this))
|
||||
{
|
||||
if (FFlag::DebugLuauGreedyGeneralization)
|
||||
rci.traverse(hic->subjectType);
|
||||
rci.traverse(hic->resultType);
|
||||
// `HasIndexerConstraint` should not mutate `indexType`.
|
||||
// `HasIndexerConstraint` should not mutate `subjectType` or `indexType`.
|
||||
}
|
||||
else if (auto apc = get<AssignPropConstraint>(*this))
|
||||
{
|
||||
|
@ -143,18 +128,10 @@ DenseHashSet<TypeId> Constraint::getMaybeMutatedFreeTypes() const
|
|||
rci.traverse(ty);
|
||||
// `UnpackConstraint` should not mutate `sourcePack`.
|
||||
}
|
||||
else if (auto rpc = get<ReduceConstraint>(*this); FFlag::DebugLuauGreedyGeneralization && rpc)
|
||||
{
|
||||
rci.traverse(rpc->ty);
|
||||
}
|
||||
else if (auto rpc = get<ReducePackConstraint>(*this))
|
||||
{
|
||||
rci.traverse(rpc->tp);
|
||||
}
|
||||
else if (auto tcc = get<TableCheckConstraint>(*this))
|
||||
{
|
||||
rci.traverse(tcc->exprType);
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -124,8 +124,7 @@ void write(JsonEmitter& emitter, const ConstraintBlock& block)
|
|||
ObjectEmitter o = emitter.writeObject();
|
||||
o.writePair("stringification", block.stringification);
|
||||
|
||||
auto go = [&o](auto&& t)
|
||||
{
|
||||
auto go = [&o](auto&& t) {
|
||||
using T = std::decay_t<decltype(t)>;
|
||||
|
||||
o.writePair("id", toPointerId(t));
|
||||
|
@ -351,12 +350,8 @@ void DcrLogger::popBlock(NotNull<const Constraint> block)
|
|||
}
|
||||
}
|
||||
|
||||
static void snapshotTypeStrings(
|
||||
const std::vector<ExprTypesAtLocation>& interestedExprs,
|
||||
const std::vector<AnnotationTypesAtLocation>& interestedAnnots,
|
||||
DenseHashMap<const void*, std::string>& map,
|
||||
ToStringOptions& opts
|
||||
)
|
||||
static void snapshotTypeStrings(const std::vector<ExprTypesAtLocation>& interestedExprs,
|
||||
const std::vector<AnnotationTypesAtLocation>& interestedAnnots, DenseHashMap<const void*, std::string>& map, ToStringOptions& opts)
|
||||
{
|
||||
for (const ExprTypesAtLocation& tys : interestedExprs)
|
||||
{
|
||||
|
@ -373,10 +368,7 @@ static void snapshotTypeStrings(
|
|||
}
|
||||
|
||||
void DcrLogger::captureBoundaryState(
|
||||
BoundarySnapshot& target,
|
||||
const Scope* rootScope,
|
||||
const std::vector<NotNull<const Constraint>>& unsolvedConstraints
|
||||
)
|
||||
BoundarySnapshot& target, const Scope* rootScope, const std::vector<NotNull<const Constraint>>& unsolvedConstraints)
|
||||
{
|
||||
target.rootScope = snapshotScope(rootScope, opts);
|
||||
target.unsolvedConstraints.clear();
|
||||
|
@ -399,11 +391,7 @@ void DcrLogger::captureInitialSolverState(const Scope* rootScope, const std::vec
|
|||
}
|
||||
|
||||
StepSnapshot DcrLogger::prepareStepSnapshot(
|
||||
const Scope* rootScope,
|
||||
NotNull<const Constraint> current,
|
||||
bool force,
|
||||
const std::vector<NotNull<const Constraint>>& unsolvedConstraints
|
||||
)
|
||||
const Scope* rootScope, NotNull<const Constraint> current, bool force, const std::vector<NotNull<const Constraint>>& unsolvedConstraints)
|
||||
{
|
||||
ScopeSnapshot scopeSnapshot = snapshotScope(rootScope, opts);
|
||||
DenseHashMap<const Constraint*, ConstraintSnapshot> constraints{nullptr};
|
||||
|
|
|
@ -36,9 +36,9 @@ void collectOperands(DefId def, std::vector<DefId>* operands)
|
|||
}
|
||||
}
|
||||
|
||||
DefId DefArena::freshCell(Symbol sym, Location location, bool subscripted)
|
||||
DefId DefArena::freshCell(bool subscripted)
|
||||
{
|
||||
return NotNull{allocator.allocate(Def{Cell{subscripted}, sym, location})};
|
||||
return NotNull{allocator.allocate(Def{Cell{subscripted}})};
|
||||
}
|
||||
|
||||
DefId DefArena::phi(DefId a, DefId b)
|
||||
|
|
|
@ -277,7 +277,7 @@ static DifferResult diffSingleton(DifferEnvironment& env, TypeId left, TypeId ri
|
|||
static DifferResult diffFunction(DifferEnvironment& env, TypeId left, TypeId right);
|
||||
static DifferResult diffGeneric(DifferEnvironment& env, TypeId left, TypeId right);
|
||||
static DifferResult diffNegation(DifferEnvironment& env, TypeId left, TypeId right);
|
||||
static DifferResult diffExternType(DifferEnvironment& env, TypeId left, TypeId right);
|
||||
static DifferResult diffClass(DifferEnvironment& env, TypeId left, TypeId right);
|
||||
struct FindSeteqCounterexampleResult
|
||||
{
|
||||
// nullopt if no counterexample found
|
||||
|
@ -286,22 +286,15 @@ struct FindSeteqCounterexampleResult
|
|||
bool inLeft;
|
||||
};
|
||||
static FindSeteqCounterexampleResult findSeteqCounterexample(
|
||||
DifferEnvironment& env,
|
||||
const std::vector<TypeId>& left,
|
||||
const std::vector<TypeId>& right
|
||||
);
|
||||
DifferEnvironment& env, const std::vector<TypeId>& left, const std::vector<TypeId>& right);
|
||||
static DifferResult diffUnion(DifferEnvironment& env, TypeId left, TypeId right);
|
||||
static DifferResult diffIntersection(DifferEnvironment& env, TypeId left, TypeId right);
|
||||
/**
|
||||
* The last argument gives context info on which complex type contained the TypePack.
|
||||
*/
|
||||
static DifferResult diffTpi(DifferEnvironment& env, DiffError::Kind possibleNonNormalErrorKind, TypePackId left, TypePackId right);
|
||||
static DifferResult diffCanonicalTpShape(
|
||||
DifferEnvironment& env,
|
||||
DiffError::Kind possibleNonNormalErrorKind,
|
||||
const std::pair<std::vector<TypeId>, std::optional<TypePackId>>& left,
|
||||
const std::pair<std::vector<TypeId>, std::optional<TypePackId>>& right
|
||||
);
|
||||
static DifferResult diffCanonicalTpShape(DifferEnvironment& env, DiffError::Kind possibleNonNormalErrorKind,
|
||||
const std::pair<std::vector<TypeId>, std::optional<TypePackId>>& left, const std::pair<std::vector<TypeId>, std::optional<TypePackId>>& right);
|
||||
static DifferResult diffHandleFlattenedTail(DifferEnvironment& env, DiffError::Kind possibleNonNormalErrorKind, TypePackId left, TypePackId right);
|
||||
static DifferResult diffGenericTp(DifferEnvironment& env, TypePackId left, TypePackId right);
|
||||
|
||||
|
@ -331,13 +324,8 @@ static DifferResult diffTable(DifferEnvironment& env, TypeId left, TypeId right)
|
|||
if (leftTable->props.find(field) == leftTable->props.end())
|
||||
{
|
||||
// right has a field the left doesn't
|
||||
return DifferResult{DiffError{
|
||||
DiffError::Kind::MissingTableProperty,
|
||||
DiffPathNodeLeaf::nullopts(),
|
||||
DiffPathNodeLeaf::detailsTableProperty(value.type(), field),
|
||||
env.getDevFixFriendlyNameLeft(),
|
||||
env.getDevFixFriendlyNameRight()
|
||||
}};
|
||||
return DifferResult{DiffError{DiffError::Kind::MissingTableProperty, DiffPathNodeLeaf::nullopts(),
|
||||
DiffPathNodeLeaf::detailsTableProperty(value.type(), field), env.getDevFixFriendlyNameLeft(), env.getDevFixFriendlyNameRight()}};
|
||||
}
|
||||
}
|
||||
// left and right have the same set of keys
|
||||
|
@ -481,14 +469,14 @@ static DifferResult diffNegation(DifferEnvironment& env, TypeId left, TypeId rig
|
|||
return differResult;
|
||||
}
|
||||
|
||||
static DifferResult diffExternType(DifferEnvironment& env, TypeId left, TypeId right)
|
||||
static DifferResult diffClass(DifferEnvironment& env, TypeId left, TypeId right)
|
||||
{
|
||||
const ExternType* leftExternType = get<ExternType>(left);
|
||||
const ExternType* rightExternType = get<ExternType>(right);
|
||||
LUAU_ASSERT(leftExternType);
|
||||
LUAU_ASSERT(rightExternType);
|
||||
const ClassType* leftClass = get<ClassType>(left);
|
||||
const ClassType* rightClass = get<ClassType>(right);
|
||||
LUAU_ASSERT(leftClass);
|
||||
LUAU_ASSERT(rightClass);
|
||||
|
||||
if (leftExternType == rightExternType)
|
||||
if (leftClass == rightClass)
|
||||
{
|
||||
return DifferResult{};
|
||||
}
|
||||
|
@ -503,10 +491,7 @@ static DifferResult diffExternType(DifferEnvironment& env, TypeId left, TypeId r
|
|||
}
|
||||
|
||||
static FindSeteqCounterexampleResult findSeteqCounterexample(
|
||||
DifferEnvironment& env,
|
||||
const std::vector<TypeId>& left,
|
||||
const std::vector<TypeId>& right
|
||||
)
|
||||
DifferEnvironment& env, const std::vector<TypeId>& left, const std::vector<TypeId>& right)
|
||||
{
|
||||
std::unordered_set<size_t> unmatchedRightIdxes;
|
||||
for (size_t i = 0; i < right.size(); i++)
|
||||
|
@ -651,9 +636,9 @@ static DifferResult diffUsingEnv(DifferEnvironment& env, TypeId left, TypeId rig
|
|||
{
|
||||
return diffNegation(env, left, right);
|
||||
}
|
||||
else if (auto lc = get<ExternType>(left))
|
||||
else if (auto lc = get<ClassType>(left))
|
||||
{
|
||||
return diffExternType(env, left, right);
|
||||
return diffClass(env, left, right);
|
||||
}
|
||||
|
||||
throw InternalCompilerError{"Unimplemented Simple TypeId variant for diffing"};
|
||||
|
@ -718,7 +703,7 @@ static DifferResult diffUsingEnv(DifferEnvironment& env, TypeId left, TypeId rig
|
|||
env.popVisiting();
|
||||
return diffRes;
|
||||
}
|
||||
if (auto le = get<ErrorType>(left))
|
||||
if (auto le = get<Luau::Unifiable::Error>(left))
|
||||
{
|
||||
// TODO: return debug-friendly result state
|
||||
env.popVisiting();
|
||||
|
@ -775,12 +760,8 @@ static DifferResult diffTpi(DifferEnvironment& env, DiffError::Kind possibleNonN
|
|||
return diffHandleFlattenedTail(env, possibleNonNormalErrorKind, *leftFlatTpi.second, *rightFlatTpi.second);
|
||||
}
|
||||
|
||||
static DifferResult diffCanonicalTpShape(
|
||||
DifferEnvironment& env,
|
||||
DiffError::Kind possibleNonNormalErrorKind,
|
||||
const std::pair<std::vector<TypeId>, std::optional<TypePackId>>& left,
|
||||
const std::pair<std::vector<TypeId>, std::optional<TypePackId>>& right
|
||||
)
|
||||
static DifferResult diffCanonicalTpShape(DifferEnvironment& env, DiffError::Kind possibleNonNormalErrorKind,
|
||||
const std::pair<std::vector<TypeId>, std::optional<TypePackId>>& left, const std::pair<std::vector<TypeId>, std::optional<TypePackId>>& right)
|
||||
{
|
||||
if (left.first.size() == right.first.size() && left.second.has_value() == right.second.has_value())
|
||||
return DifferResult{};
|
||||
|
@ -960,7 +941,7 @@ bool isSimple(TypeId ty)
|
|||
{
|
||||
ty = follow(ty);
|
||||
// TODO: think about GenericType, etc.
|
||||
return get<PrimitiveType>(ty) || get<SingletonType>(ty) || get<AnyType>(ty) || get<NegationType>(ty) || get<ExternType>(ty) ||
|
||||
return get<PrimitiveType>(ty) || get<SingletonType>(ty) || get<AnyType>(ty) || get<NegationType>(ty) || get<ClassType>(ty) ||
|
||||
get<UnknownType>(ty) || get<NeverType>(ty);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,324 @@
|
|||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#include "Luau/BuiltinDefinitions.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauDeclareExternType)
|
||||
LUAU_FASTFLAG(LuauTypeFunOptional)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCheckedEmbeddedDefinitions2, false);
|
||||
LUAU_FASTFLAG(LuauAttributeSyntax);
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
static const std::string kBuiltinDefinitionBaseSrc = R"BUILTIN_SRC(
|
||||
static const std::string kBuiltinDefinitionLuaSrc = R"BUILTIN_SRC(
|
||||
|
||||
declare buffer: {
|
||||
create: (size: number) -> buffer,
|
||||
fromstring: (str: string) -> buffer,
|
||||
tostring: (b: buffer) -> string,
|
||||
len: (b: buffer) -> number,
|
||||
copy: (target: buffer, targetOffset: number, source: buffer, sourceOffset: number?, count: number?) -> (),
|
||||
fill: (b: buffer, offset: number, value: number, count: number?) -> (),
|
||||
readi8: (b: buffer, offset: number) -> number,
|
||||
readu8: (b: buffer, offset: number) -> number,
|
||||
readi16: (b: buffer, offset: number) -> number,
|
||||
readu16: (b: buffer, offset: number) -> number,
|
||||
readi32: (b: buffer, offset: number) -> number,
|
||||
readu32: (b: buffer, offset: number) -> number,
|
||||
readf32: (b: buffer, offset: number) -> number,
|
||||
readf64: (b: buffer, offset: number) -> number,
|
||||
writei8: (b: buffer, offset: number, value: number) -> (),
|
||||
writeu8: (b: buffer, offset: number, value: number) -> (),
|
||||
writei16: (b: buffer, offset: number, value: number) -> (),
|
||||
writeu16: (b: buffer, offset: number, value: number) -> (),
|
||||
writei32: (b: buffer, offset: number, value: number) -> (),
|
||||
writeu32: (b: buffer, offset: number, value: number) -> (),
|
||||
writef32: (b: buffer, offset: number, value: number) -> (),
|
||||
writef64: (b: buffer, offset: number, value: number) -> (),
|
||||
readstring: (b: buffer, offset: number, count: number) -> string,
|
||||
writestring: (b: buffer, offset: number, value: string, count: number?) -> (),
|
||||
}
|
||||
|
||||
declare bit32: {
|
||||
band: (...number) -> number,
|
||||
bor: (...number) -> number,
|
||||
bxor: (...number) -> number,
|
||||
btest: (number, ...number) -> boolean,
|
||||
rrotate: (x: number, disp: number) -> number,
|
||||
lrotate: (x: number, disp: number) -> number,
|
||||
lshift: (x: number, disp: number) -> number,
|
||||
arshift: (x: number, disp: number) -> number,
|
||||
rshift: (x: number, disp: number) -> number,
|
||||
bnot: (x: number) -> number,
|
||||
extract: (n: number, field: number, width: number?) -> number,
|
||||
replace: (n: number, v: number, field: number, width: number?) -> number,
|
||||
countlz: (n: number) -> number,
|
||||
countrz: (n: number) -> number,
|
||||
byteswap: (n: number) -> number,
|
||||
}
|
||||
|
||||
declare math: {
|
||||
frexp: (n: number) -> (number, number),
|
||||
ldexp: (s: number, e: number) -> number,
|
||||
fmod: (x: number, y: number) -> number,
|
||||
modf: (n: number) -> (number, number),
|
||||
pow: (x: number, y: number) -> number,
|
||||
exp: (n: number) -> number,
|
||||
|
||||
ceil: (n: number) -> number,
|
||||
floor: (n: number) -> number,
|
||||
abs: (n: number) -> number,
|
||||
sqrt: (n: number) -> number,
|
||||
|
||||
log: (n: number, base: number?) -> number,
|
||||
log10: (n: number) -> number,
|
||||
|
||||
rad: (n: number) -> number,
|
||||
deg: (n: number) -> number,
|
||||
|
||||
sin: (n: number) -> number,
|
||||
cos: (n: number) -> number,
|
||||
tan: (n: number) -> number,
|
||||
sinh: (n: number) -> number,
|
||||
cosh: (n: number) -> number,
|
||||
tanh: (n: number) -> number,
|
||||
atan: (n: number) -> number,
|
||||
acos: (n: number) -> number,
|
||||
asin: (n: number) -> number,
|
||||
atan2: (y: number, x: number) -> number,
|
||||
|
||||
min: (number, ...number) -> number,
|
||||
max: (number, ...number) -> number,
|
||||
|
||||
pi: number,
|
||||
huge: number,
|
||||
|
||||
randomseed: (seed: number) -> (),
|
||||
random: (number?, number?) -> number,
|
||||
|
||||
sign: (n: number) -> number,
|
||||
clamp: (n: number, min: number, max: number) -> number,
|
||||
noise: (x: number, y: number?, z: number?) -> number,
|
||||
round: (n: number) -> number,
|
||||
}
|
||||
|
||||
type DateTypeArg = {
|
||||
year: number,
|
||||
month: number,
|
||||
day: number,
|
||||
hour: number?,
|
||||
min: number?,
|
||||
sec: number?,
|
||||
isdst: boolean?,
|
||||
}
|
||||
|
||||
type DateTypeResult = {
|
||||
year: number,
|
||||
month: number,
|
||||
wday: number,
|
||||
yday: number,
|
||||
day: number,
|
||||
hour: number,
|
||||
min: number,
|
||||
sec: number,
|
||||
isdst: boolean,
|
||||
}
|
||||
|
||||
declare os: {
|
||||
time: (time: DateTypeArg?) -> number,
|
||||
date: ((formatString: "*t" | "!*t", time: number?) -> DateTypeResult) & ((formatString: string?, time: number?) -> string),
|
||||
difftime: (t2: DateTypeResult | number, t1: DateTypeResult | number) -> number,
|
||||
clock: () -> number,
|
||||
}
|
||||
|
||||
declare function require(target: any): any
|
||||
|
||||
declare function getfenv(target: any): { [string]: any }
|
||||
|
||||
declare _G: any
|
||||
declare _VERSION: string
|
||||
|
||||
declare function gcinfo(): number
|
||||
|
||||
declare function print<T...>(...: T...)
|
||||
|
||||
declare function type<T>(value: T): string
|
||||
declare function typeof<T>(value: T): string
|
||||
|
||||
-- `assert` has a magic function attached that will give more detailed type information
|
||||
declare function assert<T>(value: T, errorMessage: string?): T
|
||||
declare function error<T>(message: T, level: number?): never
|
||||
|
||||
declare function tostring<T>(value: T): string
|
||||
declare function tonumber<T>(value: T, radix: number?): number?
|
||||
|
||||
declare function rawequal<T1, T2>(a: T1, b: T2): boolean
|
||||
declare function rawget<K, V>(tab: {[K]: V}, k: K): V
|
||||
declare function rawset<K, V>(tab: {[K]: V}, k: K, v: V): {[K]: V}
|
||||
declare function rawlen<K, V>(obj: {[K]: V} | string): number
|
||||
|
||||
declare function setfenv<T..., R...>(target: number | (T...) -> R..., env: {[string]: any}): ((T...) -> R...)?
|
||||
|
||||
declare function ipairs<V>(tab: {V}): (({V}, number) -> (number?, V), {V}, number)
|
||||
|
||||
declare function pcall<A..., R...>(f: (A...) -> R..., ...: A...): (boolean, R...)
|
||||
|
||||
-- FIXME: The actual type of `xpcall` is:
|
||||
-- <E, A..., R1..., R2...>(f: (A...) -> R1..., err: (E) -> R2..., A...) -> (true, R1...) | (false, R2...)
|
||||
-- Since we can't represent the return value, we use (boolean, R1...).
|
||||
declare function xpcall<E, A..., R1..., R2...>(f: (A...) -> R1..., err: (E) -> R2..., ...: A...): (boolean, R1...)
|
||||
|
||||
-- `select` has a magic function attached to provide more detailed type information
|
||||
declare function select<A...>(i: string | number, ...: A...): ...any
|
||||
|
||||
-- FIXME: This type is not entirely correct - `loadstring` returns a function or
|
||||
-- (nil, string).
|
||||
declare function loadstring<A...>(src: string, chunkname: string?): (((A...) -> any)?, string?)
|
||||
|
||||
declare function newproxy(mt: boolean?): any
|
||||
|
||||
declare coroutine: {
|
||||
create: <A..., R...>(f: (A...) -> R...) -> thread,
|
||||
resume: <A..., R...>(co: thread, A...) -> (boolean, R...),
|
||||
running: () -> thread,
|
||||
status: (co: thread) -> "dead" | "running" | "normal" | "suspended",
|
||||
wrap: <A..., R...>(f: (A...) -> R...) -> ((A...) -> R...),
|
||||
yield: <A..., R...>(A...) -> R...,
|
||||
isyieldable: () -> boolean,
|
||||
close: (co: thread) -> (boolean, any)
|
||||
}
|
||||
|
||||
declare table: {
|
||||
concat: <V>(t: {V}, sep: string?, i: number?, j: number?) -> string,
|
||||
insert: (<V>(t: {V}, value: V) -> ()) & (<V>(t: {V}, pos: number, value: V) -> ()),
|
||||
maxn: <V>(t: {V}) -> number,
|
||||
remove: <V>(t: {V}, number?) -> V?,
|
||||
sort: <V>(t: {V}, comp: ((V, V) -> boolean)?) -> (),
|
||||
create: <V>(count: number, value: V?) -> {V},
|
||||
find: <V>(haystack: {V}, needle: V, init: number?) -> number?,
|
||||
|
||||
unpack: <V>(list: {V}, i: number?, j: number?) -> ...V,
|
||||
pack: <V>(...V) -> { n: number, [number]: V },
|
||||
|
||||
getn: <V>(t: {V}) -> number,
|
||||
foreach: <K, V>(t: {[K]: V}, f: (K, V) -> ()) -> (),
|
||||
foreachi: <V>({V}, (number, V) -> ()) -> (),
|
||||
|
||||
move: <V>(src: {V}, a: number, b: number, t: number, dst: {V}?) -> {V},
|
||||
clear: <K, V>(table: {[K]: V}) -> (),
|
||||
|
||||
isfrozen: <K, V>(t: {[K]: V}) -> boolean,
|
||||
}
|
||||
|
||||
declare debug: {
|
||||
info: (<R...>(thread: thread, level: number, options: string) -> R...) & (<R...>(level: number, options: string) -> R...) & (<A..., R1..., R2...>(func: (A...) -> R1..., options: string) -> R2...),
|
||||
traceback: ((message: string?, level: number?) -> string) & ((thread: thread, message: string?, level: number?) -> string),
|
||||
}
|
||||
|
||||
declare utf8: {
|
||||
char: (...number) -> string,
|
||||
charpattern: string,
|
||||
codes: (str: string) -> ((string, number) -> (number, number), string, number),
|
||||
codepoint: (str: string, i: number?, j: number?) -> ...number,
|
||||
len: (s: string, i: number?, j: number?) -> (number?, number?),
|
||||
offset: (s: string, n: number?, i: number?) -> number,
|
||||
}
|
||||
|
||||
-- Cannot use `typeof` here because it will produce a polytype when we expect a monotype.
|
||||
declare function unpack<V>(tab: {V}, i: number?, j: number?): ...V
|
||||
|
||||
)BUILTIN_SRC";
|
||||
|
||||
static const std::string kBuiltinDefinitionLuaSrcChecked = R"BUILTIN_SRC(
|
||||
|
||||
declare bit32: {
|
||||
band: @checked (...number) -> number,
|
||||
bor: @checked (...number) -> number,
|
||||
bxor: @checked (...number) -> number,
|
||||
btest: @checked (number, ...number) -> boolean,
|
||||
rrotate: @checked (x: number, disp: number) -> number,
|
||||
lrotate: @checked (x: number, disp: number) -> number,
|
||||
lshift: @checked (x: number, disp: number) -> number,
|
||||
arshift: @checked (x: number, disp: number) -> number,
|
||||
rshift: @checked (x: number, disp: number) -> number,
|
||||
bnot: @checked (x: number) -> number,
|
||||
extract: @checked (n: number, field: number, width: number?) -> number,
|
||||
replace: @checked (n: number, v: number, field: number, width: number?) -> number,
|
||||
countlz: @checked (n: number) -> number,
|
||||
countrz: @checked (n: number) -> number,
|
||||
byteswap: @checked (n: number) -> number,
|
||||
}
|
||||
|
||||
declare math: {
|
||||
frexp: @checked (n: number) -> (number, number),
|
||||
ldexp: @checked (s: number, e: number) -> number,
|
||||
fmod: @checked (x: number, y: number) -> number,
|
||||
modf: @checked (n: number) -> (number, number),
|
||||
pow: @checked (x: number, y: number) -> number,
|
||||
exp: @checked (n: number) -> number,
|
||||
|
||||
ceil: @checked (n: number) -> number,
|
||||
floor: @checked (n: number) -> number,
|
||||
abs: @checked (n: number) -> number,
|
||||
sqrt: @checked (n: number) -> number,
|
||||
|
||||
log: @checked (n: number, base: number?) -> number,
|
||||
log10: @checked (n: number) -> number,
|
||||
|
||||
rad: @checked (n: number) -> number,
|
||||
deg: @checked (n: number) -> number,
|
||||
|
||||
sin: @checked (n: number) -> number,
|
||||
cos: @checked (n: number) -> number,
|
||||
tan: @checked (n: number) -> number,
|
||||
sinh: @checked (n: number) -> number,
|
||||
cosh: @checked (n: number) -> number,
|
||||
tanh: @checked (n: number) -> number,
|
||||
atan: @checked (n: number) -> number,
|
||||
acos: @checked (n: number) -> number,
|
||||
asin: @checked (n: number) -> number,
|
||||
atan2: @checked (y: number, x: number) -> number,
|
||||
|
||||
min: @checked (number, ...number) -> number,
|
||||
max: @checked (number, ...number) -> number,
|
||||
|
||||
pi: number,
|
||||
huge: number,
|
||||
|
||||
randomseed: @checked (seed: number) -> (),
|
||||
random: @checked (number?, number?) -> number,
|
||||
|
||||
sign: @checked (n: number) -> number,
|
||||
clamp: @checked (n: number, min: number, max: number) -> number,
|
||||
noise: @checked (x: number, y: number?, z: number?) -> number,
|
||||
round: @checked (n: number) -> number,
|
||||
}
|
||||
|
||||
type DateTypeArg = {
|
||||
year: number,
|
||||
month: number,
|
||||
day: number,
|
||||
hour: number?,
|
||||
min: number?,
|
||||
sec: number?,
|
||||
isdst: boolean?,
|
||||
}
|
||||
|
||||
type DateTypeResult = {
|
||||
year: number,
|
||||
month: number,
|
||||
wday: number,
|
||||
yday: number,
|
||||
day: number,
|
||||
hour: number,
|
||||
min: number,
|
||||
sec: number,
|
||||
isdst: boolean,
|
||||
}
|
||||
|
||||
declare os: {
|
||||
time: (time: DateTypeArg?) -> number,
|
||||
date: ((formatString: "*t" | "!*t", time: number?) -> DateTypeResult) & ((formatString: string?, time: number?) -> string),
|
||||
difftime: (t2: DateTypeResult | number, t1: DateTypeResult | number) -> number,
|
||||
clock: () -> number,
|
||||
}
|
||||
|
||||
@checked declare function require(target: any): any
|
||||
|
||||
|
@ -55,119 +366,6 @@ declare function loadstring<A...>(src: string, chunkname: string?): (((A...) ->
|
|||
|
||||
@checked declare function newproxy(mt: boolean?): any
|
||||
|
||||
-- Cannot use `typeof` here because it will produce a polytype when we expect a monotype.
|
||||
declare function unpack<V>(tab: {V}, i: number?, j: number?): ...V
|
||||
|
||||
)BUILTIN_SRC";
|
||||
|
||||
static const std::string kBuiltinDefinitionBit32Src = R"BUILTIN_SRC(
|
||||
|
||||
declare bit32: {
|
||||
band: @checked (...number) -> number,
|
||||
bor: @checked (...number) -> number,
|
||||
bxor: @checked (...number) -> number,
|
||||
btest: @checked (number, ...number) -> boolean,
|
||||
rrotate: @checked (x: number, disp: number) -> number,
|
||||
lrotate: @checked (x: number, disp: number) -> number,
|
||||
lshift: @checked (x: number, disp: number) -> number,
|
||||
arshift: @checked (x: number, disp: number) -> number,
|
||||
rshift: @checked (x: number, disp: number) -> number,
|
||||
bnot: @checked (x: number) -> number,
|
||||
extract: @checked (n: number, field: number, width: number?) -> number,
|
||||
replace: @checked (n: number, v: number, field: number, width: number?) -> number,
|
||||
countlz: @checked (n: number) -> number,
|
||||
countrz: @checked (n: number) -> number,
|
||||
byteswap: @checked (n: number) -> number,
|
||||
}
|
||||
|
||||
)BUILTIN_SRC";
|
||||
|
||||
static const std::string kBuiltinDefinitionMathSrc = R"BUILTIN_SRC(
|
||||
|
||||
declare math: {
|
||||
frexp: @checked (n: number) -> (number, number),
|
||||
ldexp: @checked (s: number, e: number) -> number,
|
||||
fmod: @checked (x: number, y: number) -> number,
|
||||
modf: @checked (n: number) -> (number, number),
|
||||
pow: @checked (x: number, y: number) -> number,
|
||||
exp: @checked (n: number) -> number,
|
||||
|
||||
ceil: @checked (n: number) -> number,
|
||||
floor: @checked (n: number) -> number,
|
||||
abs: @checked (n: number) -> number,
|
||||
sqrt: @checked (n: number) -> number,
|
||||
|
||||
log: @checked (n: number, base: number?) -> number,
|
||||
log10: @checked (n: number) -> number,
|
||||
|
||||
rad: @checked (n: number) -> number,
|
||||
deg: @checked (n: number) -> number,
|
||||
|
||||
sin: @checked (n: number) -> number,
|
||||
cos: @checked (n: number) -> number,
|
||||
tan: @checked (n: number) -> number,
|
||||
sinh: @checked (n: number) -> number,
|
||||
cosh: @checked (n: number) -> number,
|
||||
tanh: @checked (n: number) -> number,
|
||||
atan: @checked (n: number) -> number,
|
||||
acos: @checked (n: number) -> number,
|
||||
asin: @checked (n: number) -> number,
|
||||
atan2: @checked (y: number, x: number) -> number,
|
||||
|
||||
min: @checked (number, ...number) -> number,
|
||||
max: @checked (number, ...number) -> number,
|
||||
|
||||
pi: number,
|
||||
huge: number,
|
||||
|
||||
randomseed: @checked (seed: number) -> (),
|
||||
random: @checked (number?, number?) -> number,
|
||||
|
||||
sign: @checked (n: number) -> number,
|
||||
clamp: @checked (n: number, min: number, max: number) -> number,
|
||||
noise: @checked (x: number, y: number?, z: number?) -> number,
|
||||
round: @checked (n: number) -> number,
|
||||
map: @checked (x: number, inmin: number, inmax: number, outmin: number, outmax: number) -> number,
|
||||
lerp: @checked (a: number, b: number, t: number) -> number,
|
||||
}
|
||||
|
||||
)BUILTIN_SRC";
|
||||
|
||||
static const std::string kBuiltinDefinitionOsSrc = R"BUILTIN_SRC(
|
||||
|
||||
type DateTypeArg = {
|
||||
year: number,
|
||||
month: number,
|
||||
day: number,
|
||||
hour: number?,
|
||||
min: number?,
|
||||
sec: number?,
|
||||
isdst: boolean?,
|
||||
}
|
||||
|
||||
type DateTypeResult = {
|
||||
year: number,
|
||||
month: number,
|
||||
wday: number,
|
||||
yday: number,
|
||||
day: number,
|
||||
hour: number,
|
||||
min: number,
|
||||
sec: number,
|
||||
isdst: boolean,
|
||||
}
|
||||
|
||||
declare os: {
|
||||
time: (time: DateTypeArg?) -> number,
|
||||
date: ((formatString: "*t" | "!*t", time: number?) -> DateTypeResult) & ((formatString: string?, time: number?) -> string),
|
||||
difftime: (t2: DateTypeResult | number, t1: DateTypeResult | number) -> number,
|
||||
clock: () -> number,
|
||||
}
|
||||
|
||||
)BUILTIN_SRC";
|
||||
|
||||
static const std::string kBuiltinDefinitionCoroutineSrc = R"BUILTIN_SRC(
|
||||
|
||||
declare coroutine: {
|
||||
create: <A..., R...>(f: (A...) -> R...) -> thread,
|
||||
resume: <A..., R...>(co: thread, A...) -> (boolean, R...),
|
||||
|
@ -179,10 +377,6 @@ declare coroutine: {
|
|||
close: @checked (co: thread) -> (boolean, any)
|
||||
}
|
||||
|
||||
)BUILTIN_SRC";
|
||||
|
||||
static const std::string kBuiltinDefinitionTableSrc = R"BUILTIN_SRC(
|
||||
|
||||
declare table: {
|
||||
concat: <V>(t: {V}, sep: string?, i: number?, j: number?) -> string,
|
||||
insert: (<V>(t: {V}, value: V) -> ()) & (<V>(t: {V}, pos: number, value: V) -> ()),
|
||||
|
@ -205,19 +399,11 @@ declare table: {
|
|||
isfrozen: <K, V>(t: {[K]: V}) -> boolean,
|
||||
}
|
||||
|
||||
)BUILTIN_SRC";
|
||||
|
||||
static const std::string kBuiltinDefinitionDebugSrc = R"BUILTIN_SRC(
|
||||
|
||||
declare debug: {
|
||||
info: ((thread: thread, level: number, options: string) -> ...any) & ((level: number, options: string) -> ...any) & (<A..., R1...>(func: (A...) -> R1..., options: string) -> ...any),
|
||||
info: (<R...>(thread: thread, level: number, options: string) -> R...) & (<R...>(level: number, options: string) -> R...) & (<A..., R1..., R2...>(func: (A...) -> R1..., options: string) -> R2...),
|
||||
traceback: ((message: string?, level: number?) -> string) & ((thread: thread, message: string?, level: number?) -> string),
|
||||
}
|
||||
|
||||
)BUILTIN_SRC";
|
||||
|
||||
static const std::string kBuiltinDefinitionUtf8Src = R"BUILTIN_SRC(
|
||||
|
||||
declare utf8: {
|
||||
char: @checked (...number) -> string,
|
||||
charpattern: string,
|
||||
|
@ -227,9 +413,10 @@ declare utf8: {
|
|||
offset: @checked (s: string, n: number?, i: number?) -> number,
|
||||
}
|
||||
|
||||
)BUILTIN_SRC";
|
||||
-- Cannot use `typeof` here because it will produce a polytype when we expect a monotype.
|
||||
declare function unpack<V>(tab: {V}, i: number?, j: number?): ...V
|
||||
|
||||
|
||||
static const std::string kBuiltinDefinitionBufferSrc = R"BUILTIN_SRC(
|
||||
--- Buffer API
|
||||
declare buffer: {
|
||||
create: @checked (size: number) -> buffer,
|
||||
|
@ -256,200 +443,17 @@ declare buffer: {
|
|||
writef64: @checked (b: buffer, offset: number, value: number) -> (),
|
||||
readstring: @checked (b: buffer, offset: number, count: number) -> string,
|
||||
writestring: @checked (b: buffer, offset: number, value: string, count: number?) -> (),
|
||||
readbits: @checked (b: buffer, bitOffset: number, bitCount: number) -> number,
|
||||
writebits: @checked (b: buffer, bitOffset: number, bitCount: number, value: number) -> (),
|
||||
}
|
||||
|
||||
)BUILTIN_SRC";
|
||||
|
||||
static const std::string kBuiltinDefinitionVectorSrc = (FFlag::LuauDeclareExternType)
|
||||
? R"BUILTIN_SRC(
|
||||
|
||||
-- While vector would have been better represented as a built-in primitive type, type solver extern type handling covers most of the properties
|
||||
declare extern type vector with
|
||||
x: number
|
||||
y: number
|
||||
z: number
|
||||
end
|
||||
|
||||
declare vector: {
|
||||
create: @checked (x: number, y: number, z: number?) -> vector,
|
||||
magnitude: @checked (vec: vector) -> number,
|
||||
normalize: @checked (vec: vector) -> vector,
|
||||
cross: @checked (vec1: vector, vec2: vector) -> vector,
|
||||
dot: @checked (vec1: vector, vec2: vector) -> number,
|
||||
angle: @checked (vec1: vector, vec2: vector, axis: vector?) -> number,
|
||||
floor: @checked (vec: vector) -> vector,
|
||||
ceil: @checked (vec: vector) -> vector,
|
||||
abs: @checked (vec: vector) -> vector,
|
||||
sign: @checked (vec: vector) -> vector,
|
||||
clamp: @checked (vec: vector, min: vector, max: vector) -> vector,
|
||||
max: @checked (vector, ...vector) -> vector,
|
||||
min: @checked (vector, ...vector) -> vector,
|
||||
|
||||
zero: vector,
|
||||
one: vector,
|
||||
}
|
||||
|
||||
)BUILTIN_SRC"
|
||||
: R"BUILTIN_SRC(
|
||||
|
||||
-- While vector would have been better represented as a built-in primitive type, type solver class handling covers most of the properties
|
||||
declare class vector
|
||||
x: number
|
||||
y: number
|
||||
z: number
|
||||
end
|
||||
|
||||
declare vector: {
|
||||
create: @checked (x: number, y: number, z: number?) -> vector,
|
||||
magnitude: @checked (vec: vector) -> number,
|
||||
normalize: @checked (vec: vector) -> vector,
|
||||
cross: @checked (vec1: vector, vec2: vector) -> vector,
|
||||
dot: @checked (vec1: vector, vec2: vector) -> number,
|
||||
angle: @checked (vec1: vector, vec2: vector, axis: vector?) -> number,
|
||||
floor: @checked (vec: vector) -> vector,
|
||||
ceil: @checked (vec: vector) -> vector,
|
||||
abs: @checked (vec: vector) -> vector,
|
||||
sign: @checked (vec: vector) -> vector,
|
||||
clamp: @checked (vec: vector, min: vector, max: vector) -> vector,
|
||||
max: @checked (vector, ...vector) -> vector,
|
||||
min: @checked (vector, ...vector) -> vector,
|
||||
|
||||
zero: vector,
|
||||
one: vector,
|
||||
}
|
||||
|
||||
)BUILTIN_SRC";
|
||||
|
||||
std::string getBuiltinDefinitionSource()
|
||||
{
|
||||
std::string result = kBuiltinDefinitionBaseSrc;
|
||||
std::string result = kBuiltinDefinitionLuaSrc;
|
||||
|
||||
result += kBuiltinDefinitionBit32Src;
|
||||
result += kBuiltinDefinitionMathSrc;
|
||||
result += kBuiltinDefinitionOsSrc;
|
||||
result += kBuiltinDefinitionCoroutineSrc;
|
||||
result += kBuiltinDefinitionTableSrc;
|
||||
result += kBuiltinDefinitionDebugSrc;
|
||||
result += kBuiltinDefinitionUtf8Src;
|
||||
result += kBuiltinDefinitionBufferSrc;
|
||||
result += kBuiltinDefinitionVectorSrc;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO: split into separate tagged unions when the new solver can appropriately handle that.
|
||||
static const std::string kBuiltinDefinitionTypeMethodSrc = R"BUILTIN_SRC(
|
||||
|
||||
export type type = {
|
||||
tag: "nil" | "unknown" | "never" | "any" | "boolean" | "number" | "string" | "buffer" | "thread" |
|
||||
"singleton" | "negation" | "union" | "intersection" | "table" | "function" | "class" | "generic",
|
||||
|
||||
is: (self: type, arg: string) -> boolean,
|
||||
|
||||
-- for singleton type
|
||||
value: (self: type) -> (string | boolean | nil),
|
||||
|
||||
-- for negation type
|
||||
inner: (self: type) -> type,
|
||||
|
||||
-- for union and intersection types
|
||||
components: (self: type) -> {type},
|
||||
|
||||
-- for table type
|
||||
setproperty: (self: type, key: type, value: type?) -> (),
|
||||
setreadproperty: (self: type, key: type, value: type?) -> (),
|
||||
setwriteproperty: (self: type, key: type, value: type?) -> (),
|
||||
readproperty: (self: type, key: type) -> type?,
|
||||
writeproperty: (self: type, key: type) -> type?,
|
||||
properties: (self: type) -> { [type]: { read: type?, write: type? } },
|
||||
setindexer: (self: type, index: type, result: type) -> (),
|
||||
setreadindexer: (self: type, index: type, result: type) -> (),
|
||||
setwriteindexer: (self: type, index: type, result: type) -> (),
|
||||
indexer: (self: type) -> { index: type, readresult: type, writeresult: type }?,
|
||||
readindexer: (self: type) -> { index: type, result: type }?,
|
||||
writeindexer: (self: type) -> { index: type, result: type }?,
|
||||
setmetatable: (self: type, arg: type) -> (),
|
||||
metatable: (self: type) -> type?,
|
||||
|
||||
-- for function type
|
||||
setparameters: (self: type, head: {type}?, tail: type?) -> (),
|
||||
parameters: (self: type) -> { head: {type}?, tail: type? },
|
||||
setreturns: (self: type, head: {type}?, tail: type? ) -> (),
|
||||
returns: (self: type) -> { head: {type}?, tail: type? },
|
||||
setgenerics: (self: type, {type}?) -> (),
|
||||
generics: (self: type) -> {type},
|
||||
|
||||
-- for class type
|
||||
-- 'properties', 'metatable', 'indexer', 'readindexer' and 'writeindexer' are shared with table type
|
||||
readparent: (self: type) -> type?,
|
||||
writeparent: (self: type) -> type?,
|
||||
|
||||
-- for generic type
|
||||
name: (self: type) -> string?,
|
||||
ispack: (self: type) -> boolean,
|
||||
}
|
||||
|
||||
)BUILTIN_SRC";
|
||||
|
||||
static const std::string kBuiltinDefinitionTypesLibSrc = R"BUILTIN_SRC(
|
||||
|
||||
declare types: {
|
||||
unknown: type,
|
||||
never: type,
|
||||
any: type,
|
||||
boolean: type,
|
||||
number: type,
|
||||
string: type,
|
||||
thread: type,
|
||||
buffer: type,
|
||||
|
||||
singleton: @checked (arg: string | boolean | nil) -> type,
|
||||
generic: @checked (name: string, ispack: boolean?) -> type,
|
||||
negationof: @checked (arg: type) -> type,
|
||||
unionof: @checked (...type) -> type,
|
||||
intersectionof: @checked (...type) -> type,
|
||||
newtable: @checked (props: {[type]: type} | {[type]: { read: type, write: type } } | nil, indexer: { index: type, readresult: type, writeresult: type }?, metatable: type?) -> type,
|
||||
newfunction: @checked (parameters: { head: {type}?, tail: type? }?, returns: { head: {type}?, tail: type? }?, generics: {type}?) -> type,
|
||||
copy: @checked (arg: type) -> type,
|
||||
}
|
||||
)BUILTIN_SRC";
|
||||
|
||||
static const std::string kBuiltinDefinitionTypesLibWithOptionalSrc = R"BUILTIN_SRC(
|
||||
|
||||
declare types: {
|
||||
unknown: type,
|
||||
never: type,
|
||||
any: type,
|
||||
boolean: type,
|
||||
number: type,
|
||||
string: type,
|
||||
thread: type,
|
||||
buffer: type,
|
||||
|
||||
singleton: @checked (arg: string | boolean | nil) -> type,
|
||||
optional: @checked (arg: type) -> type,
|
||||
generic: @checked (name: string, ispack: boolean?) -> type,
|
||||
negationof: @checked (arg: type) -> type,
|
||||
unionof: @checked (...type) -> type,
|
||||
intersectionof: @checked (...type) -> type,
|
||||
newtable: @checked (props: {[type]: type} | {[type]: { read: type, write: type } } | nil, indexer: { index: type, readresult: type, writeresult: type }?, metatable: type?) -> type,
|
||||
newfunction: @checked (parameters: { head: {type}?, tail: type? }?, returns: { head: {type}?, tail: type? }?, generics: {type}?) -> type,
|
||||
copy: @checked (arg: type) -> type,
|
||||
}
|
||||
)BUILTIN_SRC";
|
||||
|
||||
|
||||
std::string getTypeFunctionDefinitionSource()
|
||||
{
|
||||
|
||||
std::string result = kBuiltinDefinitionTypeMethodSrc;
|
||||
|
||||
if (FFlag::LuauTypeFunOptional)
|
||||
result += kBuiltinDefinitionTypesLibWithOptionalSrc;
|
||||
else
|
||||
result += kBuiltinDefinitionTypesLibSrc;
|
||||
// Annotates each non generic function as checked
|
||||
if (FFlag::LuauCheckedEmbeddedDefinitions2 && FFlag::LuauAttributeSyntax)
|
||||
result = kBuiltinDefinitionLuaSrcChecked;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -8,7 +8,6 @@
|
|||
#include "Luau/StringUtils.h"
|
||||
#include "Luau/ToString.h"
|
||||
#include "Luau/Type.h"
|
||||
#include "Luau/TypeChecker2.h"
|
||||
#include "Luau/TypeFunction.h"
|
||||
|
||||
#include <optional>
|
||||
|
@ -18,15 +17,11 @@
|
|||
#include <unordered_set>
|
||||
|
||||
LUAU_FASTINTVARIABLE(LuauIndentTypeMismatchMaxTypeLength, 10)
|
||||
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||
|
||||
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauImproveNonFunctionCallError, false)
|
||||
|
||||
static std::string wrongNumberOfArgsString(
|
||||
size_t expectedCount,
|
||||
std::optional<size_t> maximumCount,
|
||||
size_t actualCount,
|
||||
const char* argPrefix = nullptr,
|
||||
bool isVariadic = false
|
||||
)
|
||||
size_t expectedCount, std::optional<size_t> maximumCount, size_t actualCount, const char* argPrefix = nullptr, bool isVariadic = false)
|
||||
{
|
||||
std::string s = "expects ";
|
||||
|
||||
|
@ -70,43 +65,15 @@ namespace Luau
|
|||
{
|
||||
|
||||
// this list of binary operator type functions is used for better stringification of type functions errors
|
||||
static const std::unordered_map<std::string, const char*> DEPRECATED_kBinaryOps{
|
||||
{"add", "+"},
|
||||
{"sub", "-"},
|
||||
{"mul", "*"},
|
||||
{"div", "/"},
|
||||
{"idiv", "//"},
|
||||
{"pow", "^"},
|
||||
{"mod", "%"},
|
||||
{"concat", ".."},
|
||||
{"and", "and"},
|
||||
{"or", "or"},
|
||||
{"lt", "< or >="},
|
||||
{"le", "<= or >"},
|
||||
{"eq", "== or ~="}
|
||||
};
|
||||
|
||||
static const std::unordered_map<std::string, const char*> kBinaryOps{
|
||||
{"add", "+"},
|
||||
{"sub", "-"},
|
||||
{"mul", "*"},
|
||||
{"div", "/"},
|
||||
{"idiv", "//"},
|
||||
{"pow", "^"},
|
||||
{"mod", "%"},
|
||||
{"concat", ".."},
|
||||
{"lt", "< or >="},
|
||||
{"le", "<= or >"},
|
||||
{"eq", "== or ~="}
|
||||
};
|
||||
static const std::unordered_map<std::string, const char*> kBinaryOps{{"add", "+"}, {"sub", "-"}, {"mul", "*"}, {"div", "/"}, {"idiv", "//"},
|
||||
{"pow", "^"}, {"mod", "%"}, {"concat", ".."}, {"and", "and"}, {"or", "or"}, {"lt", "< or >="}, {"le", "<= or >"}, {"eq", "== or ~="}};
|
||||
|
||||
// this list of unary operator type functions is used for better stringification of type functions errors
|
||||
static const std::unordered_map<std::string, const char*> kUnaryOps{{"unm", "-"}, {"len", "#"}, {"not", "not"}};
|
||||
|
||||
// this list of type functions will receive a special error indicating that the user should file a bug on the GitHub repository
|
||||
// putting a type function in this list indicates that it is expected to _always_ reduce
|
||||
static const std::unordered_set<std::string> DEPRECATED_kUnreachableTypeFunctions{"refine", "singleton", "union", "intersect"};
|
||||
static const std::unordered_set<std::string> kUnreachableTypeFunctions{"refine", "singleton", "union", "intersect", "and", "or"};
|
||||
static const std::unordered_set<std::string> kUnreachableTypeFunctions{"refine", "singleton", "union", "intersect"};
|
||||
|
||||
struct ErrorConverter
|
||||
{
|
||||
|
@ -119,24 +86,18 @@ struct ErrorConverter
|
|||
|
||||
std::string result;
|
||||
|
||||
auto quote = [&](std::string s)
|
||||
{
|
||||
auto quote = [&](std::string s) {
|
||||
return "'" + s + "'";
|
||||
};
|
||||
|
||||
auto constructErrorMessage =
|
||||
[&](std::string givenType, std::string wantedType, std::optional<std::string> givenModule, std::optional<std::string> wantedModule
|
||||
) -> std::string
|
||||
{
|
||||
auto constructErrorMessage = [&](std::string givenType, std::string wantedType, std::optional<std::string> givenModule,
|
||||
std::optional<std::string> wantedModule) -> std::string {
|
||||
std::string given = givenModule ? quote(givenType) + " from " + quote(*givenModule) : quote(givenType);
|
||||
std::string wanted = wantedModule ? quote(wantedType) + " from " + quote(*wantedModule) : quote(wantedType);
|
||||
size_t luauIndentTypeMismatchMaxTypeLength = size_t(FInt::LuauIndentTypeMismatchMaxTypeLength);
|
||||
if (givenType.length() <= luauIndentTypeMismatchMaxTypeLength || wantedType.length() <= luauIndentTypeMismatchMaxTypeLength)
|
||||
return "Type " + given + " could not be converted into " + wanted;
|
||||
if (FFlag::LuauImproveTypePathsInErrors)
|
||||
return "Type\n\t" + given + "\ncould not be converted into\n\t" + wanted;
|
||||
else
|
||||
return "Type\n " + given + "\ncould not be converted into\n " + wanted;
|
||||
return "Type\n " + given + "\ncould not be converted into\n " + wanted;
|
||||
};
|
||||
|
||||
if (givenTypeName == wantedTypeName)
|
||||
|
@ -203,7 +164,7 @@ struct ErrorConverter
|
|||
TypeId t = follow(e.table);
|
||||
if (get<TableType>(t))
|
||||
return "Key '" + e.key + "' not found in table '" + Luau::toString(t) + "'";
|
||||
else if (get<ExternType>(t))
|
||||
else if (get<ClassType>(t))
|
||||
return "Key '" + e.key + "' not found in class '" + Luau::toString(t) + "'";
|
||||
else
|
||||
return "Type '" + Luau::toString(e.table) + "' does not have key '" + e.key + "'";
|
||||
|
@ -371,7 +332,7 @@ struct ErrorConverter
|
|||
std::string s = "Key '" + e.key + "' not found in ";
|
||||
|
||||
TypeId t = follow(e.table);
|
||||
if (get<ExternType>(t))
|
||||
if (get<ClassType>(t))
|
||||
s += "class";
|
||||
else
|
||||
s += "table";
|
||||
|
@ -390,11 +351,6 @@ struct ErrorConverter
|
|||
return e.message;
|
||||
}
|
||||
|
||||
std::string operator()(const Luau::ConstraintSolvingIncompleteError& e) const
|
||||
{
|
||||
return "Type inference failed to complete, you may see some confusing types and type errors.";
|
||||
}
|
||||
|
||||
std::optional<TypeId> findCallMetamethod(TypeId type) const
|
||||
{
|
||||
type = follow(type);
|
||||
|
@ -402,8 +358,8 @@ struct ErrorConverter
|
|||
std::optional<TypeId> metatable;
|
||||
if (const MetatableType* mtType = get<MetatableType>(type))
|
||||
metatable = mtType->metatable;
|
||||
else if (const ExternType* externType = get<ExternType>(type))
|
||||
metatable = externType->metatable;
|
||||
else if (const ClassType* classType = get<ClassType>(type))
|
||||
metatable = classType->metatable;
|
||||
|
||||
if (!metatable)
|
||||
return std::nullopt;
|
||||
|
@ -426,30 +382,35 @@ struct ErrorConverter
|
|||
|
||||
std::string operator()(const Luau::CannotCallNonFunction& e) const
|
||||
{
|
||||
if (auto unionTy = get<UnionType>(follow(e.ty)))
|
||||
if (DFFlag::LuauImproveNonFunctionCallError)
|
||||
{
|
||||
std::string err = "Cannot call a value of the union type:";
|
||||
|
||||
for (auto option : unionTy)
|
||||
if (auto unionTy = get<UnionType>(follow(e.ty)))
|
||||
{
|
||||
option = follow(option);
|
||||
std::string err = "Cannot call a value of the union type:";
|
||||
|
||||
if (get<FunctionType>(option) || findCallMetamethod(option))
|
||||
for (auto option : unionTy)
|
||||
{
|
||||
err += "\n | " + toString(option);
|
||||
continue;
|
||||
option = follow(option);
|
||||
|
||||
if (get<FunctionType>(option) || findCallMetamethod(option))
|
||||
{
|
||||
err += "\n | " + toString(option);
|
||||
continue;
|
||||
}
|
||||
|
||||
// early-exit if we find something that isn't callable in the union.
|
||||
return "Cannot call a value of type " + toString(option) + " in union:\n " + toString(e.ty);
|
||||
}
|
||||
|
||||
// early-exit if we find something that isn't callable in the union.
|
||||
return "Cannot call a value of type " + toString(option) + " in union:\n " + toString(e.ty);
|
||||
err += "\nWe are unable to determine the appropriate result type for such a call.";
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
err += "\nWe are unable to determine the appropriate result type for such a call.";
|
||||
|
||||
return err;
|
||||
return "Cannot call a value of type " + toString(e.ty);
|
||||
}
|
||||
|
||||
return "Cannot call a value of type " + toString(e.ty);
|
||||
return "Cannot call non-function " + toString(e.ty);
|
||||
}
|
||||
std::string operator()(const Luau::ExtraInformation& e) const
|
||||
{
|
||||
|
@ -611,7 +572,7 @@ struct ErrorConverter
|
|||
return ss;
|
||||
}
|
||||
|
||||
std::string operator()(const DynamicPropertyLookupOnExternTypesUnsafe& e) const
|
||||
std::string operator()(const DynamicPropertyLookupOnClassesUnsafe& e) const
|
||||
{
|
||||
return "Attempting a dynamic property access on type '" + Luau::toString(e.ty) + "' is unsafe and may cause exceptions at runtime";
|
||||
}
|
||||
|
@ -621,10 +582,10 @@ struct ErrorConverter
|
|||
auto tfit = get<TypeFunctionInstanceType>(e.ty);
|
||||
LUAU_ASSERT(tfit); // Luau analysis has actually done something wrong if this type is not a type function.
|
||||
if (!tfit)
|
||||
return "Internal error: Unexpected type " + Luau::toString(e.ty) + " flagged as an uninhabited type function.";
|
||||
return "Unexpected type " + Luau::toString(e.ty) + " flagged as an uninhabited type function.";
|
||||
|
||||
// unary operators
|
||||
if (auto unaryString = kUnaryOps.find(tfit->function->name); unaryString != kUnaryOps.end())
|
||||
if (auto unaryString = kUnaryOps.find(tfit->family->name); unaryString != kUnaryOps.end())
|
||||
{
|
||||
std::string result = "Operator '" + std::string(unaryString->second) + "' could not be applied to ";
|
||||
|
||||
|
@ -632,8 +593,8 @@ struct ErrorConverter
|
|||
{
|
||||
result += "operand of type " + Luau::toString(tfit->typeArguments[0]);
|
||||
|
||||
if (tfit->function->name != "not")
|
||||
result += "; there is no corresponding overload for __" + tfit->function->name;
|
||||
if (tfit->family->name != "not")
|
||||
result += "; there is no corresponding overload for __" + tfit->family->name;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -658,8 +619,7 @@ struct ErrorConverter
|
|||
}
|
||||
|
||||
// binary operators
|
||||
const auto binaryOps = FFlag::DebugLuauGreedyGeneralization ? kBinaryOps : DEPRECATED_kBinaryOps;
|
||||
if (auto binaryString = binaryOps.find(tfit->function->name); binaryString != binaryOps.end())
|
||||
if (auto binaryString = kBinaryOps.find(tfit->family->name); binaryString != kBinaryOps.end())
|
||||
{
|
||||
std::string result = "Operator '" + std::string(binaryString->second) + "' could not be applied to operands of types ";
|
||||
|
||||
|
@ -686,14 +646,14 @@ struct ErrorConverter
|
|||
result += ", " + Luau::toString(packArg);
|
||||
}
|
||||
|
||||
result += "; there is no corresponding overload for __" + tfit->function->name;
|
||||
result += "; there is no corresponding overload for __" + tfit->family->name;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// miscellaneous
|
||||
|
||||
if ("keyof" == tfit->function->name || "rawkeyof" == tfit->function->name)
|
||||
if ("keyof" == tfit->family->name || "rawkeyof" == tfit->family->name)
|
||||
{
|
||||
if (tfit->typeArguments.size() == 1 && tfit->packArguments.empty())
|
||||
return "Type '" + toString(tfit->typeArguments[0]) + "' does not have keys, so '" + Luau::toString(e.ty) + "' is invalid";
|
||||
|
@ -701,22 +661,22 @@ struct ErrorConverter
|
|||
return "Type function instance " + Luau::toString(e.ty) + " is ill-formed, and thus invalid";
|
||||
}
|
||||
|
||||
if ("index" == tfit->function->name || "rawget" == tfit->function->name)
|
||||
if ("index" == tfit->family->name || "rawget" == tfit->family->name)
|
||||
{
|
||||
if (tfit->typeArguments.size() != 2)
|
||||
return "Type function instance " + Luau::toString(e.ty) + " is ill-formed, and thus invalid";
|
||||
|
||||
if (auto errType = get<ErrorType>(tfit->typeArguments[1])) // Second argument to (index | rawget)<_,_> is not a type
|
||||
return "Second argument to " + tfit->function->name + "<" + Luau::toString(tfit->typeArguments[0]) + ", _> is not a valid index type";
|
||||
return "Second argument to " + tfit->family->name + "<" + Luau::toString(tfit->typeArguments[0]) + ", _> is not a valid index type";
|
||||
else // Property `indexer` does not exist on type `indexee`
|
||||
return "Property '" + Luau::toString(tfit->typeArguments[1]) + "' does not exist on type '" + Luau::toString(tfit->typeArguments[0]) +
|
||||
"'";
|
||||
}
|
||||
|
||||
if ((FFlag::DebugLuauGreedyGeneralization ? kUnreachableTypeFunctions : DEPRECATED_kUnreachableTypeFunctions).count(tfit->function->name))
|
||||
if (kUnreachableTypeFunctions.count(tfit->family->name))
|
||||
{
|
||||
return "Type function instance " + Luau::toString(e.ty) + " is uninhabited\n" +
|
||||
"This is likely to be a bug, please report it at https://github.com/luau-lang/luau/issues";
|
||||
"This is likely to be a bug, please report it at https://github.com/luau-lang/luau/issues";
|
||||
}
|
||||
|
||||
// Everything should be specialized above to report a more descriptive error that hopefully does not mention "type functions" explicitly.
|
||||
|
@ -746,7 +706,7 @@ struct ErrorConverter
|
|||
|
||||
std::string operator()(const UninhabitedTypePackFunction& e) const
|
||||
{
|
||||
return "Type pack function instance " + Luau::toString(e.tp) + " is uninhabited";
|
||||
return "Type pack family instance " + Luau::toString(e.tp) + " is uninhabited";
|
||||
}
|
||||
|
||||
std::string operator()(const WhereClauseNeeded& e) const
|
||||
|
@ -758,7 +718,7 @@ struct ErrorConverter
|
|||
|
||||
std::string operator()(const PackWhereClauseNeeded& e) const
|
||||
{
|
||||
return "Type pack function instance " + Luau::toString(e.tp) +
|
||||
return "Type pack family instance " + Luau::toString(e.tp) +
|
||||
" depends on generic function parameters but does not appear in the function signature; this construct cannot be type-checked at this "
|
||||
"time";
|
||||
}
|
||||
|
@ -772,15 +732,8 @@ struct ErrorConverter
|
|||
|
||||
std::string operator()(const NonStrictFunctionDefinitionError& e) const
|
||||
{
|
||||
if (e.functionName.empty())
|
||||
{
|
||||
return "Argument " + e.argument + " with type '" + toString(e.argumentType) + "' is used in a way that will run time error";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Argument " + e.argument + " with type '" + toString(e.argumentType) + "' in function '" + e.functionName +
|
||||
"' is used in a way that will run time error";
|
||||
}
|
||||
return "Argument " + e.argument + " with type '" + toString(e.argumentType) + "' in function '" + e.functionName +
|
||||
"' is used in a way that will run time error";
|
||||
}
|
||||
|
||||
std::string operator()(const PropertyAccessViolation& e) const
|
||||
|
@ -814,16 +767,6 @@ struct ErrorConverter
|
|||
return "Encountered an unexpected type pack in subtyping: " + toString(e.tp);
|
||||
}
|
||||
|
||||
std::string operator()(const UserDefinedTypeFunctionError& e) const
|
||||
{
|
||||
return e.message;
|
||||
}
|
||||
|
||||
std::string operator()(const ReservedIdentifier& e) const
|
||||
{
|
||||
return e.name + " cannot be used as an identifier for a type function or alias";
|
||||
}
|
||||
|
||||
std::string operator()(const CannotAssignToNever& e) const
|
||||
{
|
||||
std::string result = "Cannot assign a value of type " + toString(e.rhsType) + " to a field of type never";
|
||||
|
@ -1044,11 +987,6 @@ bool InternalError::operator==(const InternalError& rhs) const
|
|||
return message == rhs.message;
|
||||
}
|
||||
|
||||
bool ConstraintSolvingIncompleteError::operator==(const ConstraintSolvingIncompleteError& rhs) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CannotCallNonFunction::operator==(const CannotCallNonFunction& rhs) const
|
||||
{
|
||||
return ty == rhs.ty;
|
||||
|
@ -1149,7 +1087,7 @@ bool TypePackMismatch::operator==(const TypePackMismatch& rhs) const
|
|||
return *wantedTp == *rhs.wantedTp && *givenTp == *rhs.givenTp;
|
||||
}
|
||||
|
||||
bool DynamicPropertyLookupOnExternTypesUnsafe::operator==(const DynamicPropertyLookupOnExternTypesUnsafe& rhs) const
|
||||
bool DynamicPropertyLookupOnClassesUnsafe::operator==(const DynamicPropertyLookupOnClassesUnsafe& rhs) const
|
||||
{
|
||||
return ty == rhs.ty;
|
||||
}
|
||||
|
@ -1206,16 +1144,6 @@ bool UnexpectedTypePackInSubtyping::operator==(const UnexpectedTypePackInSubtypi
|
|||
return tp == rhs.tp;
|
||||
}
|
||||
|
||||
bool UserDefinedTypeFunctionError::operator==(const UserDefinedTypeFunctionError& rhs) const
|
||||
{
|
||||
return message == rhs.message;
|
||||
}
|
||||
|
||||
bool ReservedIdentifier::operator==(const ReservedIdentifier& rhs) const
|
||||
{
|
||||
return name == rhs.name;
|
||||
}
|
||||
|
||||
bool CannotAssignToNever::operator==(const CannotAssignToNever& rhs) const
|
||||
{
|
||||
if (cause.size() != rhs.cause.size())
|
||||
|
@ -1249,13 +1177,11 @@ bool containsParseErrorName(const TypeError& error)
|
|||
template<typename T>
|
||||
void copyError(T& e, TypeArena& destArena, CloneState& cloneState)
|
||||
{
|
||||
auto clone = [&](auto&& ty)
|
||||
{
|
||||
auto clone = [&](auto&& ty) {
|
||||
return ::Luau::clone(ty, destArena, cloneState);
|
||||
};
|
||||
|
||||
auto visitErrorData = [&](auto&& e)
|
||||
{
|
||||
auto visitErrorData = [&](auto&& e) {
|
||||
copyError(e, destArena, cloneState);
|
||||
};
|
||||
|
||||
|
@ -1330,9 +1256,6 @@ void copyError(T& e, TypeArena& destArena, CloneState& cloneState)
|
|||
else if constexpr (std::is_same_v<T, InternalError>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, ConstraintSolvingIncompleteError>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, CannotCallNonFunction>)
|
||||
{
|
||||
e.ty = clone(e.ty);
|
||||
|
@ -1391,7 +1314,7 @@ void copyError(T& e, TypeArena& destArena, CloneState& cloneState)
|
|||
e.wantedTp = clone(e.wantedTp);
|
||||
e.givenTp = clone(e.givenTp);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, DynamicPropertyLookupOnExternTypesUnsafe>)
|
||||
else if constexpr (std::is_same_v<T, DynamicPropertyLookupOnClassesUnsafe>)
|
||||
e.ty = clone(e.ty);
|
||||
else if constexpr (std::is_same_v<T, UninhabitedTypeFunction>)
|
||||
e.ty = clone(e.ty);
|
||||
|
@ -1425,9 +1348,6 @@ void copyError(T& e, TypeArena& destArena, CloneState& cloneState)
|
|||
e.ty = clone(e.ty);
|
||||
else if constexpr (std::is_same_v<T, UnexpectedTypePackInSubtyping>)
|
||||
e.tp = clone(e.tp);
|
||||
else if constexpr (std::is_same_v<T, UserDefinedTypeFunctionError>)
|
||||
{
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, CannotAssignToNever>)
|
||||
{
|
||||
e.rhsType = clone(e.rhsType);
|
||||
|
@ -1435,9 +1355,6 @@ void copyError(T& e, TypeArena& destArena, CloneState& cloneState)
|
|||
for (auto& ty : e.cause)
|
||||
ty = clone(ty);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, ReservedIdentifier>)
|
||||
{
|
||||
}
|
||||
else
|
||||
static_assert(always_false_v<T>, "Non-exhaustive type switch");
|
||||
}
|
||||
|
@ -1446,8 +1363,7 @@ void copyErrors(ErrorVec& errors, TypeArena& destArena, NotNull<BuiltinTypes> bu
|
|||
{
|
||||
CloneState cloneState{builtinTypes};
|
||||
|
||||
auto visitErrorData = [&](auto&& e)
|
||||
{
|
||||
auto visitErrorData = [&](auto&& e) {
|
||||
copyError(e, destArena, cloneState);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,160 +0,0 @@
|
|||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#include "Luau/FileResolver.h"
|
||||
|
||||
#include "Luau/Common.h"
|
||||
#include "Luau/StringUtils.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
static std::optional<RequireSuggestions> processRequireSuggestions(std::optional<RequireSuggestions> suggestions)
|
||||
{
|
||||
if (!suggestions)
|
||||
return suggestions;
|
||||
|
||||
for (RequireSuggestion& suggestion : *suggestions)
|
||||
{
|
||||
suggestion.fullPath = escape(suggestion.fullPath);
|
||||
}
|
||||
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
static RequireSuggestions makeSuggestionsFromAliases(std::vector<RequireAlias> aliases)
|
||||
{
|
||||
RequireSuggestions result;
|
||||
for (RequireAlias& alias : aliases)
|
||||
{
|
||||
RequireSuggestion suggestion;
|
||||
suggestion.label = "@" + std::move(alias.alias);
|
||||
suggestion.fullPath = suggestion.label;
|
||||
suggestion.tags = std::move(alias.tags);
|
||||
result.push_back(std::move(suggestion));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static RequireSuggestions makeSuggestionsForFirstComponent(std::unique_ptr<RequireNode> node)
|
||||
{
|
||||
RequireSuggestions result = makeSuggestionsFromAliases(node->getAvailableAliases());
|
||||
result.push_back(RequireSuggestion{"./", "./", {}});
|
||||
result.push_back(RequireSuggestion{"../", "../", {}});
|
||||
return result;
|
||||
}
|
||||
|
||||
static RequireSuggestions makeSuggestionsFromNode(std::unique_ptr<RequireNode> node, const std::string_view path, bool isPartialPath)
|
||||
{
|
||||
LUAU_ASSERT(!path.empty());
|
||||
|
||||
RequireSuggestions result;
|
||||
|
||||
const size_t lastSlashInPath = path.find_last_of('/');
|
||||
|
||||
if (lastSlashInPath != std::string_view::npos)
|
||||
{
|
||||
// Add a suggestion for the parent directory
|
||||
RequireSuggestion parentSuggestion;
|
||||
parentSuggestion.label = "..";
|
||||
|
||||
// TODO: after exposing require-by-string's path normalization API, this
|
||||
// if-else can be replaced. Instead, we can simply normalize the result
|
||||
// of inserting ".." at the end of the current path.
|
||||
if (lastSlashInPath >= 2 && path.substr(lastSlashInPath - 2, 3) == "../")
|
||||
{
|
||||
parentSuggestion.fullPath = path.substr(0, lastSlashInPath + 1);
|
||||
parentSuggestion.fullPath += "..";
|
||||
}
|
||||
else
|
||||
{
|
||||
parentSuggestion.fullPath = path.substr(0, lastSlashInPath);
|
||||
}
|
||||
|
||||
result.push_back(std::move(parentSuggestion));
|
||||
}
|
||||
|
||||
std::string fullPathPrefix;
|
||||
if (isPartialPath)
|
||||
{
|
||||
// ./path/to/chi -> ./path/to/
|
||||
fullPathPrefix += path.substr(0, lastSlashInPath + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (path.back() == '/')
|
||||
{
|
||||
// ./path/to/ -> ./path/to/
|
||||
fullPathPrefix += path;
|
||||
}
|
||||
else
|
||||
{
|
||||
// ./path/to -> ./path/to/
|
||||
fullPathPrefix += path;
|
||||
fullPathPrefix += "/";
|
||||
}
|
||||
}
|
||||
|
||||
for (const std::unique_ptr<RequireNode>& child : node->getChildren())
|
||||
{
|
||||
if (!child)
|
||||
continue;
|
||||
|
||||
std::string pathComponent = child->getPathComponent();
|
||||
|
||||
// If path component contains a slash, it cannot be required by string.
|
||||
// There's no point suggesting it.
|
||||
if (pathComponent.find('/') != std::string::npos)
|
||||
continue;
|
||||
|
||||
RequireSuggestion suggestion;
|
||||
suggestion.label = isPartialPath || path.back() == '/' ? child->getLabel() : "/" + child->getLabel();
|
||||
suggestion.fullPath = fullPathPrefix + std::move(pathComponent);
|
||||
suggestion.tags = child->getTags();
|
||||
result.push_back(std::move(suggestion));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<RequireSuggestions> RequireSuggester::getRequireSuggestionsImpl(const ModuleName& requirer, const std::optional<std::string>& path)
|
||||
const
|
||||
{
|
||||
if (!path)
|
||||
return std::nullopt;
|
||||
|
||||
std::unique_ptr<RequireNode> requirerNode = getNode(requirer);
|
||||
if (!requirerNode)
|
||||
return std::nullopt;
|
||||
|
||||
const size_t slashPos = path->find_last_of('/');
|
||||
|
||||
if (slashPos == std::string::npos)
|
||||
return makeSuggestionsForFirstComponent(std::move(requirerNode));
|
||||
|
||||
// If path already points at a Node, return the Node's children as paths.
|
||||
if (std::unique_ptr<RequireNode> node = requirerNode->resolvePathToNode(*path))
|
||||
return makeSuggestionsFromNode(std::move(node), *path, /* isPartialPath = */ false);
|
||||
|
||||
// Otherwise, recover a partial path and use this to generate suggestions.
|
||||
if (std::unique_ptr<RequireNode> partialNode = requirerNode->resolvePathToNode(path->substr(0, slashPos)))
|
||||
return makeSuggestionsFromNode(std::move(partialNode), *path, /* isPartialPath = */ true);
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<RequireSuggestions> RequireSuggester::getRequireSuggestions(const ModuleName& requirer, const std::optional<std::string>& path) const
|
||||
{
|
||||
return processRequireSuggestions(getRequireSuggestionsImpl(requirer, path));
|
||||
}
|
||||
|
||||
std::optional<RequireSuggestions> FileResolver::getRequireSuggestions(const ModuleName& requirer, const std::optional<std::string>& path) const
|
||||
{
|
||||
return requireSuggester ? requireSuggester->getRequireSuggestions(requirer, path) : std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace Luau
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -9,7 +9,6 @@ GlobalTypes::GlobalTypes(NotNull<BuiltinTypes> builtinTypes)
|
|||
: builtinTypes(builtinTypes)
|
||||
{
|
||||
globalScope = std::make_shared<Scope>(globalTypes.addTypePack(TypePackVar{FreeTypePack{TypeLevel{}}}));
|
||||
globalTypeFunctionScope = std::make_shared<Scope>(globalTypes.addTypePack(TypePackVar{FreeTypePack{TypeLevel{}}}));
|
||||
|
||||
globalScope->addBuiltinTypeBinding("any", TypeFun{{}, builtinTypes->anyType});
|
||||
globalScope->addBuiltinTypeBinding("nil", TypeFun{{}, builtinTypes->nilType});
|
||||
|
|
|
@ -1,169 +0,0 @@
|
|||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
|
||||
#include "Luau/DenseHash.h"
|
||||
#include "Luau/Polarity.h"
|
||||
#include "Luau/Scope.h"
|
||||
#include "Luau/VisitType.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
struct InferPolarity : TypeVisitor
|
||||
{
|
||||
NotNull<TypeArena> arena;
|
||||
NotNull<Scope> scope;
|
||||
|
||||
DenseHashMap<TypeId, Polarity> types{nullptr};
|
||||
DenseHashMap<TypePackId, Polarity> packs{nullptr};
|
||||
|
||||
Polarity polarity = Polarity::Positive;
|
||||
|
||||
explicit InferPolarity(NotNull<TypeArena> arena, NotNull<Scope> scope)
|
||||
: arena(arena)
|
||||
, scope(scope)
|
||||
{
|
||||
}
|
||||
|
||||
void flip()
|
||||
{
|
||||
polarity = invert(polarity);
|
||||
}
|
||||
|
||||
bool visit(TypeId ty, const GenericType& gt) override
|
||||
{
|
||||
if (ty->owningArena != arena)
|
||||
return false;
|
||||
|
||||
if (subsumes(scope, gt.scope))
|
||||
types[ty] |= polarity;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool visit(TypeId ty, const TableType& tt) override
|
||||
{
|
||||
if (ty->owningArena != arena)
|
||||
return false;
|
||||
|
||||
const Polarity p = polarity;
|
||||
for (const auto& [name, prop] : tt.props)
|
||||
{
|
||||
if (prop.isShared())
|
||||
{
|
||||
polarity = Polarity::Mixed;
|
||||
traverse(prop.type());
|
||||
}
|
||||
else if (prop.isReadOnly())
|
||||
{
|
||||
polarity = p;
|
||||
traverse(*prop.readTy);
|
||||
}
|
||||
else if (prop.isWriteOnly())
|
||||
{
|
||||
polarity = invert(p);
|
||||
traverse(*prop.writeTy);
|
||||
}
|
||||
else
|
||||
LUAU_ASSERT(!"Unreachable");
|
||||
}
|
||||
|
||||
if (tt.indexer)
|
||||
{
|
||||
polarity = Polarity::Mixed;
|
||||
traverse(tt.indexer->indexType);
|
||||
traverse(tt.indexer->indexResultType);
|
||||
}
|
||||
|
||||
polarity = p;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool visit(TypeId ty, const FunctionType& ft) override
|
||||
{
|
||||
if (ty->owningArena != arena)
|
||||
return false;
|
||||
|
||||
const Polarity p = polarity;
|
||||
|
||||
polarity = Polarity::Positive;
|
||||
|
||||
// If these types actually occur within the function signature, their
|
||||
// polarity will be overwritten. If not, we infer that they are phantom
|
||||
// types.
|
||||
for (TypeId generic : ft.generics)
|
||||
{
|
||||
generic = follow(generic);
|
||||
const auto gen = get<GenericType>(generic);
|
||||
if (gen && subsumes(scope, gen->scope))
|
||||
types[generic] = Polarity::None;
|
||||
}
|
||||
for (TypePackId genericPack : ft.genericPacks)
|
||||
{
|
||||
genericPack = follow(genericPack);
|
||||
const auto gen = get<GenericTypePack>(genericPack);
|
||||
if (gen && subsumes(scope, gen->scope))
|
||||
packs[genericPack] = Polarity::None;
|
||||
}
|
||||
|
||||
flip();
|
||||
traverse(ft.argTypes);
|
||||
flip();
|
||||
traverse(ft.retTypes);
|
||||
|
||||
polarity = p;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool visit(TypeId, const ExternType&) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool visit(TypePackId tp, const GenericTypePack& gtp) override
|
||||
{
|
||||
packs[tp] |= polarity;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TID>
|
||||
static void inferGenericPolarities_(NotNull<TypeArena> arena, NotNull<Scope> scope, TID ty)
|
||||
{
|
||||
if (!FFlag::LuauNonReentrantGeneralization2)
|
||||
return;
|
||||
|
||||
InferPolarity infer{arena, scope};
|
||||
infer.traverse(ty);
|
||||
|
||||
for (const auto& [ty, polarity] : infer.types)
|
||||
{
|
||||
auto gt = getMutable<GenericType>(ty);
|
||||
LUAU_ASSERT(gt);
|
||||
gt->polarity = polarity;
|
||||
}
|
||||
|
||||
for (const auto& [tp, polarity] : infer.packs)
|
||||
{
|
||||
if (tp->owningArena != arena)
|
||||
continue;
|
||||
auto gp = getMutable<GenericTypePack>(tp);
|
||||
LUAU_ASSERT(gp);
|
||||
gp->polarity = polarity;
|
||||
}
|
||||
}
|
||||
|
||||
void inferGenericPolarities(NotNull<TypeArena> arena, NotNull<Scope> scope, TypeId ty)
|
||||
{
|
||||
inferGenericPolarities_(arena, scope, ty);
|
||||
}
|
||||
|
||||
void inferGenericPolarities(NotNull<TypeArena> arena, NotNull<Scope> scope, TypePackId tp)
|
||||
{
|
||||
inferGenericPolarities_(arena, scope, tp);
|
||||
}
|
||||
|
||||
} // namespace Luau
|
|
@ -10,13 +10,16 @@
|
|||
|
||||
#include <algorithm>
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
|
||||
LUAU_FASTFLAG(LuauReusableSubstitutions)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
void Instantiation::resetState(const TxnLog* log, TypeArena* arena, NotNull<BuiltinTypes> builtinTypes, TypeLevel level, Scope* scope)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauReusableSubstitutions);
|
||||
|
||||
Substitution::resetState(log, arena);
|
||||
|
||||
this->builtinTypes = builtinTypes;
|
||||
|
@ -49,7 +52,7 @@ bool Instantiation::ignoreChildren(TypeId ty)
|
|||
{
|
||||
if (log->getMutable<FunctionType>(ty))
|
||||
return true;
|
||||
else if (get<ExternType>(ty))
|
||||
else if (get<ClassType>(ty))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
|
@ -60,19 +63,34 @@ TypeId Instantiation::clean(TypeId ty)
|
|||
const FunctionType* ftv = log->getMutable<FunctionType>(ty);
|
||||
LUAU_ASSERT(ftv);
|
||||
|
||||
FunctionType clone = FunctionType{level, ftv->argTypes, ftv->retTypes, ftv->definition, ftv->hasSelf};
|
||||
clone.magic = ftv->magic;
|
||||
FunctionType clone = FunctionType{level, scope, ftv->argTypes, ftv->retTypes, ftv->definition, ftv->hasSelf};
|
||||
clone.magicFunction = ftv->magicFunction;
|
||||
clone.dcrMagicFunction = ftv->dcrMagicFunction;
|
||||
clone.dcrMagicRefinement = ftv->dcrMagicRefinement;
|
||||
clone.tags = ftv->tags;
|
||||
clone.argNames = ftv->argNames;
|
||||
TypeId result = addType(std::move(clone));
|
||||
|
||||
// Annoyingly, we have to do this even if there are no generics,
|
||||
// to replace any generic tables.
|
||||
reusableReplaceGenerics.resetState(log, arena, builtinTypes, level, scope, ftv->generics, ftv->genericPacks);
|
||||
if (FFlag::LuauReusableSubstitutions)
|
||||
{
|
||||
// Annoyingly, we have to do this even if there are no generics,
|
||||
// to replace any generic tables.
|
||||
reusableReplaceGenerics.resetState(log, arena, builtinTypes, level, scope, ftv->generics, ftv->genericPacks);
|
||||
|
||||
// TODO: What to do if this returns nullopt?
|
||||
// We don't have access to the error-reporting machinery
|
||||
result = reusableReplaceGenerics.substitute(result).value_or(result);
|
||||
// TODO: What to do if this returns nullopt?
|
||||
// We don't have access to the error-reporting machinery
|
||||
result = reusableReplaceGenerics.substitute(result).value_or(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Annoyingly, we have to do this even if there are no generics,
|
||||
// to replace any generic tables.
|
||||
ReplaceGenerics replaceGenerics{log, arena, builtinTypes, level, scope, ftv->generics, ftv->genericPacks};
|
||||
|
||||
// TODO: What to do if this returns nullopt?
|
||||
// We don't have access to the error-reporting machinery
|
||||
result = replaceGenerics.substitute(result).value_or(result);
|
||||
}
|
||||
|
||||
asMutable(result)->documentationSymbol = ty->documentationSymbol;
|
||||
return result;
|
||||
|
@ -84,16 +102,11 @@ TypePackId Instantiation::clean(TypePackId tp)
|
|||
return tp;
|
||||
}
|
||||
|
||||
void ReplaceGenerics::resetState(
|
||||
const TxnLog* log,
|
||||
TypeArena* arena,
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
TypeLevel level,
|
||||
Scope* scope,
|
||||
const std::vector<TypeId>& generics,
|
||||
const std::vector<TypePackId>& genericPacks
|
||||
)
|
||||
void ReplaceGenerics::resetState(const TxnLog* log, TypeArena* arena, NotNull<BuiltinTypes> builtinTypes, TypeLevel level, Scope* scope,
|
||||
const std::vector<TypeId>& generics, const std::vector<TypePackId>& genericPacks)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauReusableSubstitutions);
|
||||
|
||||
Substitution::resetState(log, arena);
|
||||
|
||||
this->builtinTypes = builtinTypes;
|
||||
|
@ -119,7 +132,7 @@ bool ReplaceGenerics::ignoreChildren(TypeId ty)
|
|||
// whenever we quantify, so the vectors overlap if and only if they are equal.
|
||||
return (!generics.empty() || !genericPacks.empty()) && (ftv->generics == generics) && (ftv->genericPacks == genericPacks);
|
||||
}
|
||||
else if (get<ExternType>(ty))
|
||||
else if (get<ClassType>(ty))
|
||||
return true;
|
||||
else
|
||||
{
|
||||
|
@ -155,7 +168,7 @@ TypeId ReplaceGenerics::clean(TypeId ty)
|
|||
clone.definitionLocation = ttv->definitionLocation;
|
||||
return addType(std::move(clone));
|
||||
}
|
||||
else if (FFlag::LuauSolverV2)
|
||||
else if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
TypeId res = freshType(NotNull{arena}, builtinTypes, scope);
|
||||
getMutable<FreeType>(res)->level = level;
|
||||
|
@ -163,7 +176,7 @@ TypeId ReplaceGenerics::clean(TypeId ty)
|
|||
}
|
||||
else
|
||||
{
|
||||
return arena->freshType(builtinTypes, scope, level);
|
||||
return addType(FreeType{scope, level});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -174,12 +187,7 @@ TypePackId ReplaceGenerics::clean(TypePackId tp)
|
|||
}
|
||||
|
||||
std::optional<TypeId> instantiate(
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<TypeArena> arena,
|
||||
NotNull<TypeCheckLimits> limits,
|
||||
NotNull<Scope> scope,
|
||||
TypeId ty
|
||||
)
|
||||
NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, NotNull<TypeCheckLimits> limits, NotNull<Scope> scope, TypeId ty)
|
||||
{
|
||||
ty = follow(ty);
|
||||
|
||||
|
|
|
@ -6,25 +6,8 @@ namespace Luau
|
|||
|
||||
bool Instantiation2::ignoreChildren(TypeId ty)
|
||||
{
|
||||
if (get<ExternType>(ty))
|
||||
if (get<ClassType>(ty))
|
||||
return true;
|
||||
|
||||
if (auto ftv = get<FunctionType>(ty))
|
||||
{
|
||||
if (ftv->hasNoFreeOrGenericTypes)
|
||||
return false;
|
||||
|
||||
// If this function type quantifies over these generics, we don't want substitution to
|
||||
// go any further into them because it's being shadowed in this case.
|
||||
for (auto generic : ftv->generics)
|
||||
if (genericSubstitutions.contains(generic))
|
||||
return true;
|
||||
|
||||
for (auto generic : ftv->genericPacks)
|
||||
if (genericPackSubstitutions.contains(generic))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -64,22 +47,14 @@ TypePackId Instantiation2::clean(TypePackId tp)
|
|||
}
|
||||
|
||||
std::optional<TypeId> instantiate2(
|
||||
TypeArena* arena,
|
||||
DenseHashMap<TypeId, TypeId> genericSubstitutions,
|
||||
DenseHashMap<TypePackId, TypePackId> genericPackSubstitutions,
|
||||
TypeId ty
|
||||
)
|
||||
TypeArena* arena, DenseHashMap<TypeId, TypeId> genericSubstitutions, DenseHashMap<TypePackId, TypePackId> genericPackSubstitutions, TypeId ty)
|
||||
{
|
||||
Instantiation2 instantiation{arena, std::move(genericSubstitutions), std::move(genericPackSubstitutions)};
|
||||
return instantiation.substitute(ty);
|
||||
}
|
||||
|
||||
std::optional<TypePackId> instantiate2(
|
||||
TypeArena* arena,
|
||||
DenseHashMap<TypeId, TypeId> genericSubstitutions,
|
||||
DenseHashMap<TypePackId, TypePackId> genericPackSubstitutions,
|
||||
TypePackId tp
|
||||
)
|
||||
TypeArena* arena, DenseHashMap<TypeId, TypeId> genericSubstitutions, DenseHashMap<TypePackId, TypePackId> genericPackSubstitutions, TypePackId tp)
|
||||
{
|
||||
Instantiation2 instantiation{arena, std::move(genericSubstitutions), std::move(genericPackSubstitutions)};
|
||||
return instantiation.substitute(tp);
|
||||
|
|
|
@ -114,8 +114,6 @@ static void errorToString(std::ostream& stream, const T& err)
|
|||
stream << "GenericError { " << err.message << " }";
|
||||
else if constexpr (std::is_same_v<T, InternalError>)
|
||||
stream << "InternalError { " << err.message << " }";
|
||||
else if constexpr (std::is_same_v<T, ConstraintSolvingIncompleteError>)
|
||||
stream << "ConstraintSolvingIncompleteError {}";
|
||||
else if constexpr (std::is_same_v<T, CannotCallNonFunction>)
|
||||
stream << "CannotCallNonFunction { " << toString(err.ty) << " }";
|
||||
else if constexpr (std::is_same_v<T, ExtraInformation>)
|
||||
|
@ -193,8 +191,8 @@ static void errorToString(std::ostream& stream, const T& err)
|
|||
stream << "NormalizationTooComplex { }";
|
||||
else if constexpr (std::is_same_v<T, TypePackMismatch>)
|
||||
stream << "TypePackMismatch { wanted = '" + toString(err.wantedTp) + "', given = '" + toString(err.givenTp) + "' }";
|
||||
else if constexpr (std::is_same_v<T, DynamicPropertyLookupOnExternTypesUnsafe>)
|
||||
stream << "DynamicPropertyLookupOnExternTypesUnsafe { " << toString(err.ty) << " }";
|
||||
else if constexpr (std::is_same_v<T, DynamicPropertyLookupOnClassesUnsafe>)
|
||||
stream << "DynamicPropertyLookupOnClassesUnsafe { " << toString(err.ty) << " }";
|
||||
else if constexpr (std::is_same_v<T, UninhabitedTypeFunction>)
|
||||
stream << "UninhabitedTypeFunction { " << toString(err.ty) << " }";
|
||||
else if constexpr (std::is_same_v<T, ExplicitFunctionAnnotationRecommended>)
|
||||
|
@ -227,10 +225,6 @@ static void errorToString(std::ostream& stream, const T& err)
|
|||
stream << "UnexpectedTypeInSubtyping { ty = '" + toString(err.ty) + "' }";
|
||||
else if constexpr (std::is_same_v<T, UnexpectedTypePackInSubtyping>)
|
||||
stream << "UnexpectedTypePackInSubtyping { tp = '" + toString(err.tp) + "' }";
|
||||
else if constexpr (std::is_same_v<T, UserDefinedTypeFunctionError>)
|
||||
stream << "UserDefinedTypeFunctionError { " << err.message << " }";
|
||||
else if constexpr (std::is_same_v<T, ReservedIdentifier>)
|
||||
stream << "ReservedIdentifier { " << err.name << " }";
|
||||
else if constexpr (std::is_same_v<T, CannotAssignToNever>)
|
||||
{
|
||||
stream << "CannotAssignToNever { rvalueType = '" << toString(err.rhsType) << "', reason = '" << err.reason << "', cause = { ";
|
||||
|
@ -265,8 +259,7 @@ std::ostream& operator<<(std::ostream& stream, const CannotAssignToNever::Reason
|
|||
|
||||
std::ostream& operator<<(std::ostream& stream, const TypeErrorData& data)
|
||||
{
|
||||
auto cb = [&](const auto& e)
|
||||
{
|
||||
auto cb = [&](const auto& e) {
|
||||
return errorToString(stream, e);
|
||||
};
|
||||
visit(cb, data);
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -14,58 +14,31 @@
|
|||
|
||||
#include <algorithm>
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2);
|
||||
LUAU_FASTFLAG(LuauRetainDefinitionAliasLocations)
|
||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
|
||||
LUAU_FASTFLAGVARIABLE(LuauSkipEmptyInstantiations, false);
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
static void defaultLogLuau(std::string_view context, std::string_view input)
|
||||
{
|
||||
// The default is to do nothing because we don't want to mess with
|
||||
// the xml parsing done by the dcr script.
|
||||
}
|
||||
|
||||
Luau::LogLuauProc logLuau = &defaultLogLuau;
|
||||
|
||||
void setLogLuau(LogLuauProc ll)
|
||||
{
|
||||
logLuau = ll;
|
||||
}
|
||||
|
||||
void resetLogLuauProc()
|
||||
{
|
||||
logLuau = &defaultLogLuau;
|
||||
}
|
||||
|
||||
static bool contains(Position pos, Comment comment)
|
||||
{
|
||||
if (comment.location.contains(pos))
|
||||
return true;
|
||||
else if (comment.type == Lexeme::BrokenComment && comment.location.begin <= pos) // Broken comments are broken specifically because they don't
|
||||
// have an end
|
||||
else if (comment.type == Lexeme::BrokenComment &&
|
||||
comment.location.begin <= pos) // Broken comments are broken specifically because they don't have an end
|
||||
return true;
|
||||
// comments actually span the whole line - in incremental mode, we could pass a cursor outside of the current parsed comment range span, but it
|
||||
// would still be 'within' the comment So, the cursor must be on the same line and the comment itself must come strictly after the `begin`
|
||||
else if (comment.type == Lexeme::Comment && comment.location.end.line == pos.line && comment.location.begin <= pos)
|
||||
else if (comment.type == Lexeme::Comment && comment.location.end == pos)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isWithinComment(const std::vector<Comment>& commentLocations, Position pos)
|
||||
static bool isWithinComment(const std::vector<Comment>& commentLocations, Position pos)
|
||||
{
|
||||
auto iter = std::lower_bound(
|
||||
commentLocations.begin(),
|
||||
commentLocations.end(),
|
||||
Comment{Lexeme::Comment, Location{pos, pos}},
|
||||
[](const Comment& a, const Comment& b)
|
||||
{
|
||||
if (a.type == Lexeme::Comment)
|
||||
return a.location.end.line < b.location.end.line;
|
||||
commentLocations.begin(), commentLocations.end(), Comment{Lexeme::Comment, Location{pos, pos}}, [](const Comment& a, const Comment& b) {
|
||||
return a.location.end < b.location.end;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
if (iter == commentLocations.end())
|
||||
return false;
|
||||
|
@ -144,7 +117,7 @@ struct ClonePublicInterface : Substitution
|
|||
|
||||
if (FunctionType* ftv = getMutable<FunctionType>(result))
|
||||
{
|
||||
if (ftv->generics.empty() && ftv->genericPacks.empty())
|
||||
if (FFlag::LuauSkipEmptyInstantiations && ftv->generics.empty() && ftv->genericPacks.empty())
|
||||
{
|
||||
GenericTypeFinder marker;
|
||||
marker.traverse(result);
|
||||
|
@ -158,26 +131,6 @@ struct ClonePublicInterface : Substitution
|
|||
else if (TableType* ttv = getMutable<TableType>(result))
|
||||
{
|
||||
ttv->level = TypeLevel{0, 0};
|
||||
if (FFlag::LuauSolverV2)
|
||||
ttv->scope = nullptr;
|
||||
}
|
||||
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
if (auto freety = getMutable<FreeType>(result))
|
||||
{
|
||||
module->errors.emplace_back(
|
||||
freety->scope->location,
|
||||
module->name,
|
||||
InternalError{"Free type is escaping its module; please report this bug at "
|
||||
"https://github.com/luau-lang/luau/issues"}
|
||||
);
|
||||
result = builtinTypes->errorRecoveryType();
|
||||
}
|
||||
else if (auto genericty = getMutable<GenericType>(result))
|
||||
{
|
||||
genericty->scope = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -185,27 +138,7 @@ struct ClonePublicInterface : Substitution
|
|||
|
||||
TypePackId clean(TypePackId tp) override
|
||||
{
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
auto clonedTp = clone(tp);
|
||||
if (auto ftp = getMutable<FreeTypePack>(clonedTp))
|
||||
{
|
||||
module->errors.emplace_back(
|
||||
ftp->scope->location,
|
||||
module->name,
|
||||
InternalError{"Free type pack is escaping its module; please report this bug at "
|
||||
"https://github.com/luau-lang/luau/issues"}
|
||||
);
|
||||
clonedTp = builtinTypes->errorRecoveryTypePack();
|
||||
}
|
||||
else if (auto gtp = getMutable<GenericTypePack>(clonedTp))
|
||||
gtp->scope = nullptr;
|
||||
return clonedTp;
|
||||
}
|
||||
else
|
||||
{
|
||||
return clone(tp);
|
||||
}
|
||||
return clone(tp);
|
||||
}
|
||||
|
||||
TypeId cloneType(TypeId ty)
|
||||
|
@ -265,10 +198,7 @@ struct ClonePublicInterface : Substitution
|
|||
|
||||
TypeId type = cloneType(tf.type);
|
||||
|
||||
if (FFlag::LuauRetainDefinitionAliasLocations)
|
||||
return TypeFun{typeParams, typePackParams, type, tf.definitionLocation};
|
||||
else
|
||||
return TypeFun{typeParams, typePackParams, type};
|
||||
return TypeFun{typeParams, typePackParams, type};
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -285,7 +215,7 @@ void Module::clonePublicInterface(NotNull<BuiltinTypes> builtinTypes, InternalEr
|
|||
ScopePtr moduleScope = getModuleScope();
|
||||
|
||||
TypePackId returnType = moduleScope->returnType;
|
||||
std::optional<TypePackId> varargPack = FFlag::LuauSolverV2 ? std::nullopt : moduleScope->varargPack;
|
||||
std::optional<TypePackId> varargPack = FFlag::DebugLuauDeferredConstraintResolution ? std::nullopt : moduleScope->varargPack;
|
||||
|
||||
TxnLog log;
|
||||
ClonePublicInterface clonePublicInterface{&log, builtinTypes, this};
|
||||
|
|
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue