diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 55f9db39..c7fb782a 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,5 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Questions
- url: https://github.com/Roblox/luau/discussions
+ url: https://github.com/luau-lang/luau/discussions
about: Please use GitHub Discussions if you have questions or need help.
diff --git a/.github/codecov.yml b/.github/codecov.yml
index 69cb7601..7e0dee17 100644
--- a/.github/codecov.yml
+++ b/.github/codecov.yml
@@ -1 +1,7 @@
comment: false
+coverage:
+ status:
+ patch: false
+ project:
+ default:
+ informational: true
diff --git a/.github/workflows/benchmark-dev.yml b/.github/workflows/benchmark-dev.yml
deleted file mode 100644
index b6115acc..00000000
--- a/.github/workflows/benchmark-dev.yml
+++ /dev/null
@@ -1,185 +0,0 @@
-name: benchmark-dev
-
-on:
- push:
- branches:
- - master
-
- paths-ignore:
- - "docs/**"
- - "papers/**"
- - "rfcs/**"
- - "*.md"
-
-jobs:
- windows:
- name: windows-${{matrix.arch}}
- strategy:
- fail-fast: false
- matrix:
- os: [windows-latest]
- arch: [Win32, x64]
- bench:
- - {
- script: "run-benchmarks",
- timeout: 12,
- title: "Luau Benchmarks",
- }
- benchResultsRepo:
- - { name: "luau-lang/benchmark-data", branch: "main" }
-
- runs-on: ${{ matrix.os }}
- steps:
- - name: Checkout Luau repository
- uses: actions/checkout@v3
-
- - name: Build Luau
- shell: bash # necessary for fail-fast
- run: |
- mkdir build && cd build
- cmake .. -DCMAKE_BUILD_TYPE=Release
- cmake --build . --target Luau.Repl.CLI --config Release
- cmake --build . --target Luau.Analyze.CLI --config Release
-
- - name: Move build files to root
- run: |
- move build/Release/* .
-
- - uses: actions/setup-python@v3
- with:
- python-version: "3.9"
- architecture: "x64"
-
- - name: Install python dependencies
- run: |
- python -m pip install requests
- python -m pip install --user numpy scipy matplotlib ipython jupyter pandas sympy nose
-
- - name: Run benchmark
- run: |
- python bench/bench.py | tee ${{ matrix.bench.script }}-output.txt
-
- - name: Push benchmark results
- id: pushBenchmarkAttempt1
- continue-on-error: true
- uses: ./.github/workflows/push-results
- with:
- repository: ${{ matrix.benchResultsRepo.name }}
- branch: ${{ matrix.benchResultsRepo.branch }}
- token: ${{ secrets.BENCH_GITHUB_TOKEN }}
- path: "./gh-pages"
- bench_name: "${{ matrix.bench.title }} (Windows ${{matrix.arch}})"
- bench_tool: "benchmarkluau"
- bench_output_file_path: "./${{ matrix.bench.script }}-output.txt"
- bench_external_data_json_path: "./gh-pages/dev/bench/data-${{ matrix.os }}.json"
-
- - name: Push benchmark results (Attempt 2)
- id: pushBenchmarkAttempt2
- continue-on-error: true
- if: steps.pushBenchmarkAttempt1.outcome == 'failure'
- uses: ./.github/workflows/push-results
- with:
- repository: ${{ matrix.benchResultsRepo.name }}
- branch: ${{ matrix.benchResultsRepo.branch }}
- token: ${{ secrets.BENCH_GITHUB_TOKEN }}
- path: "./gh-pages"
- bench_name: "${{ matrix.bench.title }} (Windows ${{matrix.arch}})"
- bench_tool: "benchmarkluau"
- bench_output_file_path: "./${{ matrix.bench.script }}-output.txt"
- bench_external_data_json_path: "./gh-pages/dev/bench/data-${{ matrix.os }}.json"
-
- - name: Push benchmark results (Attempt 3)
- id: pushBenchmarkAttempt3
- continue-on-error: true
- if: steps.pushBenchmarkAttempt2.outcome == 'failure'
- uses: ./.github/workflows/push-results
- with:
- repository: ${{ matrix.benchResultsRepo.name }}
- branch: ${{ matrix.benchResultsRepo.branch }}
- token: ${{ secrets.BENCH_GITHUB_TOKEN }}
- path: "./gh-pages"
- bench_name: "${{ matrix.bench.title }} (Windows ${{matrix.arch}})"
- bench_tool: "benchmarkluau"
- bench_output_file_path: "./${{ matrix.bench.script }}-output.txt"
- bench_external_data_json_path: "./gh-pages/dev/bench/data-${{ matrix.os }}.json"
-
- unix:
- name: ${{matrix.os}}
- strategy:
- fail-fast: false
- matrix:
- os: [ubuntu-20.04, macos-latest]
- bench:
- - {
- script: "run-benchmarks",
- timeout: 12,
- title: "Luau Benchmarks",
- }
- benchResultsRepo:
- - { name: "luau-lang/benchmark-data", branch: "main" }
-
- runs-on: ${{ matrix.os }}
- steps:
- - name: Checkout Luau repository
- uses: actions/checkout@v3
-
- - name: Build Luau
- run: make config=release luau luau-analyze
-
- - uses: actions/setup-python@v3
- with:
- python-version: "3.9"
- architecture: "x64"
-
- - name: Install python dependencies
- run: |
- python -m pip install requests
- python -m pip install --user numpy scipy matplotlib ipython jupyter pandas sympy nose
-
- - name: Run benchmark
- run: |
- python bench/bench.py | tee ${{ matrix.bench.script }}-output.txt
-
- - name: Push benchmark results
- id: pushBenchmarkAttempt1
- continue-on-error: true
- uses: ./.github/workflows/push-results
- with:
- repository: ${{ matrix.benchResultsRepo.name }}
- branch: ${{ matrix.benchResultsRepo.branch }}
- token: ${{ secrets.BENCH_GITHUB_TOKEN }}
- path: "./gh-pages"
- bench_name: ${{ matrix.bench.title }}
- bench_tool: "benchmarkluau"
- bench_output_file_path: "./${{ matrix.bench.script }}-output.txt"
- bench_external_data_json_path: "./gh-pages/dev/bench/data-${{ matrix.os }}.json"
-
- - name: Push benchmark results (Attempt 2)
- id: pushBenchmarkAttempt2
- continue-on-error: true
- if: steps.pushBenchmarkAttempt1.outcome == 'failure'
- uses: ./.github/workflows/push-results
- with:
- repository: ${{ matrix.benchResultsRepo.name }}
- branch: ${{ matrix.benchResultsRepo.branch }}
- token: ${{ secrets.BENCH_GITHUB_TOKEN }}
- path: "./gh-pages"
- bench_name: ${{ matrix.bench.title }}
- bench_tool: "benchmarkluau"
- bench_output_file_path: "./${{ matrix.bench.script }}-output.txt"
- bench_external_data_json_path: "./gh-pages/dev/bench/data-${{ matrix.os }}.json"
-
- - name: Push benchmark results (Attempt 3)
- id: pushBenchmarkAttempt3
- continue-on-error: true
- if: steps.pushBenchmarkAttempt2.outcome == 'failure'
- uses: ./.github/workflows/push-results
- with:
- repository: ${{ matrix.benchResultsRepo.name }}
- branch: ${{ matrix.benchResultsRepo.branch }}
- token: ${{ secrets.BENCH_GITHUB_TOKEN }}
- path: "./gh-pages"
- bench_name: ${{ matrix.bench.title }}
- bench_tool: "benchmarkluau"
- bench_output_file_path: "./${{ matrix.bench.script }}-output.txt"
- bench_external_data_json_path: "./gh-pages/dev/bench/data-${{ matrix.os }}.json"
diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml
index 7fb88e21..7a11fbe1 100644
--- a/.github/workflows/benchmark.yml
+++ b/.github/workflows/benchmark.yml
@@ -25,6 +25,7 @@ jobs:
- name: Install valgrind
run: |
+ sudo apt-get update
sudo apt-get install valgrind
- name: Build Luau (gcc)
@@ -41,7 +42,7 @@ jobs:
- name: Build Luau (clang)
run: |
make config=profile clean
- CXX=clang++ make config=profile luau luau-analyze
+ CXX=clang++ make config=profile luau luau-analyze luau-compile
- name: Run benchmark (bench-gcc)
run: |
@@ -62,22 +63,24 @@ 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=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=DebugLuauDeferredConstraintResolution bench/other/regex.lua 2>&1 | filter regex-dcr | tee -a analyze-output.txt
- name: Run benchmark (compile)
run: |
filter() {
- awk '/.*I\s+refs:\s+[0-9,]+/ {gsub(",", "", $4); X=$4} END {print "SUCCESS: '$1' : " X/1e7 "ms +/- 0% on luau --compile"}'
+ awk '/.*I\s+refs:\s+[0-9,]+/ {gsub(",", "", $4); X=$4} END {print "SUCCESS: '$1' : " X/1e7 "ms +/- 0% on luau-compile"}'
}
- valgrind --tool=callgrind ./luau --compile=null -O0 bench/other/LuauPolyfillMap.lua 2>&1 | filter map-O0 | tee -a compile-output.txt
- valgrind --tool=callgrind ./luau --compile=null -O1 bench/other/LuauPolyfillMap.lua 2>&1 | filter map-O1 | tee -a compile-output.txt
- valgrind --tool=callgrind ./luau --compile=null -O2 bench/other/LuauPolyfillMap.lua 2>&1 | filter map-O2 | tee -a compile-output.txt
- valgrind --tool=callgrind ./luau --compile=codegennull -O2 bench/other/LuauPolyfillMap.lua 2>&1 | filter map-O2-codegen | tee -a compile-output.txt
- valgrind --tool=callgrind ./luau --compile=null -O0 bench/other/regex.lua 2>&1 | filter regex-O0 | tee -a compile-output.txt
- valgrind --tool=callgrind ./luau --compile=null -O1 bench/other/regex.lua 2>&1 | filter regex-O1 | tee -a compile-output.txt
- valgrind --tool=callgrind ./luau --compile=null -O2 bench/other/regex.lua 2>&1 | filter regex-O2 | tee -a compile-output.txt
- valgrind --tool=callgrind ./luau --compile=codegennull -O2 bench/other/regex.lua 2>&1 | filter regex-O2-codegen | tee -a compile-output.txt
+ valgrind --tool=callgrind ./luau-compile --null -O0 bench/other/LuauPolyfillMap.lua 2>&1 | filter map-O0 | tee -a compile-output.txt
+ valgrind --tool=callgrind ./luau-compile --null -O1 bench/other/LuauPolyfillMap.lua 2>&1 | filter map-O1 | tee -a compile-output.txt
+ valgrind --tool=callgrind ./luau-compile --null -O2 bench/other/LuauPolyfillMap.lua 2>&1 | filter map-O2 | tee -a compile-output.txt
+ valgrind --tool=callgrind ./luau-compile --codegennull -O2 bench/other/LuauPolyfillMap.lua 2>&1 | filter map-O2-codegen | tee -a compile-output.txt
+ valgrind --tool=callgrind ./luau-compile --null -O0 bench/other/regex.lua 2>&1 | filter regex-O0 | tee -a compile-output.txt
+ valgrind --tool=callgrind ./luau-compile --null -O1 bench/other/regex.lua 2>&1 | filter regex-O1 | tee -a compile-output.txt
+ valgrind --tool=callgrind ./luau-compile --null -O2 bench/other/regex.lua 2>&1 | filter regex-O2 | tee -a compile-output.txt
+ valgrind --tool=callgrind ./luau-compile --codegennull -O2 bench/other/regex.lua 2>&1 | filter regex-O2-codegen | tee -a compile-output.txt
- name: Checkout benchmark results
uses: actions/checkout@v3
@@ -122,7 +125,7 @@ jobs:
- name: Store results (compile)
uses: Roblox/rhysd-github-action-benchmark@v-luau
with:
- name: luau --compile
+ name: luau-compile
tool: "benchmarkluau"
output-file-path: ./compile-output.txt
external-data-json-path: ./gh-pages/compile.json
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index b084a4f5..b278c437 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -42,9 +42,10 @@ jobs:
./luau-tests -ts=Conformance --codegen -O2 --fflags=true
- name: make cli
run: |
- make -j2 config=sanitize werror=1 luau luau-analyze # match config with tests to improve build time
+ make -j2 config=sanitize werror=1 luau luau-analyze luau-compile # match config with tests to improve build time
./luau tests/conformance/assert.lua
./luau-analyze tests/conformance/assert.lua
+ ./luau-compile tests/conformance/assert.lua
windows:
runs-on: windows-latest
@@ -76,9 +77,10 @@ jobs:
- name: cmake cli
shell: bash # necessary for fail-fast
run: |
- cmake --build . --target Luau.Repl.CLI Luau.Analyze.CLI --config Debug # match config with tests to improve build time
+ 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.lua
Debug/luau-analyze tests/conformance/assert.lua
+ Debug/luau-compile tests/conformance/assert.lua
coverage:
runs-on: ubuntu-20.04
diff --git a/.github/workflows/new-release.yml b/.github/workflows/new-release.yml
index 5fe7f792..52b2eb0c 100644
--- a/.github/workflows/new-release.yml
+++ b/.github/workflows/new-release.yml
@@ -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 --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*
diff --git a/.github/workflows/push-results/action.yml b/.github/workflows/push-results/action.yml
deleted file mode 100644
index b5ffebee..00000000
--- a/.github/workflows/push-results/action.yml
+++ /dev/null
@@ -1,63 +0,0 @@
-name: Checkout & push results
-description: Checkout a given repo and push results to GitHub
-inputs:
- repository:
- required: true
- type: string
- description: The benchmark results repository to check out
- branch:
- required: true
- type: string
- description: The benchmark results repository's branch to check out
- token:
- required: true
- type: string
- description: The GitHub token to use for pushing results
- path:
- required: true
- type: string
- description: The path to check out the results repository to
- bench_name:
- required: true
- type: string
- bench_tool:
- required: true
- type: string
- bench_output_file_path:
- required: true
- type: string
- bench_external_data_json_path:
- required: true
- type: string
-
-runs:
- using: "composite"
- steps:
- - name: Checkout repository
- uses: actions/checkout@v3
- with:
- repository: ${{ inputs.repository }}
- ref: ${{ inputs.branch }}
- token: ${{ inputs.token }}
- path: ${{ inputs.path }}
-
- - name: Store results
- uses: Roblox/rhysd-github-action-benchmark@v-luau
- with:
- name: ${{ inputs.bench_name }}
- tool: ${{ inputs.bench_tool }}
- gh-pages-branch: ${{ inputs.branch }}
- output-file-path: ${{ inputs.bench_output_file_path }}
- external-data-json-path: ${{ inputs.bench_external_data_json_path }}
-
- - name: Push benchmark results
- shell: bash
- run: |
- echo "Pushing benchmark results..."
- cd gh-pages
- git config user.name github-actions
- git config user.email github@users.noreply.github.com
- git add *.json
- git commit -m "Add benchmarks results for ${{ github.sha }}"
- git push
- cd ..
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 497483a7..f796a7c1 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -22,7 +22,7 @@ jobs:
- name: configure
run: cmake . -DCMAKE_BUILD_TYPE=Release
- name: build
- run: cmake --build . --target Luau.Repl.CLI Luau.Analyze.CLI --config Release -j 2
+ run: cmake --build . --target Luau.Repl.CLI Luau.Analyze.CLI Luau.Compile.CLI --config Release -j 2
- uses: actions/upload-artifact@v2
if: matrix.os.name != 'windows'
with:
diff --git a/.gitignore b/.gitignore
index af77f73c..528ab204 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,4 +10,5 @@
/luau
/luau-tests
/luau-analyze
+/luau-compile
__pycache__
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 00000000..13566b81
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 00000000..432e2af5
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+Luau
\ No newline at end of file
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644
index 00000000..f6038816
--- /dev/null
+++ b/.idea/codeStyles/Project.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 00000000..79ee123c
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/luau.iml b/.idea/luau.iml
new file mode 100644
index 00000000..f08604bb
--- /dev/null
+++ b/.idea/luau.iml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 00000000..79b3c948
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 00000000..fa51d68b
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 00000000..35eb1ddf
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Analysis/include/Luau/Anyification.h b/Analysis/include/Luau/Anyification.h
index 7b6f7171..c81dc3fb 100644
--- a/Analysis/include/Luau/Anyification.h
+++ b/Analysis/include/Luau/Anyification.h
@@ -4,7 +4,7 @@
#include "Luau/NotNull.h"
#include "Luau/Substitution.h"
-#include "Luau/Type.h"
+#include "Luau/TypeFwd.h"
#include
@@ -39,4 +39,4 @@ struct Anyification : Substitution
bool ignoreChildren(TypePackId ty) override;
};
-} // namespace Luau
\ No newline at end of file
+} // namespace Luau
diff --git a/Analysis/include/Luau/ApplyTypeFunction.h b/Analysis/include/Luau/ApplyTypeFunction.h
index 3f5f47fd..71430b28 100644
--- a/Analysis/include/Luau/ApplyTypeFunction.h
+++ b/Analysis/include/Luau/ApplyTypeFunction.h
@@ -3,7 +3,7 @@
#include "Luau/Substitution.h"
#include "Luau/TxnLog.h"
-#include "Luau/Type.h"
+#include "Luau/TypeFwd.h"
namespace Luau
{
diff --git a/Analysis/include/Luau/AstQuery.h b/Analysis/include/Luau/AstQuery.h
index aa7ef8d3..7652a89f 100644
--- a/Analysis/include/Luau/AstQuery.h
+++ b/Analysis/include/Luau/AstQuery.h
@@ -3,6 +3,7 @@
#include "Luau/Ast.h"
#include "Luau/Documentation.h"
+#include "Luau/TypeFwd.h"
#include
@@ -13,9 +14,6 @@ struct Binding;
struct SourceModule;
struct Module;
-struct Type;
-using TypeId = const Type*;
-
using ScopePtr = std::shared_ptr;
struct ExprOrLocal
@@ -64,8 +62,11 @@ private:
};
std::vector findAncestryAtPositionForAutocomplete(const SourceModule& source, Position pos);
+std::vector findAncestryAtPositionForAutocomplete(AstStatBlock* root, Position pos);
std::vector findAstAncestryOfPosition(const SourceModule& source, Position pos, bool includeTypes = false);
+std::vector findAstAncestryOfPosition(AstStatBlock* root, Position pos, bool includeTypes = false);
AstNode* findNodeAtPosition(const SourceModule& source, Position pos);
+AstNode* findNodeAtPosition(AstStatBlock* root, Position pos);
AstExpr* findExprAtPosition(const SourceModule& source, Position pos);
ScopePtr findScopeAtPosition(const Module& module, Position pos);
std::optional findBindingAtPosition(const Module& module, const SourceModule& source, Position pos);
diff --git a/Analysis/include/Luau/Autocomplete.h b/Analysis/include/Luau/Autocomplete.h
index 61832577..bc709c7f 100644
--- a/Analysis/include/Luau/Autocomplete.h
+++ b/Analysis/include/Luau/Autocomplete.h
@@ -38,6 +38,7 @@ enum class AutocompleteEntryKind
String,
Type,
Module,
+ GeneratedFunction,
};
enum class ParenthesesRecommendation
@@ -70,6 +71,10 @@ struct AutocompleteEntry
std::optional documentationSymbol = std::nullopt;
Tags tags;
ParenthesesRecommendation parens = ParenthesesRecommendation::None;
+ std::optional insertText;
+
+ // Only meaningful if kind is Property.
+ bool indexedWithSelf = false;
};
using AutocompleteEntryMap = std::unordered_map;
@@ -94,4 +99,6 @@ using StringCompletionCallback =
AutocompleteResult autocomplete(Frontend& frontend, const ModuleName& moduleName, Position position, StringCompletionCallback callback);
+constexpr char kGeneratedAnonymousFunctionEntryName[] = "function (anonymous autofilled)";
+
} // namespace Luau
diff --git a/Analysis/include/Luau/Breadcrumb.h b/Analysis/include/Luau/Breadcrumb.h
deleted file mode 100644
index 59b293a0..00000000
--- a/Analysis/include/Luau/Breadcrumb.h
+++ /dev/null
@@ -1,75 +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/Def.h"
-#include "Luau/NotNull.h"
-#include "Luau/Variant.h"
-
-#include
-#include
-
-namespace Luau
-{
-
-using NullableBreadcrumbId = const struct Breadcrumb*;
-using BreadcrumbId = NotNull;
-
-struct FieldMetadata
-{
- std::string prop;
-};
-
-struct SubscriptMetadata
-{
- BreadcrumbId key;
-};
-
-using Metadata = Variant;
-
-struct Breadcrumb
-{
- NullableBreadcrumbId previous;
- DefId def;
- std::optional metadata;
- std::vector children;
-};
-
-inline Breadcrumb* asMutable(NullableBreadcrumbId breadcrumb)
-{
- LUAU_ASSERT(breadcrumb);
- return const_cast(breadcrumb);
-}
-
-template
-const T* getMetadata(NullableBreadcrumbId breadcrumb)
-{
- if (!breadcrumb || !breadcrumb->metadata)
- return nullptr;
-
- return get_if(&*breadcrumb->metadata);
-}
-
-struct BreadcrumbArena
-{
- TypedAllocator allocator;
-
- template
- BreadcrumbId add(NullableBreadcrumbId previous, DefId def, Args&&... args)
- {
- Breadcrumb* bc = allocator.allocate(Breadcrumb{previous, def, std::forward(args)...});
- if (previous)
- asMutable(previous)->children.push_back(NotNull{bc});
- return NotNull{bc};
- }
-
- template
- BreadcrumbId emplace(NullableBreadcrumbId previous, DefId def, Args&&... args)
- {
- Breadcrumb* bc = allocator.allocate(Breadcrumb{previous, def, Metadata{T{std::forward(args)...}}});
- if (previous)
- asMutable(previous)->children.push_back(NotNull{bc});
- return NotNull{bc};
- }
-};
-
-} // namespace Luau
diff --git a/Analysis/include/Luau/BuiltinDefinitions.h b/Analysis/include/Luau/BuiltinDefinitions.h
index 16213958..6154f3d1 100644
--- a/Analysis/include/Luau/BuiltinDefinitions.h
+++ b/Analysis/include/Luau/BuiltinDefinitions.h
@@ -14,11 +14,7 @@ struct GlobalTypes;
struct TypeChecker;
struct TypeArena;
-void registerBuiltinTypes(GlobalTypes& globals);
-
-void registerBuiltinGlobals(TypeChecker& typeChecker, GlobalTypes& globals);
-void registerBuiltinGlobals(Frontend& frontend);
-
+void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeCheckForAutocomplete = false);
TypeId makeUnion(TypeArena& arena, std::vector&& types);
TypeId makeIntersection(TypeArena& arena, std::vector&& types);
diff --git a/Analysis/include/Luau/Cancellation.h b/Analysis/include/Luau/Cancellation.h
new file mode 100644
index 00000000..44131863
--- /dev/null
+++ b/Analysis/include/Luau/Cancellation.h
@@ -0,0 +1,24 @@
+// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
+#pragma once
+
+#include
+
+namespace Luau
+{
+
+struct FrontendCancellationToken
+{
+ void cancel()
+ {
+ cancelled.store(true);
+ }
+
+ bool requested()
+ {
+ return cancelled.load();
+ }
+
+ std::atomic cancelled;
+};
+
+} // namespace Luau
diff --git a/Analysis/include/Luau/Clone.h b/Analysis/include/Luau/Clone.h
index 51f1e7a6..28ab9931 100644
--- a/Analysis/include/Luau/Clone.h
+++ b/Analysis/include/Luau/Clone.h
@@ -16,6 +16,8 @@ using SeenTypePacks = std::unordered_map;
struct CloneState
{
+ NotNull builtinTypes;
+
SeenTypes seenTypes;
SeenTypePacks seenTypePacks;
@@ -26,7 +28,4 @@ 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);
-TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log, bool alwaysClone = false);
-TypeId shallowClone(TypeId ty, NotNull dest);
-
} // namespace Luau
diff --git a/Analysis/include/Luau/Constraint.h b/Analysis/include/Luau/Constraint.h
index 2223c29e..a026fdae 100644
--- a/Analysis/include/Luau/Constraint.h
+++ b/Analysis/include/Luau/Constraint.h
@@ -4,8 +4,8 @@
#include "Luau/Ast.h" // Used for some of the enumerations
#include "Luau/DenseHash.h"
#include "Luau/NotNull.h"
-#include "Luau/Type.h"
#include "Luau/Variant.h"
+#include "Luau/TypeFwd.h"
#include
#include
@@ -16,12 +16,6 @@ namespace Luau
struct Scope;
-struct Type;
-using TypeId = const Type*;
-
-struct TypePackVar;
-using TypePackId = const TypePackVar*;
-
// subType <: superType
struct SubtypeConstraint
{
@@ -34,6 +28,11 @@ struct PackSubtypeConstraint
{
TypePackId subPack;
TypePackId superPack;
+
+ // HACK!! TODO clip.
+ // We need to know which of `PackSubtypeConstraint` are emitted from `AstStatReturn` vs any others.
+ // Then we force these specific `PackSubtypeConstraint` to only dispatch in the order of the `return`s.
+ bool returns = false;
};
// generalizedType ~ gen sourceType
@@ -50,37 +49,15 @@ struct InstantiationConstraint
TypeId superType;
};
-struct UnaryConstraint
-{
- AstExprUnary::Op op;
- TypeId operandType;
- TypeId resultType;
-};
-
-// let L : leftType
-// let R : rightType
-// in
-// L op R : resultType
-struct BinaryConstraint
-{
- AstExprBinary::Op op;
- TypeId leftType;
- TypeId rightType;
- TypeId resultType;
-
- // When we dispatch this constraint, we update the key at this map to record
- // the overload that we selected.
- const AstNode* astFragment;
- DenseHashMap* astOriginalCallTypes;
- DenseHashMap* astOverloadResolvedTypes;
-};
-
// iteratee is iterable
// iterators is the iteration types.
struct IterableConstraint
{
TypePackId iterator;
TypePackId variables;
+
+ const AstNode* nextAstFragment;
+ DenseHashMap* astForInNextTypes;
};
// name(namedType) = name
@@ -105,8 +82,12 @@ struct FunctionCallConstraint
TypeId fn;
TypePackId argsPack;
TypePackId result;
- class AstExprCall* callSite;
+ class AstExprCall* callSite = nullptr;
std::vector> discriminantTypes;
+
+ // When we dispatch this constraint, we update the key at this map to record
+ // the overload that we selected.
+ DenseHashMap* astOverloadResolvedTypes = nullptr;
};
// result ~ prim ExpectedType SomeSingletonType MultitonType
@@ -139,6 +120,24 @@ struct HasPropConstraint
TypeId resultType;
TypeId subjectType;
std::string prop;
+
+ // HACK: We presently need types like true|false or string|"hello" when
+ // deciding whether a particular literal expression should have a singleton
+ // type. This boolean is set to true when extracting the property type of a
+ // value that may be a union of tables.
+ //
+ // For example, in the following code fragment, we want the lookup of the
+ // success property to yield true|false when extracting an expectedType in
+ // this expression:
+ //
+ // type Result = {success:true, result: T} | {success:false, error: E}
+ //
+ // local r: Result = {success=true, result=9}
+ //
+ // If we naively simplify the expectedType to boolean, we will erroneously
+ // compute the type boolean for the success property of the table literal.
+ // This causes type checking to fail.
+ bool suppressSimplification = false;
};
// result ~ setProp subjectType ["prop", "prop2", ...] propType
@@ -191,11 +190,66 @@ struct UnpackConstraint
{
TypePackId resultPack;
TypePackId sourcePack;
+
+ // UnpackConstraint is sometimes used to resolve the types of assignments.
+ // When this is the case, any LocalTypes in resultPack can have their
+ // domains extended by the corresponding type from sourcePack.
+ bool resultIsLValue = false;
};
-using ConstraintV = Variant;
+// resultType ~ refine type mode discriminant
+//
+// Compute type & discriminant (or type | discriminant) as soon as possible (but
+// no sooner), simplify, and bind resultType to that type.
+struct RefineConstraint
+{
+ enum
+ {
+ Intersection,
+ Union
+ } mode;
+
+ TypeId resultType;
+
+ TypeId type;
+ TypeId discriminant;
+};
+
+// resultType ~ T0 op T1 op ... op TN
+//
+// op is either union or intersection. If any of the input types are blocked,
+// this constraint will block unless forced.
+struct SetOpConstraint
+{
+ enum
+ {
+ Intersection,
+ Union
+ } mode;
+
+ TypeId resultType;
+ std::vector types;
+};
+
+// ty ~ reduce ty
+//
+// Try to reduce ty, if it is a TypeFamilyInstanceType. Otherwise, do nothing.
+struct ReduceConstraint
+{
+ TypeId ty;
+};
+
+// tp ~ reduce tp
+//
+// Analogous to ReduceConstraint, but for type packs.
+struct ReducePackConstraint
+{
+ TypePackId tp;
+};
+
+using ConstraintV = Variant;
struct Constraint
{
@@ -209,6 +263,8 @@ struct Constraint
ConstraintV c;
std::vector> dependencies;
+
+ DenseHashSet getFreeTypes() const;
};
using ConstraintPtr = std::unique_ptr;
diff --git a/Analysis/include/Luau/ConstraintGraphBuilder.h b/Analysis/include/Luau/ConstraintGenerator.h
similarity index 65%
rename from Analysis/include/Luau/ConstraintGraphBuilder.h
rename to Analysis/include/Luau/ConstraintGenerator.h
index e79c4c91..69b0fd20 100644
--- a/Analysis/include/Luau/ConstraintGraphBuilder.h
+++ b/Analysis/include/Luau/ConstraintGenerator.h
@@ -2,15 +2,20 @@
#pragma once
#include "Luau/Ast.h"
-#include "Luau/Refinement.h"
#include "Luau/Constraint.h"
+#include "Luau/ControlFlow.h"
#include "Luau/DataFlowGraph.h"
+#include "Luau/InsertionOrderedMap.h"
#include "Luau/Module.h"
#include "Luau/ModuleResolver.h"
+#include "Luau/Normalize.h"
#include "Luau/NotNull.h"
+#include "Luau/Refinement.h"
#include "Luau/Symbol.h"
-#include "Luau/Type.h"
+#include "Luau/TypeFwd.h"
+#include "Luau/TypeUtils.h"
#include "Luau/Variant.h"
+#include "Luau/Normalize.h"
#include
#include
@@ -52,21 +57,35 @@ struct InferencePack
}
};
-struct ConstraintGraphBuilder
+struct ConstraintGenerator
{
// A list of all the scopes in the module. This vector holds ownership of the
// scope pointers; the scopes themselves borrow pointers to other scopes to
// define the scope hierarchy.
std::vector> scopes;
- ModuleName moduleName;
ModulePtr module;
NotNull builtinTypes;
const NotNull arena;
// The root scope of the module we're generating constraints for.
- // This is null when the CGB is initially constructed.
+ // This is null when the CG is initially constructed.
Scope* rootScope;
+ struct InferredBinding
+ {
+ Scope* scope;
+ Location location;
+ TypeIds types;
+ };
+
+ // Some locals have multiple type states. We wish for Scope::bindings to
+ // map each local name onto the union of every type that the local can have
+ // over its lifetime, so we use this map to accumulate the set of types it
+ // might have.
+ //
+ // See the functions recordInferredBinding and fillInInferredBindings.
+ DenseHashMap inferredBindings{{}};
+
// Constraints that go straight to the solver.
std::vector constraints;
@@ -85,18 +104,33 @@ struct ConstraintGraphBuilder
// It is pretty uncommon for constraint generation to itself produce errors, but it can happen.
std::vector errors;
+ // Needed to be able to enable error-suppression preservation for immediate refinements.
+ NotNull normalizer;
// Needed to resolve modules to make 'require' import types properly.
NotNull moduleResolver;
// Occasionally constraint generation needs to produce an ICE.
const NotNull ice;
ScopePtr globalScope;
+
+ std::function prepareModuleScope;
+ std::vector requireCycles;
+
DcrLogger* logger;
- ConstraintGraphBuilder(const ModuleName& moduleName, ModulePtr module, TypeArena* arena, NotNull moduleResolver,
- NotNull builtinTypes, NotNull ice, const ScopePtr& globalScope, DcrLogger* logger,
- NotNull dfg);
+ ConstraintGenerator(ModulePtr module, NotNull normalizer, NotNull moduleResolver,
+ NotNull builtinTypes, NotNull ice, const ScopePtr& globalScope,
+ std::function prepareModuleScope, DcrLogger* logger, NotNull dfg,
+ std::vector requireCycles);
+ /**
+ * The entry point to the ConstraintGenerator. This will construct a set
+ * of scopes, constraints, and free types that can be solved later.
+ * @param block the root block to generate constraints for.
+ */
+ void visitModuleRoot(AstStatBlock* block);
+
+private:
/**
* Fabricates a new free type belonging to a given scope.
* @param scope the scope the free type belongs to.
@@ -116,6 +150,8 @@ struct ConstraintGraphBuilder
*/
ScopePtr childScope(AstNode* node, const ScopePtr& parent);
+ std::optional lookup(Scope* scope, DefId def);
+
/**
* Adds a new constraint with no dependencies to a given scope.
* @param scope the scope to add the constraint to.
@@ -132,38 +168,44 @@ struct ConstraintGraphBuilder
*/
NotNull addConstraint(const ScopePtr& scope, std::unique_ptr c);
+ struct RefinementPartition
+ {
+ // Types that we want to intersect against the type of the expression.
+ std::vector discriminantTypes;
+
+ // Sometimes the type we're discriminating against is implicitly nil.
+ bool shouldAppendNilType = false;
+ };
+
+ using RefinementContext = InsertionOrderedMap;
+ void unionRefinements(const RefinementContext& lhs, const RefinementContext& rhs, RefinementContext& dest, std::vector* constraints);
+ void computeRefinement(const ScopePtr& scope, RefinementId refinement, RefinementContext* refis, bool sense, bool eq, std::vector* constraints);
void applyRefinements(const ScopePtr& scope, Location location, RefinementId refinement);
- /**
- * The entry point to the ConstraintGraphBuilder. This will construct a set
- * of scopes, constraints, and free types that can be solved later.
- * @param block the root block to generate constraints for.
- */
- void visit(AstStatBlock* block);
+ ControlFlow visitBlockWithoutChildScope(const ScopePtr& scope, AstStatBlock* block);
- void visitBlockWithoutChildScope(const ScopePtr& scope, AstStatBlock* block);
-
- void visit(const ScopePtr& scope, AstStat* stat);
- void visit(const ScopePtr& scope, AstStatBlock* block);
- void visit(const ScopePtr& scope, AstStatLocal* local);
- void visit(const ScopePtr& scope, AstStatFor* for_);
- void visit(const ScopePtr& scope, AstStatForIn* forIn);
- void visit(const ScopePtr& scope, AstStatWhile* while_);
- void visit(const ScopePtr& scope, AstStatRepeat* repeat);
- void visit(const ScopePtr& scope, AstStatLocalFunction* function);
- void visit(const ScopePtr& scope, AstStatFunction* function);
- void visit(const ScopePtr& scope, AstStatReturn* ret);
- void visit(const ScopePtr& scope, AstStatAssign* assign);
- void visit(const ScopePtr& scope, AstStatCompoundAssign* assign);
- void visit(const ScopePtr& scope, AstStatIf* ifStatement);
- void visit(const ScopePtr& scope, AstStatTypeAlias* alias);
- void visit(const ScopePtr& scope, AstStatDeclareGlobal* declareGlobal);
- void visit(const ScopePtr& scope, AstStatDeclareClass* declareClass);
- void visit(const ScopePtr& scope, AstStatDeclareFunction* declareFunction);
- void visit(const ScopePtr& scope, AstStatError* error);
+ ControlFlow visit(const ScopePtr& scope, AstStat* stat);
+ ControlFlow visit(const ScopePtr& scope, AstStatBlock* block);
+ ControlFlow visit(const ScopePtr& scope, AstStatLocal* local);
+ ControlFlow visit(const ScopePtr& scope, AstStatFor* for_);
+ ControlFlow visit(const ScopePtr& scope, AstStatForIn* forIn);
+ ControlFlow visit(const ScopePtr& scope, AstStatWhile* while_);
+ ControlFlow visit(const ScopePtr& scope, AstStatRepeat* repeat);
+ ControlFlow visit(const ScopePtr& scope, AstStatLocalFunction* function);
+ ControlFlow visit(const ScopePtr& scope, AstStatFunction* function);
+ ControlFlow visit(const ScopePtr& scope, AstStatReturn* ret);
+ ControlFlow visit(const ScopePtr& scope, AstStatAssign* assign);
+ ControlFlow visit(const ScopePtr& scope, AstStatCompoundAssign* assign);
+ ControlFlow visit(const ScopePtr& scope, AstStatIf* ifStatement);
+ ControlFlow visit(const ScopePtr& scope, AstStatTypeAlias* alias);
+ ControlFlow visit(const ScopePtr& scope, AstStatDeclareGlobal* declareGlobal);
+ 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 exprs, const std::vector>& expectedTypes = {});
- InferencePack checkPack(const ScopePtr& scope, AstExpr* expr, const std::vector>& expectedTypes = {});
+ InferencePack checkPack(
+ const ScopePtr& scope, AstExpr* expr, const std::vector>& expectedTypes = {}, bool generalize = true);
InferencePack checkPack(const ScopePtr& scope, AstExprCall* call);
@@ -173,9 +215,11 @@ struct ConstraintGraphBuilder
* @param expr the expression to check.
* @param expectedType the type of the expression that is expected from its
* surrounding context. Used to implement bidirectional type checking.
+ * @param generalize If true, generalize any lambdas that are encountered.
* @return the type of the expression.
*/
- Inference check(const ScopePtr& scope, AstExpr* expr, std::optional expectedType = {}, bool forceSingleton = false);
+ Inference check(
+ const ScopePtr& scope, AstExpr* expr, std::optional expectedType = {}, bool forceSingleton = false, bool generalize = true);
Inference check(const ScopePtr& scope, AstExprConstantString* string, std::optional expectedType, bool forceSingleton);
Inference check(const ScopePtr& scope, AstExprConstantBool* bool_, std::optional expectedType, bool forceSingleton);
@@ -183,6 +227,7 @@ struct ConstraintGraphBuilder
Inference check(const ScopePtr& scope, AstExprGlobal* global);
Inference check(const ScopePtr& scope, AstExprIndexName* indexName);
Inference check(const ScopePtr& scope, AstExprIndexExpr* indexExpr);
+ Inference check(const ScopePtr& scope, AstExprFunction* func, std::optional expectedType, bool generalize);
Inference check(const ScopePtr& scope, AstExprUnary* unary);
Inference check(const ScopePtr& scope, AstExprBinary* binary, std::optional expectedType);
Inference check(const ScopePtr& scope, AstExprIfElse* ifElse, std::optional expectedType);
@@ -191,9 +236,16 @@ struct ConstraintGraphBuilder
Inference check(const ScopePtr& scope, AstExprTable* expr, std::optional expectedType);
std::tuple checkBinary(const ScopePtr& scope, AstExprBinary* binary, std::optional expectedType);
- std::vector checkLValues(const ScopePtr& scope, AstArray exprs);
-
- TypeId checkLValue(const ScopePtr& scope, AstExpr* expr);
+ /**
+ * Generate constraints to assign assignedTy to the expression expr
+ * @returns the type of the expression. This may or may not be assignedTy itself.
+ */
+ std::optional checkLValue(const ScopePtr& scope, AstExpr* expr, TypeId assignedTy);
+ std::optional checkLValue(const ScopePtr& scope, AstExprLocal* local, TypeId assignedTy);
+ std::optional checkLValue(const ScopePtr& scope, AstExprGlobal* global, TypeId assignedTy);
+ std::optional checkLValue(const ScopePtr& scope, AstExprIndexName* indexName, TypeId assignedTy);
+ std::optional checkLValue(const ScopePtr& scope, AstExprIndexExpr* indexExpr, TypeId assignedTy);
+ TypeId updateProperty(const ScopePtr& scope, AstExpr* expr, TypeId assignedTy);
struct FunctionSignature
{
@@ -208,7 +260,8 @@ struct ConstraintGraphBuilder
ScopePtr bodyScope;
};
- FunctionSignature checkFunctionSignature(const ScopePtr& parent, AstExprFunction* fn, std::optional expectedType = {});
+ FunctionSignature checkFunctionSignature(
+ const ScopePtr& parent, AstExprFunction* fn, std::optional expectedType = {}, std::optional originalName = {});
/**
* Checks the body of a function expression.
@@ -277,12 +330,18 @@ struct ConstraintGraphBuilder
/** Scan the program for global definitions.
*
- * ConstraintGraphBuilder needs to differentiate between globals and accesses to undefined symbols. Doing this "for
+ * ConstraintGenerator needs to differentiate between globals and accesses to undefined symbols. Doing this "for
* real" in a general way is going to be pretty hard, so we are choosing not to tackle that yet. For now, we do an
* initial scan of the AST and note what globals are defined.
*/
void prepopulateGlobalScope(const ScopePtr& globalScope, AstStatBlock* program);
+ // Record the fact that a particular local has a particular type in at least
+ // one of its states.
+ void recordInferredBinding(AstLocal* local, TypeId ty);
+
+ void fillInInferredBindings(const ScopePtr& globalScope, AstStatBlock* block);
+
/** Given a function type annotation, return a vector describing the expected types of the calls to the function
* For example, calling a function with annotation ((number) -> string & ((string) -> number))
* yields a vector of size 1, with value: [number | string]
diff --git a/Analysis/include/Luau/ConstraintSolver.h b/Analysis/include/Luau/ConstraintSolver.h
index 4fd7d0d1..4a4d639b 100644
--- a/Analysis/include/Luau/ConstraintSolver.h
+++ b/Analysis/include/Luau/ConstraintSolver.h
@@ -3,14 +3,18 @@
#pragma once
#include "Luau/Constraint.h"
+#include "Luau/DenseHash.h"
#include "Luau/Error.h"
+#include "Luau/Location.h"
#include "Luau/Module.h"
#include "Luau/Normalize.h"
#include "Luau/ToString.h"
#include "Luau/Type.h"
-#include "Luau/TypeReduction.h"
+#include "Luau/TypeCheckLimits.h"
+#include "Luau/TypeFwd.h"
#include "Luau/Variant.h"
+#include
#include
namespace Luau
@@ -49,11 +53,10 @@ struct HashInstantiationSignature
struct ConstraintSolver
{
- TypeArena* arena;
+ NotNull arena;
NotNull builtinTypes;
InternalErrorReporter iceReporter;
NotNull normalizer;
- NotNull reducer;
// The entire set of constraints that the solver is trying to resolve.
std::vector> constraints;
NotNull rootScope;
@@ -75,6 +78,13 @@ struct ConstraintSolver
std::unordered_map>, HashBlockedConstraintId> blocked;
// Memoized instantiations of type aliases.
DenseHashMap instantiatedAliases{{}};
+ // Breadcrumbs for where a free type's upper bound was expanded. We use
+ // these to provide more helpful error messages when a free type is solved
+ // as never unexpectedly.
+ DenseHashMap>> upperBoundContributors{nullptr};
+
+ // A mapping from free types to the number of unresolved constraints that mention them.
+ DenseHashMap unresolvedConstraints{{}};
// Recorded errors that take place within the solver.
ErrorVec errors;
@@ -83,10 +93,11 @@ struct ConstraintSolver
std::vector requireCycles;
DcrLogger* logger;
+ TypeCheckLimits limits;
explicit ConstraintSolver(NotNull normalizer, NotNull rootScope, std::vector> constraints,
- ModuleName moduleName, NotNull reducer, NotNull moduleResolver, std::vector requireCycles,
- DcrLogger* logger);
+ ModuleName moduleName, NotNull moduleResolver, std::vector requireCycles, DcrLogger* logger,
+ TypeCheckLimits limits);
// Randomize the order in which to dispatch constraints
void randomize(unsigned seed);
@@ -111,8 +122,6 @@ struct ConstraintSolver
bool tryDispatch(const PackSubtypeConstraint& c, NotNull constraint, bool force);
bool tryDispatch(const GeneralizationConstraint& c, NotNull constraint, bool force);
bool tryDispatch(const InstantiationConstraint& c, NotNull constraint, bool force);
- bool tryDispatch(const UnaryConstraint& c, NotNull constraint, bool force);
- bool tryDispatch(const BinaryConstraint& c, NotNull constraint, bool force);
bool tryDispatch(const IterableConstraint& c, NotNull constraint, bool force);
bool tryDispatch(const NameConstraint& c, NotNull constraint);
bool tryDispatch(const TypeAliasExpansionConstraint& c, NotNull constraint);
@@ -123,6 +132,10 @@ struct ConstraintSolver
bool tryDispatch(const SetIndexerConstraint& c, NotNull constraint, bool force);
bool tryDispatch(const SingletonOrTopTypeConstraint& c, NotNull constraint);
bool tryDispatch(const UnpackConstraint& c, NotNull constraint);
+ bool tryDispatch(const RefineConstraint& c, NotNull constraint, bool force);
+ bool tryDispatch(const SetOpConstraint& c, NotNull constraint, bool force);
+ bool tryDispatch(const ReduceConstraint& c, NotNull constraint, bool force);
+ bool tryDispatch(const ReducePackConstraint& c, NotNull constraint, bool force);
// for a, ... in some_table do
// also handles __iter metamethod
@@ -132,8 +145,10 @@ struct ConstraintSolver
bool tryDispatchIterableFunction(
TypeId nextTy, TypeId tableTy, TypeId firstIndexTy, const IterableConstraint& c, NotNull constraint, bool force);
- std::pair, std::optional> lookupTableProp(TypeId subjectType, const std::string& propName);
- std::pair, std::optional> lookupTableProp(TypeId subjectType, const std::string& propName, std::unordered_set& seen);
+ std::pair, std::optional> lookupTableProp(
+ TypeId subjectType, const std::string& propName, bool suppressSimplification = false);
+ std::pair, std::optional> lookupTableProp(
+ TypeId subjectType, const std::string& propName, bool suppressSimplification, DenseHashSet& seen);
void block(NotNull target, NotNull constraint);
/**
@@ -143,21 +158,39 @@ struct ConstraintSolver
bool block(TypeId target, NotNull constraint);
bool block(TypePackId target, NotNull constraint);
- // Traverse the type. If any blocked or pending types are found, block
- // the constraint on them.
+ // Block on every target
+ template
+ bool block(const T& targets, NotNull constraint)
+ {
+ for (TypeId target : targets)
+ block(target, constraint);
+
+ return false;
+ }
+
+ /**
+ * For all constraints that are blocked on one constraint, make them block
+ * on a new constraint.
+ * @param source the constraint to copy blocks from.
+ * @param addition the constraint that other constraints should now block on.
+ */
+ void inheritBlocks(NotNull source, NotNull addition);
+
+ // Traverse the type. If any pending types are found, block the constraint
+ // on them.
//
// Returns false if a type blocks the constraint.
//
// FIXME: This use of a boolean for the return result is an appalling
// interface.
- bool recursiveBlock(TypeId target, NotNull constraint);
- bool recursiveBlock(TypePackId target, NotNull constraint);
+ bool blockOnPendingTypes(TypeId target, NotNull constraint);
+ bool blockOnPendingTypes(TypePackId target, NotNull constraint);
void unblock(NotNull progressed);
- void unblock(TypeId progressed);
- void unblock(TypePackId progressed);
- void unblock(const std::vector& types);
- void unblock(const std::vector& packs);
+ void unblock(TypeId progressed, Location location);
+ void unblock(TypePackId progressed, Location location);
+ void unblock(const std::vector& types, Location location);
+ void unblock(const std::vector& packs, Location location);
/**
* @returns true if the TypeId is in a blocked state.
@@ -180,8 +213,9 @@ struct ConstraintSolver
* the result.
* @param subType the sub-type to unify.
* @param superType the super-type to unify.
+ * @returns optionally a unification too complex error if unification failed
*/
- void unify(TypeId subType, TypeId superType, NotNull scope);
+ std::optional unify(NotNull scope, Location location, TypeId subType, TypeId superType);
/**
* Creates a new Unifier and performs a single unification operation. Commits
@@ -189,7 +223,7 @@ struct ConstraintSolver
* @param subPack the sub-type pack to unify.
* @param superPack the super-type pack to unify.
*/
- void unify(TypePackId subPack, TypePackId superPack, NotNull scope);
+ ErrorVec unify(NotNull scope, Location location, TypePackId subPack, TypePackId superPack);
/** Pushes a new solver constraint to the solver.
* @param cv the body of the constraint.
@@ -210,7 +244,41 @@ struct ConstraintSolver
void reportError(TypeErrorData&& data, const Location& location);
void reportError(TypeError e);
+ /**
+ * Checks the existing set of constraints to see if there exist any that contain
+ * the provided free type, indicating that it is not yet ready to be replaced by
+ * one of its bounds.
+ * @param ty the free type that to check for related constraints
+ * @returns whether or not it is unsafe to replace the free type by one of its bounds
+ */
+ bool hasUnresolvedConstraints(TypeId ty);
+
private:
+
+ /** Helper used by tryDispatch(SubtypeConstraint) and
+ * tryDispatch(PackSubtypeConstraint)
+ *
+ * Attempts to unify subTy with superTy. If doing so would require unifying
+ * BlockedTypes, fail and block the constraint on those BlockedTypes.
+ *
+ * If unification fails, replace all free types with errorType.
+ *
+ * If unification succeeds, unblock every type changed by the unification.
+ */
+ template
+ bool tryUnify(NotNull constraint, TID subTy, TID superTy);
+
+ /**
+ * Bind a BlockedType to another type while taking care not to bind it to
+ * itself in the case that resultTy == blockedTy. This can happen if we
+ * have a tautological constraint. When it does, we must instead bind
+ * blockedTy to a fresh type belonging to an appropriate scope.
+ *
+ * To determine which scope is appropriate, we also accept rootTy, which is
+ * to be the type that contains blockedTy.
+ */
+ void bindBlockedType(TypeId blockedTy, TypeId resultTy, TypeId rootTy, Location location);
+
/**
* Marks a constraint as being blocked on a type or type pack. The constraint
* solver will not attempt to dispatch blocked constraints until their
@@ -231,7 +299,10 @@ private:
TypeId errorRecoveryType() const;
TypePackId errorRecoveryTypePack() const;
- TypeId unionOfTypes(TypeId a, TypeId b, NotNull scope, bool unifyFreeTypes);
+ TypePackId anyifyModuleReturnTypePackGenerics(TypePackId tp);
+
+ void throwTimeLimitError();
+ void throwUserCancelError();
ToStringOptions opts;
};
diff --git a/Analysis/include/Luau/ControlFlow.h b/Analysis/include/Luau/ControlFlow.h
new file mode 100644
index 00000000..82c0403c
--- /dev/null
+++ b/Analysis/include/Luau/ControlFlow.h
@@ -0,0 +1,36 @@
+// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
+#pragma once
+
+#include
+
+namespace Luau
+{
+
+struct Scope;
+using ScopePtr = std::shared_ptr;
+
+enum class ControlFlow
+{
+ None = 0b00001,
+ Returns = 0b00010,
+ Throws = 0b00100,
+ Breaks = 0b01000,
+ Continues = 0b10000,
+};
+
+inline ControlFlow operator&(ControlFlow a, ControlFlow b)
+{
+ return ControlFlow(int(a) & int(b));
+}
+
+inline ControlFlow operator|(ControlFlow a, ControlFlow b)
+{
+ return ControlFlow(int(a) | int(b));
+}
+
+inline bool matches(ControlFlow a, ControlFlow b)
+{
+ return (a & b) != ControlFlow(0);
+}
+
+} // namespace Luau
diff --git a/Analysis/include/Luau/DataFlowGraph.h b/Analysis/include/Luau/DataFlowGraph.h
index ce4ecb04..083e5046 100644
--- a/Analysis/include/Luau/DataFlowGraph.h
+++ b/Analysis/include/Luau/DataFlowGraph.h
@@ -3,29 +3,47 @@
// Do not include LValue. It should never be used here.
#include "Luau/Ast.h"
-#include "Luau/Breadcrumb.h"
+#include "Luau/ControlFlow.h"
#include "Luau/DenseHash.h"
#include "Luau/Def.h"
#include "Luau/Symbol.h"
+#include "Luau/TypedAllocator.h"
#include
namespace Luau
{
+struct RefinementKey
+{
+ const RefinementKey* parent = nullptr;
+ DefId def;
+ std::optional propName;
+};
+
+struct RefinementKeyArena
+{
+ TypedAllocator allocator;
+
+ const RefinementKey* leaf(DefId def);
+ const RefinementKey* node(const RefinementKey* parent, DefId def, const std::string& propName);
+};
+
struct DataFlowGraph
{
DataFlowGraph(DataFlowGraph&&) = default;
DataFlowGraph& operator=(DataFlowGraph&&) = default;
- NullableBreadcrumbId getBreadcrumb(const AstExpr* expr) const;
+ DefId getDef(const AstExpr* expr) const;
+ // Look up for the rvalue def for a compound assignment.
+ std::optional getRValueDefForCompoundAssign(const AstExpr* expr) const;
- BreadcrumbId getBreadcrumb(const AstLocal* local) const;
- BreadcrumbId getBreadcrumb(const AstExprLocal* local) const;
- BreadcrumbId getBreadcrumb(const AstExprGlobal* global) const;
+ DefId getDef(const AstLocal* local) const;
- BreadcrumbId getBreadcrumb(const AstStatDeclareGlobal* global) const;
- BreadcrumbId getBreadcrumb(const AstStatDeclareFunction* func) const;
+ DefId getDef(const AstStatDeclareGlobal* global) const;
+ DefId getDef(const AstStatDeclareFunction* func) const;
+
+ const RefinementKey* getRefinementKey(const AstExpr* expr) const;
private:
DataFlowGraph() = default;
@@ -33,17 +51,23 @@ private:
DataFlowGraph(const DataFlowGraph&) = delete;
DataFlowGraph& operator=(const DataFlowGraph&) = delete;
- DefArena defs;
- BreadcrumbArena breadcrumbs;
+ DefArena defArena;
+ RefinementKeyArena keyArena;
- DenseHashMap astBreadcrumbs{nullptr};
+ DenseHashMap astDefs{nullptr};
// Sometimes we don't have the AstExprLocal* but we have AstLocal*, and sometimes we need to extract that DefId.
- DenseHashMap localBreadcrumbs{nullptr};
+ DenseHashMap localDefs{nullptr};
// There's no AstStatDeclaration, and it feels useless to introduce it just to enforce an invariant in one place.
// All keys in this maps are really only statements that ambiently declares a symbol.
- DenseHashMap declaredBreadcrumbs{nullptr};
+ DenseHashMap 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 compoundAssignDefs{nullptr};
+
+ DenseHashMap astRefinementKeys{nullptr};
friend struct DataFlowGraphBuilder;
};
@@ -51,15 +75,29 @@ private:
struct DfgScope
{
DfgScope* parent;
- DenseHashMap bindings{Symbol{}};
- DenseHashMap> props{nullptr};
+ bool isLoopScope;
- NullableBreadcrumbId lookup(Symbol symbol) const;
- NullableBreadcrumbId lookup(DefId def, const std::string& key) const;
+ using Bindings = DenseHashMap;
+ using Props = DenseHashMap>;
+
+ Bindings bindings{Symbol{}};
+ Props props{nullptr};
+
+ std::optional lookup(Symbol symbol) const;
+ std::optional lookup(DefId def, const std::string& key) const;
+
+ void inherit(const DfgScope* childScope);
+
+ bool canUpdateDefinition(Symbol symbol) const;
+ bool canUpdateDefinition(DefId def, const std::string& key) const;
+};
+
+struct DataFlowResult
+{
+ DefId def;
+ const RefinementKey* parent = nullptr;
};
-// Currently unsound. We do not presently track the control flow of the program.
-// Additionally, we do not presently track assignments.
struct DataFlowGraphBuilder
{
static DataFlowGraph build(AstStatBlock* root, NotNull handle);
@@ -71,61 +109,69 @@ private:
DataFlowGraphBuilder& operator=(const DataFlowGraphBuilder&) = delete;
DataFlowGraph graph;
- NotNull defs{&graph.defs};
- NotNull breadcrumbs{&graph.breadcrumbs};
+ NotNull defArena{&graph.defArena};
+ NotNull keyArena{&graph.keyArena};
struct InternalErrorReporter* handle = nullptr;
DfgScope* moduleScope = nullptr;
std::vector> scopes;
- DfgScope* childScope(DfgScope* scope);
+ DfgScope* childScope(DfgScope* scope, bool isLoopScope = false);
- void visit(DfgScope* scope, AstStatBlock* b);
- void visitBlockWithoutChildScope(DfgScope* scope, AstStatBlock* b);
+ void join(DfgScope* p, DfgScope* a, DfgScope* b);
+ void joinBindings(DfgScope::Bindings& p, const DfgScope::Bindings& a, const DfgScope::Bindings& b);
+ void joinProps(DfgScope::Props& p, const DfgScope::Props& a, const DfgScope::Props& b);
- void visit(DfgScope* scope, AstStat* s);
- void visit(DfgScope* scope, AstStatIf* i);
- void visit(DfgScope* scope, AstStatWhile* w);
- void visit(DfgScope* scope, AstStatRepeat* r);
- void visit(DfgScope* scope, AstStatBreak* b);
- void visit(DfgScope* scope, AstStatContinue* c);
- void visit(DfgScope* scope, AstStatReturn* r);
- void visit(DfgScope* scope, AstStatExpr* e);
- void visit(DfgScope* scope, AstStatLocal* l);
- void visit(DfgScope* scope, AstStatFor* f);
- void visit(DfgScope* scope, AstStatForIn* f);
- void visit(DfgScope* scope, AstStatAssign* a);
- void visit(DfgScope* scope, AstStatCompoundAssign* c);
- void visit(DfgScope* scope, AstStatFunction* f);
- void visit(DfgScope* scope, AstStatLocalFunction* l);
- void visit(DfgScope* scope, AstStatTypeAlias* t);
- void visit(DfgScope* scope, AstStatDeclareGlobal* d);
- void visit(DfgScope* scope, AstStatDeclareFunction* d);
- void visit(DfgScope* scope, AstStatDeclareClass* d);
- void visit(DfgScope* scope, AstStatError* error);
+ DefId lookup(DfgScope* scope, Symbol symbol);
+ DefId lookup(DfgScope* scope, DefId def, const std::string& key);
- BreadcrumbId visitExpr(DfgScope* scope, AstExpr* e);
- BreadcrumbId visitExpr(DfgScope* scope, AstExprLocal* l);
- BreadcrumbId visitExpr(DfgScope* scope, AstExprGlobal* g);
- BreadcrumbId visitExpr(DfgScope* scope, AstExprCall* c);
- BreadcrumbId visitExpr(DfgScope* scope, AstExprIndexName* i);
- BreadcrumbId visitExpr(DfgScope* scope, AstExprIndexExpr* i);
- BreadcrumbId visitExpr(DfgScope* scope, AstExprFunction* f);
- BreadcrumbId visitExpr(DfgScope* scope, AstExprTable* t);
- BreadcrumbId visitExpr(DfgScope* scope, AstExprUnary* u);
- BreadcrumbId visitExpr(DfgScope* scope, AstExprBinary* b);
- BreadcrumbId visitExpr(DfgScope* scope, AstExprTypeAssertion* t);
- BreadcrumbId visitExpr(DfgScope* scope, AstExprIfElse* i);
- BreadcrumbId visitExpr(DfgScope* scope, AstExprInterpString* i);
- BreadcrumbId visitExpr(DfgScope* scope, AstExprError* error);
+ ControlFlow visit(DfgScope* scope, AstStatBlock* b);
+ ControlFlow visitBlockWithoutChildScope(DfgScope* scope, AstStatBlock* b);
- void visitLValue(DfgScope* scope, AstExpr* e);
- void visitLValue(DfgScope* scope, AstExprLocal* l);
- void visitLValue(DfgScope* scope, AstExprGlobal* g);
- void visitLValue(DfgScope* scope, AstExprIndexName* i);
- void visitLValue(DfgScope* scope, AstExprIndexExpr* i);
- void visitLValue(DfgScope* scope, AstExprError* e);
+ 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(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(DfgScope* scope, AstExpr* e, DefId incomingDef, bool isCompoundAssignment = false);
+ void visitLValue(DfgScope* scope, AstExprLocal* l, DefId incomingDef, bool isCompoundAssignment);
+ void visitLValue(DfgScope* scope, AstExprGlobal* g, DefId incomingDef, bool isCompoundAssignment);
+ void visitLValue(DfgScope* scope, AstExprIndexName* i, DefId incomingDef);
+ void visitLValue(DfgScope* scope, AstExprIndexExpr* i, DefId incomingDef);
+ void visitLValue(DfgScope* scope, AstExprError* e, DefId incomingDef);
void visitType(DfgScope* scope, AstType* t);
void visitType(DfgScope* scope, AstTypeReference* r);
diff --git a/Analysis/include/Luau/Def.h b/Analysis/include/Luau/Def.h
index 10d81367..e3fec9b6 100644
--- a/Analysis/include/Luau/Def.h
+++ b/Analysis/include/Luau/Def.h
@@ -23,6 +23,7 @@ using DefId = NotNull;
*/
struct Cell
{
+ bool subscripted = false;
};
/**
@@ -71,13 +72,15 @@ const T* get(DefId def)
return get_if(&def->v);
}
+bool containsSubscriptedDefinition(DefId def);
+
struct DefArena
{
TypedAllocator allocator;
- DefId freshCell();
- // TODO: implement once we have cases where we need to merge in definitions
- // DefId phi(const std::vector& defs);
+ DefId freshCell(bool subscripted = false);
+ DefId phi(DefId a, DefId b);
+ DefId phi(const std::vector& defs);
};
} // namespace Luau
diff --git a/Analysis/include/Luau/Differ.h b/Analysis/include/Luau/Differ.h
new file mode 100644
index 00000000..587ed5ae
--- /dev/null
+++ b/Analysis/include/Luau/Differ.h
@@ -0,0 +1,199 @@
+// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
+#pragma once
+
+#include "Luau/DenseHash.h"
+#include "Luau/TypeFwd.h"
+#include "Luau/UnifierSharedState.h"
+
+#include
+#include
+#include
+
+namespace Luau
+{
+struct DiffPathNode
+{
+ // TODO: consider using Variants to simplify toString implementation
+ enum Kind
+ {
+ TableProperty,
+ FunctionArgument,
+ FunctionReturn,
+ Union,
+ Intersection,
+ Negation,
+ };
+ Kind kind;
+ // non-null when TableProperty
+ std::optional tableProperty;
+ // non-null when FunctionArgument (unless variadic arg), FunctionReturn (unless variadic arg), Union, or Intersection (i.e. anonymous fields)
+ std::optional index;
+
+ /**
+ * Do not use for leaf nodes
+ */
+ DiffPathNode(Kind kind)
+ : kind(kind)
+ {
+ }
+
+ DiffPathNode(Kind kind, std::optional tableProperty, std::optional index)
+ : kind(kind)
+ , tableProperty(tableProperty)
+ , index(index)
+ {
+ }
+
+ std::string toString() const;
+
+ static DiffPathNode constructWithTableProperty(Name tableProperty);
+
+ static DiffPathNode constructWithKindAndIndex(Kind kind, size_t index);
+
+ static DiffPathNode constructWithKind(Kind kind);
+};
+
+struct DiffPathNodeLeaf
+{
+ std::optional ty;
+ std::optional tableProperty;
+ std::optional minLength;
+ bool isVariadic;
+ // TODO: Rename to anonymousIndex, for both union and Intersection
+ std::optional unionIndex;
+ DiffPathNodeLeaf(
+ std::optional ty, std::optional tableProperty, std::optional minLength, bool isVariadic, std::optional unionIndex)
+ : ty(ty)
+ , tableProperty(tableProperty)
+ , minLength(minLength)
+ , isVariadic(isVariadic)
+ , unionIndex(unionIndex)
+ {
+ }
+
+ static DiffPathNodeLeaf detailsNormal(TypeId ty);
+
+ static DiffPathNodeLeaf detailsTableProperty(TypeId ty, Name tableProperty);
+
+ static DiffPathNodeLeaf detailsUnionIndex(TypeId ty, size_t index);
+
+ static DiffPathNodeLeaf detailsLength(int minLength, bool isVariadic);
+
+ static DiffPathNodeLeaf nullopts();
+};
+
+struct DiffPath
+{
+ std::vector path;
+
+ std::string toString(bool prependDot) const;
+};
+struct DiffError
+{
+ enum Kind
+ {
+ Normal,
+ MissingTableProperty,
+ MissingUnionMember,
+ MissingIntersectionMember,
+ IncompatibleGeneric,
+ LengthMismatchInFnArgs,
+ LengthMismatchInFnRets,
+ };
+ Kind kind;
+
+ DiffPath diffPath;
+ DiffPathNodeLeaf left;
+ DiffPathNodeLeaf right;
+
+ std::string leftRootName;
+ std::string rightRootName;
+
+ DiffError(Kind kind, DiffPathNodeLeaf left, DiffPathNodeLeaf right, std::string leftRootName, std::string rightRootName)
+ : kind(kind)
+ , left(left)
+ , right(right)
+ , leftRootName(leftRootName)
+ , rightRootName(rightRootName)
+ {
+ checkValidInitialization(left, right);
+ }
+ DiffError(Kind kind, DiffPath diffPath, DiffPathNodeLeaf left, DiffPathNodeLeaf right, std::string leftRootName, std::string rightRootName)
+ : kind(kind)
+ , diffPath(diffPath)
+ , left(left)
+ , right(right)
+ , leftRootName(leftRootName)
+ , rightRootName(rightRootName)
+ {
+ checkValidInitialization(left, right);
+ }
+
+ std::string toString(bool multiLine = false) const;
+
+private:
+ std::string toStringALeaf(std::string rootName, const DiffPathNodeLeaf& leaf, const DiffPathNodeLeaf& otherLeaf, bool multiLine) const;
+ void checkValidInitialization(const DiffPathNodeLeaf& left, const DiffPathNodeLeaf& right);
+ void checkNonMissingPropertyLeavesHaveNulloptTableProperty() const;
+};
+
+struct DifferResult
+{
+ std::optional diffError;
+
+ DifferResult() {}
+ DifferResult(DiffError diffError)
+ : diffError(diffError)
+ {
+ }
+
+ void wrapDiffPath(DiffPathNode node);
+};
+struct DifferEnvironment
+{
+ TypeId rootLeft;
+ TypeId rootRight;
+ std::optional externalSymbolLeft;
+ std::optional externalSymbolRight;
+ DenseHashMap genericMatchedPairs;
+ DenseHashMap genericTpMatchedPairs;
+
+ DifferEnvironment(
+ TypeId rootLeft, TypeId rootRight, std::optional externalSymbolLeft, std::optional externalSymbolRight)
+ : rootLeft(rootLeft)
+ , rootRight(rootRight)
+ , externalSymbolLeft(externalSymbolLeft)
+ , externalSymbolRight(externalSymbolRight)
+ , genericMatchedPairs(nullptr)
+ , genericTpMatchedPairs(nullptr)
+ {
+ }
+
+ bool isProvenEqual(TypeId left, TypeId right) const;
+ bool isAssumedEqual(TypeId left, TypeId right) const;
+ void recordProvenEqual(TypeId left, TypeId right);
+ void pushVisiting(TypeId left, TypeId right);
+ void popVisiting();
+ std::vector>::const_reverse_iterator visitingBegin() const;
+ std::vector>::const_reverse_iterator visitingEnd() const;
+ std::string getDevFixFriendlyNameLeft() const;
+ std::string getDevFixFriendlyNameRight() const;
+
+private:
+ // TODO: consider using DenseHashSet
+ std::unordered_set, TypeIdPairHash> provenEqual;
+ // Ancestors of current types
+ std::unordered_set, TypeIdPairHash> visiting;
+ std::vector> visitingStack;
+};
+DifferResult diff(TypeId ty1, TypeId ty2);
+DifferResult diffWithSymbols(TypeId ty1, TypeId ty2, std::optional symbol1, std::optional symbol2);
+
+/**
+ * True if ty is a "simple" type, i.e. cannot contain types.
+ * string, number, boolean are simple types.
+ * function and table are not simple types.
+ */
+bool isSimple(TypeId ty);
+
+} // namespace Luau
diff --git a/Analysis/include/Luau/Error.h b/Analysis/include/Luau/Error.h
index 8571430b..ddbf6dcb 100644
--- a/Analysis/include/Luau/Error.h
+++ b/Analysis/include/Luau/Error.h
@@ -2,8 +2,12 @@
#pragma once
#include "Luau/Location.h"
+#include "Luau/NotNull.h"
#include "Luau/Type.h"
#include "Luau/Variant.h"
+#include "Luau/Ast.h"
+
+#include
namespace Luau
{
@@ -318,6 +322,7 @@ struct TypePackMismatch
{
TypePackId wantedTp;
TypePackId givenTp;
+ std::string reason;
bool operator==(const TypePackMismatch& rhs) const;
};
@@ -329,12 +334,51 @@ struct DynamicPropertyLookupOnClassesUnsafe
bool operator==(const DynamicPropertyLookupOnClassesUnsafe& rhs) const;
};
+struct UninhabitedTypeFamily
+{
+ TypeId ty;
+
+ bool operator==(const UninhabitedTypeFamily& rhs) const;
+};
+
+struct UninhabitedTypePackFamily
+{
+ TypePackId tp;
+
+ bool operator==(const UninhabitedTypePackFamily& rhs) const;
+};
+
+struct WhereClauseNeeded
+{
+ TypeId ty;
+
+ bool operator==(const WhereClauseNeeded& rhs) const;
+};
+
+struct PackWhereClauseNeeded
+{
+ TypePackId tp;
+
+ bool operator==(const PackWhereClauseNeeded& rhs) const;
+};
+
+struct CheckedFunctionCallError
+{
+ TypeId expected;
+ TypeId passed;
+ std::string checkedFunctionName;
+ // TODO: make this a vector
+ size_t argumentIndex;
+ bool operator==(const CheckedFunctionCallError& rhs) const;
+};
+
using TypeErrorData = Variant;
+ TypesAreUnrelated, NormalizationTooComplex, TypePackMismatch, DynamicPropertyLookupOnClassesUnsafe, UninhabitedTypeFamily,
+ UninhabitedTypePackFamily, WhereClauseNeeded, PackWhereClauseNeeded, CheckedFunctionCallError>;
struct TypeErrorSummary
{
@@ -403,7 +447,7 @@ std::string toString(const TypeError& error, TypeErrorToStringOptions options);
bool containsParseErrorName(const TypeError& error);
// Copy any types named in the error into destArena.
-void copyErrors(ErrorVec& errors, struct TypeArena& destArena);
+void copyErrors(ErrorVec& errors, struct TypeArena& destArena, NotNull builtinTypes);
// Internal Compiler Error
struct InternalErrorReporter
diff --git a/Analysis/include/Luau/Frontend.h b/Analysis/include/Luau/Frontend.h
index 9c0366a6..2b83c443 100644
--- a/Analysis/include/Luau/Frontend.h
+++ b/Analysis/include/Luau/Frontend.h
@@ -2,13 +2,15 @@
#pragma once
#include "Luau/Config.h"
+#include "Luau/GlobalTypes.h"
#include "Luau/Module.h"
#include "Luau/ModuleResolver.h"
#include "Luau/RequireTracer.h"
#include "Luau/Scope.h"
-#include "Luau/TypeInfer.h"
+#include "Luau/TypeCheckLimits.h"
#include "Luau/Variant.h"
+#include
#include
#include
#include
@@ -27,6 +29,8 @@ struct FileResolver;
struct ModuleResolver;
struct ParseResult;
struct HotComment;
+struct BuildQueueItem;
+struct FrontendCancellationToken;
struct LoadDefinitionFileResult
{
@@ -36,9 +40,6 @@ struct LoadDefinitionFileResult
ModulePtr module;
};
-LoadDefinitionFileResult loadDefinitionFile(TypeChecker& typeChecker, GlobalTypes& globals, ScopePtr targetScope, std::string_view definition,
- const std::string& packageName, bool captureComments);
-
std::optional parseMode(const std::vector& hotcomments);
std::vector parsePathExpr(const AstExpr& pathExpr);
@@ -69,7 +70,8 @@ struct SourceNode
}
ModuleName name;
- std::unordered_set requireSet;
+ std::string humanReadableName;
+ DenseHashSet requireSet{{}};
std::vector> requireLocations;
bool dirtySourceModule = true;
bool dirtyModule = true;
@@ -89,14 +91,29 @@ struct FrontendOptions
// order to get more precise type information)
bool forAutocomplete = false;
+ bool runLintChecks = false;
+
// If not empty, randomly shuffle the constraint set before attempting to
// solve. Use this value to seed the random number generator.
std::optional randomizeConstraintResolutionSeed;
+
+ std::optional enabledLintWarnings;
+
+ std::shared_ptr cancellationToken;
+
+ // Time limit for typechecking a single module
+ std::optional moduleTimeLimitSec;
+
+ // When true, some internal complexity limits will be scaled down for modules that miss the limit set by moduleTimeLimitSec
+ bool applyInternalLimitScaling = false;
};
struct CheckResult
{
std::vector errors;
+
+ LintResult lintResult;
+
std::vector