Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions extern/luau/.gitrepo
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
[subrepo]
remote = git@github.com:luau-lang/luau.git
branch = master
commit = c1e2f650dbd8c1080a3c5614d02adc5b75254c4e
parent = 4045f931959bfa871429a6c0878599c745229b48
commit = 640ebbc0a51bef0daa7c9b8c943d522dacb6a9a8
parent = df1e96a1741709ff2795a1dd27b997aff7af56e7
method = merge
cmdver = 0.4.9
3 changes: 3 additions & 0 deletions extern/luau/Analysis/include/Luau/ConstraintGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ struct ConstraintGenerator
// will enqueue them during solving.
std::vector<ConstraintPtr> unqueuedConstraints;

// Map a function's signature scope back to its signature type.
DenseHashMap<Scope*, TypeId> scopeToFunction{nullptr};

// The private scope of type aliases for which the type parameters belong to.
DenseHashMap<const AstStatTypeAlias*, ScopePtr> astTypeAliasDefiningScopes{nullptr};

Expand Down
5 changes: 5 additions & 0 deletions extern/luau/Analysis/include/Luau/ConstraintSolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ struct ConstraintSolver
NotNull<TypeFunctionRuntime> typeFunctionRuntime;
// The entire set of constraints that the solver is trying to resolve.
std::vector<NotNull<Constraint>> constraints;
NotNull<DenseHashMap<Scope*, TypeId>> scopeToFunction;
NotNull<Scope> rootScope;
ModuleName currentModuleName;

Expand Down Expand Up @@ -119,6 +120,7 @@ struct ConstraintSolver
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{{}};
Expand All @@ -144,6 +146,7 @@ struct ConstraintSolver
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,
Expand Down Expand Up @@ -171,6 +174,8 @@ struct ConstraintSolver
bool isDone() const;

private:
void generalizeOneType(TypeId ty);

/**
* Bind a type variable to another type.
*
Expand Down
60 changes: 60 additions & 0 deletions extern/luau/Analysis/include/Luau/FragmentAutocomplete.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,65 @@ FragmentAutocompleteResult fragmentAutocomplete(
std::optional<Position> fragmentEndPosition = std::nullopt
);

enum class FragmentAutocompleteStatus
{
Success,
FragmentTypeCheckFail,
InternalIce
};

struct FragmentAutocompleteStatusResult
{
FragmentAutocompleteStatus status;
std::optional<FragmentAutocompleteResult> result;
};

struct FragmentContext
{
std::string_view newSrc;
const ParseResult& newAstRoot;
std::optional<FrontendOptions> opts;
std::optional<Position> DEPRECATED_fragmentEndPosition;
};

/**
* @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
2 changes: 2 additions & 0 deletions extern/luau/Analysis/include/Luau/TableLiteralInference.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include "Luau/NotNull.h"
#include "Luau/TypeFwd.h"

#include <vector>

namespace Luau
{

Expand Down
3 changes: 1 addition & 2 deletions extern/luau/Analysis/include/Luau/TxnLog.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,10 @@ T* getMutable(PendingTypePack* pending)
// Log of what TypeIds we are rebinding, to be committed later.
struct TxnLog
{
explicit TxnLog(bool useScopes = false)
explicit TxnLog()
: typeVarChanges(nullptr)
, typePackChanges(nullptr)
, ownedSeen()
, useScopes(useScopes)
, sharedSeen(&ownedSeen)
{
}
Expand Down
6 changes: 5 additions & 1 deletion extern/luau/Analysis/include/Luau/TypeFunctionRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,11 @@ struct TypeFunctionClassType

std::optional<TypeFunctionTypeId> metatable; // metaclass?

std::optional<TypeFunctionTypeId> parent;
// 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 classTy;

Expand Down
10 changes: 0 additions & 10 deletions extern/luau/Analysis/include/Luau/Unifier.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,6 @@ 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);
Expand Down Expand Up @@ -169,7 +165,6 @@ struct Unifier

std::optional<TypeId> findTablePropertyRespectingMeta(TypeId lhsType, Name name);

TxnLog combineLogsIntoIntersection(std::vector<TxnLog> logs);
TxnLog combineLogsIntoUnion(std::vector<TxnLog> logs);

public:
Expand All @@ -195,11 +190,6 @@ struct Unifier

// 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);
Expand Down
4 changes: 4 additions & 0 deletions extern/luau/Analysis/src/Autocomplete.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "Luau/Autocomplete.h"

#include "Luau/AstQuery.h"
#include "Luau/TimeTrace.h"
#include "Luau/TypeArena.h"
#include "Luau/Module.h"
#include "Luau/Frontend.h"
Expand All @@ -15,6 +16,9 @@ namespace Luau

AutocompleteResult autocomplete(Frontend& frontend, const ModuleName& moduleName, Position position, StringCompletionCallback callback)
{
LUAU_TIMETRACE_SCOPE("Luau::autocomplete", "Autocomplete");
LUAU_TIMETRACE_ARGUMENT("name", moduleName.c_str());

const SourceModule* sourceModule = frontend.getSourceModule(moduleName);
if (!sourceModule)
return {};
Expand Down
100 changes: 75 additions & 25 deletions extern/luau/Analysis/src/AutocompleteCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "Luau/Common.h"
#include "Luau/FileResolver.h"
#include "Luau/Frontend.h"
#include "Luau/TimeTrace.h"
#include "Luau/ToString.h"
#include "Luau/Subtyping.h"
#include "Luau/TypeInfer.h"
Expand All @@ -24,7 +25,8 @@ LUAU_FASTINT(LuauTypeInferIterationLimit)
LUAU_FASTINT(LuauTypeInferRecursionLimit)

LUAU_FASTFLAGVARIABLE(LuauAutocompleteRefactorsForIncrementalAutocomplete)
LUAU_FASTFLAGVARIABLE(LuauAutocompleteUseLimits)

LUAU_FASTFLAGVARIABLE(LuauAutocompleteUsesModuleForTypeCompatibility)

static const std::unordered_set<std::string> kStatementStartingKeywords =
{"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"};
Expand Down Expand Up @@ -146,44 +148,91 @@ static std::optional<TypeId> findExpectedTypeAt(const Module& module, AstNode* n
return *it;
}

static bool checkTypeMatch(TypeId subTy, TypeId superTy, NotNull<Scope> scope, TypeArena* typeArena, NotNull<BuiltinTypes> builtinTypes)
static bool checkTypeMatch(
const Module& module,
TypeId subTy,
TypeId superTy,
NotNull<Scope> scope,
TypeArena* typeArena,
NotNull<BuiltinTypes> builtinTypes
)
{
InternalErrorReporter iceReporter;
UnifierSharedState unifierState(&iceReporter);
SimplifierPtr simplifier = newSimplifier(NotNull{typeArena}, builtinTypes);
Normalizer normalizer{typeArena, builtinTypes, NotNull{&unifierState}};

if (FFlag::LuauSolverV2)
if (FFlag::LuauAutocompleteUsesModuleForTypeCompatibility)
{
TypeCheckLimits limits;
TypeFunctionRuntime typeFunctionRuntime{
NotNull{&iceReporter}, NotNull{&limits}
}; // TODO: maybe subtyping checks should not invoke user-defined type function runtime
if (module.checkedInNewSolver)
{
TypeCheckLimits limits;
TypeFunctionRuntime typeFunctionRuntime{
NotNull{&iceReporter}, NotNull{&limits}
}; // TODO: maybe subtyping checks should not invoke user-defined type function runtime

unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit;
unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit;

unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit;
unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit;
Subtyping subtyping{
builtinTypes,
NotNull{typeArena},
NotNull{simplifier.get()},
NotNull{&normalizer},
NotNull{&typeFunctionRuntime},
NotNull{&iceReporter}
};

Subtyping subtyping{
builtinTypes, NotNull{typeArena}, NotNull{simplifier.get()}, NotNull{&normalizer}, NotNull{&typeFunctionRuntime}, NotNull{&iceReporter}
};
return subtyping.isSubtype(subTy, superTy, scope).isSubtype;
}
else
{
Unifier unifier(NotNull<Normalizer>{&normalizer}, scope, Location(), Variance::Covariant);

// Cost of normalization can be too high for autocomplete response time requirements
unifier.normalize = false;
unifier.checkInhabited = false;

return subtyping.isSubtype(subTy, superTy, scope).isSubtype;
unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit;
unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit;

return unifier.canUnify(subTy, superTy).empty();
}
}
else
{
Unifier unifier(NotNull<Normalizer>{&normalizer}, scope, Location(), Variance::Covariant);
if (FFlag::LuauSolverV2)
{
TypeCheckLimits limits;
TypeFunctionRuntime typeFunctionRuntime{
NotNull{&iceReporter}, NotNull{&limits}
}; // TODO: maybe subtyping checks should not invoke user-defined type function runtime

// Cost of normalization can be too high for autocomplete response time requirements
unifier.normalize = false;
unifier.checkInhabited = false;
unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit;
unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit;

if (FFlag::LuauAutocompleteUseLimits)
Subtyping subtyping{
builtinTypes,
NotNull{typeArena},
NotNull{simplifier.get()},
NotNull{&normalizer},
NotNull{&typeFunctionRuntime},
NotNull{&iceReporter}
};

return subtyping.isSubtype(subTy, superTy, scope).isSubtype;
}
else
{
Unifier unifier(NotNull<Normalizer>{&normalizer}, scope, Location(), Variance::Covariant);

// Cost of normalization can be too high for autocomplete response time requirements
unifier.normalize = false;
unifier.checkInhabited = false;
unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit;
unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit;
}

return unifier.canUnify(subTy, superTy).empty();
return unifier.canUnify(subTy, superTy).empty();
}
}
}

Expand All @@ -209,10 +258,10 @@ static TypeCorrectKind checkTypeCorrectKind(

TypeId expectedType = follow(*typeAtPosition);

auto checkFunctionType = [typeArena, builtinTypes, moduleScope, &expectedType](const FunctionType* ftv)
auto checkFunctionType = [typeArena, builtinTypes, moduleScope, &expectedType, &module](const FunctionType* ftv)
{
if (std::optional<TypeId> firstRetTy = first(ftv->retTypes))
return checkTypeMatch(*firstRetTy, expectedType, moduleScope, typeArena, builtinTypes);
return checkTypeMatch(module, *firstRetTy, expectedType, moduleScope, typeArena, builtinTypes);

return false;
};
Expand All @@ -235,7 +284,7 @@ static TypeCorrectKind checkTypeCorrectKind(
}
}

return checkTypeMatch(ty, expectedType, moduleScope, typeArena, builtinTypes) ? TypeCorrectKind::Correct : TypeCorrectKind::None;
return checkTypeMatch(module, ty, expectedType, moduleScope, typeArena, builtinTypes) ? TypeCorrectKind::Correct : TypeCorrectKind::None;
}

enum class PropIndexType
Expand Down Expand Up @@ -286,7 +335,7 @@ static void autocompleteProps(
// When called with '.', but declared with 'self', it is considered invalid if first argument is compatible
if (std::optional<TypeId> firstArgTy = first(ftv->argTypes))
{
if (checkTypeMatch(rootTy, *firstArgTy, NotNull{module.getModuleScope().get()}, typeArena, builtinTypes))
if (checkTypeMatch(module, rootTy, *firstArgTy, NotNull{module.getModuleScope().get()}, typeArena, builtinTypes))
return calledWithSelf;
}

Expand Down Expand Up @@ -1714,6 +1763,7 @@ AutocompleteResult autocomplete_(
StringCompletionCallback callback
)
{
LUAU_TIMETRACE_SCOPE("Luau::autocomplete_", "AutocompleteCore");
AstNode* node = ancestry.back();

AstExprConstantNil dummy{Location{}};
Expand Down
Loading