mirror of
https://github.com/luau-lang/luau.git
synced 2025-01-19 17:28:06 +00:00
Sync to origin/release/656
This commit is contained in:
parent
906a00d498
commit
765591c22f
90 changed files with 1482 additions and 754 deletions
|
@ -15,6 +15,12 @@ namespace Luau
|
||||||
{
|
{
|
||||||
struct FrontendOptions;
|
struct FrontendOptions;
|
||||||
|
|
||||||
|
enum class FragmentTypeCheckStatus
|
||||||
|
{
|
||||||
|
Success,
|
||||||
|
SkipAutocomplete,
|
||||||
|
};
|
||||||
|
|
||||||
struct FragmentAutocompleteAncestryResult
|
struct FragmentAutocompleteAncestryResult
|
||||||
{
|
{
|
||||||
DenseHashMap<AstName, AstLocal*> localMap{AstName()};
|
DenseHashMap<AstName, AstLocal*> localMap{AstName()};
|
||||||
|
@ -29,6 +35,7 @@ struct FragmentParseResult
|
||||||
AstStatBlock* root = nullptr;
|
AstStatBlock* root = nullptr;
|
||||||
std::vector<AstNode*> ancestry;
|
std::vector<AstNode*> ancestry;
|
||||||
AstStat* nearestStatement = nullptr;
|
AstStat* nearestStatement = nullptr;
|
||||||
|
std::vector<Comment> commentLocations;
|
||||||
std::unique_ptr<Allocator> alloc = std::make_unique<Allocator>();
|
std::unique_ptr<Allocator> alloc = std::make_unique<Allocator>();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -56,7 +63,7 @@ FragmentParseResult parseFragment(
|
||||||
std::optional<Position> fragmentEndPosition
|
std::optional<Position> fragmentEndPosition
|
||||||
);
|
);
|
||||||
|
|
||||||
FragmentTypeCheckResult typecheckFragment(
|
std::pair<FragmentTypeCheckStatus, FragmentTypeCheckResult> typecheckFragment(
|
||||||
Frontend& frontend,
|
Frontend& frontend,
|
||||||
const ModuleName& moduleName,
|
const ModuleName& moduleName,
|
||||||
const Position& cursorPos,
|
const Position& cursorPos,
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauIncrementalAutocompleteCommentDetection)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -55,6 +57,7 @@ struct SourceModule
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool isWithinComment(const std::vector<Comment>& commentLocations, Position pos);
|
||||||
bool isWithinComment(const SourceModule& sourceModule, Position pos);
|
bool isWithinComment(const SourceModule& sourceModule, Position pos);
|
||||||
bool isWithinComment(const ParseResult& result, Position pos);
|
bool isWithinComment(const ParseResult& result, Position pos);
|
||||||
|
|
||||||
|
|
|
@ -95,6 +95,8 @@ struct Scope
|
||||||
// we need that the generic type `T` in both cases is the same, so we use a cache.
|
// 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, TypeId> typeAliasTypeParameters;
|
||||||
std::unordered_map<Name, TypePackId> typeAliasTypePackParameters;
|
std::unordered_map<Name, TypePackId> typeAliasTypePackParameters;
|
||||||
|
|
||||||
|
std::optional<std::vector<TypeId>> interiorFreeTypes;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Returns true iff the left scope encloses the right scope. A Scope* equal to
|
// Returns true iff the left scope encloses the right scope. A Scope* equal to
|
||||||
|
|
|
@ -280,4 +280,13 @@ std::vector<TypeId> findBlockedTypesIn(AstExprTable* expr, NotNull<DenseHashMap<
|
||||||
*/
|
*/
|
||||||
std::vector<TypeId> findBlockedArgTypesIn(AstExprCall* expr, NotNull<DenseHashMap<const AstExpr*, TypeId>> astTypes);
|
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);
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -177,7 +177,6 @@ void AnyTypeSummary::visit(const Scope* scope, AstStatReturn* ret, const Module*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnyTypeSummary::visit(const Scope* scope, AstStatLocal* local, const Module* module, NotNull<BuiltinTypes> builtinTypes)
|
void AnyTypeSummary::visit(const Scope* scope, AstStatLocal* local, const Module* module, NotNull<BuiltinTypes> builtinTypes)
|
||||||
|
|
|
@ -25,6 +25,7 @@ LUAU_FASTINT(LuauTypeInferIterationLimit)
|
||||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAutocompleteRefactorsForIncrementalAutocomplete)
|
LUAU_FASTFLAGVARIABLE(LuauAutocompleteRefactorsForIncrementalAutocomplete)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauAutocompleteUseLimits)
|
||||||
|
|
||||||
static const std::unordered_set<std::string> kStatementStartingKeywords =
|
static const std::unordered_set<std::string> kStatementStartingKeywords =
|
||||||
{"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"};
|
{"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"};
|
||||||
|
@ -177,6 +178,12 @@ static bool checkTypeMatch(TypeId subTy, TypeId superTy, NotNull<Scope> scope, T
|
||||||
unifier.normalize = false;
|
unifier.normalize = false;
|
||||||
unifier.checkInhabited = false;
|
unifier.checkInhabited = false;
|
||||||
|
|
||||||
|
if (FFlag::LuauAutocompleteUseLimits)
|
||||||
|
{
|
||||||
|
unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit;
|
||||||
|
unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit;
|
||||||
|
}
|
||||||
|
|
||||||
return unifier.canUnify(subTy, superTy).empty();
|
return unifier.canUnify(subTy, superTy).empty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -611,7 +611,9 @@ static void dcrMagicFunctionTypeCheckFormat(MagicFunctionTypeCheckContext contex
|
||||||
if (!fmt)
|
if (!fmt)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauStringFormatArityFix)
|
if (FFlag::LuauStringFormatArityFix)
|
||||||
context.typechecker->reportError(CountMismatch{1, std::nullopt, 0, CountMismatch::Arg, true, "string.format"}, context.callSite->location);
|
context.typechecker->reportError(
|
||||||
|
CountMismatch{1, std::nullopt, 0, CountMismatch::Arg, true, "string.format"}, context.callSite->location
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,6 @@ struct ReferenceCountInitializer : TypeOnceVisitor
|
||||||
// of this type, hence:
|
// of this type, hence:
|
||||||
return !FFlag::LuauDontRefCountTypesInTypeFunctions;
|
return !FFlag::LuauDontRefCountTypesInTypeFunctions;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool isReferenceCountedType(const TypeId typ)
|
bool isReferenceCountedType(const TypeId typ)
|
||||||
|
|
|
@ -31,14 +31,15 @@
|
||||||
LUAU_FASTINT(LuauCheckRecursionLimit)
|
LUAU_FASTINT(LuauCheckRecursionLimit)
|
||||||
LUAU_FASTFLAG(DebugLuauLogSolverToJson)
|
LUAU_FASTFLAG(DebugLuauLogSolverToJson)
|
||||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||||
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
|
||||||
LUAU_FASTFLAG(LuauTypestateBuiltins2)
|
LUAU_FASTFLAG(LuauTypestateBuiltins2)
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunUpdateAllEnvs)
|
LUAU_FASTFLAG(LuauUserTypeFunUpdateAllEnvs)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNewSolverVisitErrorExprLvalues)
|
LUAU_FASTFLAGVARIABLE(LuauNewSolverVisitErrorExprLvalues)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunExportedAndLocal)
|
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunExportedAndLocal)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauNewSolverPrePopulateClasses)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNewSolverPopulateTableLocations)
|
LUAU_FASTFLAGVARIABLE(LuauNewSolverPopulateTableLocations)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunNoExtraConstraint)
|
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunNoExtraConstraint)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauTrackInteriorFreeTypesOnScope)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(InferGlobalTypes)
|
LUAU_FASTFLAGVARIABLE(InferGlobalTypes)
|
||||||
|
|
||||||
|
@ -232,8 +233,17 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
|
||||||
Checkpoint end = checkpoint(this);
|
Checkpoint end = checkpoint(this);
|
||||||
|
|
||||||
TypeId result = arena->addType(BlockedType{});
|
TypeId result = arena->addType(BlockedType{});
|
||||||
NotNull<Constraint> genConstraint =
|
NotNull<Constraint> genConstraint = addConstraint(
|
||||||
addConstraint(scope, block->location, GeneralizationConstraint{result, moduleFnTy, std::move(interiorTypes.back())});
|
scope,
|
||||||
|
block->location,
|
||||||
|
GeneralizationConstraint{
|
||||||
|
result, moduleFnTy, FFlag::LuauTrackInteriorFreeTypesOnScope ? std::vector<TypeId>{} : std::move(interiorTypes.back())
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||||
|
scope->interiorFreeTypes = std::move(interiorTypes.back());
|
||||||
|
|
||||||
getMutable<BlockedType>(result)->setOwner(genConstraint);
|
getMutable<BlockedType>(result)->setOwner(genConstraint);
|
||||||
forEachConstraint(
|
forEachConstraint(
|
||||||
start,
|
start,
|
||||||
|
@ -302,10 +312,20 @@ void ConstraintGenerator::visitFragmentRoot(const ScopePtr& resumeScope, AstStat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TypeId ConstraintGenerator::freshType(const ScopePtr& scope)
|
TypeId ConstraintGenerator::freshType(const ScopePtr& scope)
|
||||||
|
{
|
||||||
|
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||||
|
{
|
||||||
|
auto ft = Luau::freshType(arena, builtinTypes, scope.get());
|
||||||
|
interiorTypes.back().push_back(ft);
|
||||||
|
return ft;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
return Luau::freshType(arena, builtinTypes, scope.get());
|
return Luau::freshType(arena, builtinTypes, scope.get());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TypePackId ConstraintGenerator::freshTypePack(const ScopePtr& scope)
|
TypePackId ConstraintGenerator::freshTypePack(const ScopePtr& scope)
|
||||||
{
|
{
|
||||||
|
@ -667,6 +687,7 @@ void ConstraintGenerator::applyRefinements(const ScopePtr& scope, Location locat
|
||||||
void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* block)
|
void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* block)
|
||||||
{
|
{
|
||||||
std::unordered_map<Name, Location> aliasDefinitionLocations;
|
std::unordered_map<Name, Location> aliasDefinitionLocations;
|
||||||
|
std::unordered_map<Name, Location> classDefinitionLocations;
|
||||||
|
|
||||||
// In order to enable mutually-recursive type aliases, we need to
|
// In order to enable mutually-recursive type aliases, we need to
|
||||||
// populate the type bindings before we actually check any of the
|
// populate the type bindings before we actually check any of the
|
||||||
|
@ -773,6 +794,32 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc
|
||||||
|
|
||||||
aliasDefinitionLocations[function->name.value] = function->location;
|
aliasDefinitionLocations[function->name.value] = function->location;
|
||||||
}
|
}
|
||||||
|
else if (auto classDeclaration = stat->as<AstStatDeclareClass>())
|
||||||
|
{
|
||||||
|
if (!FFlag::LuauNewSolverPrePopulateClasses)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (scope->exportedTypeBindings.count(classDeclaration->name.value))
|
||||||
|
{
|
||||||
|
auto it = classDefinitionLocations.find(classDeclaration->name.value);
|
||||||
|
LUAU_ASSERT(it != classDefinitionLocations.end());
|
||||||
|
reportError(classDeclaration->location, DuplicateTypeDefinition{classDeclaration->name.value, it->second});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A class might have no name if the code is syntactically
|
||||||
|
// illegal. We mustn't prepopulate anything in this case.
|
||||||
|
if (classDeclaration->name == kParseNameError)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ScopePtr defnScope = childScope(classDeclaration, scope);
|
||||||
|
|
||||||
|
TypeId initialType = arena->addType(BlockedType{});
|
||||||
|
TypeFun initialFun{initialType};
|
||||||
|
scope->exportedTypeBindings[classDeclaration->name.value] = std::move(initialFun);
|
||||||
|
|
||||||
|
classDefinitionLocations[classDeclaration->name.value] = classDeclaration->location;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauUserTypeFunExportedAndLocal)
|
if (FFlag::LuauUserTypeFunExportedAndLocal)
|
||||||
|
@ -1623,6 +1670,11 @@ static bool isMetamethod(const Name& name)
|
||||||
|
|
||||||
ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClass* declaredClass)
|
ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClass* declaredClass)
|
||||||
{
|
{
|
||||||
|
// If a class with the same name was already defined, we skip over
|
||||||
|
auto bindingIt = scope->exportedTypeBindings.find(declaredClass->name.value);
|
||||||
|
if (FFlag::LuauNewSolverPrePopulateClasses && bindingIt == scope->exportedTypeBindings.end())
|
||||||
|
return ControlFlow::None;
|
||||||
|
|
||||||
std::optional<TypeId> superTy = std::make_optional(builtinTypes->classType);
|
std::optional<TypeId> superTy = std::make_optional(builtinTypes->classType);
|
||||||
if (declaredClass->superName)
|
if (declaredClass->superName)
|
||||||
{
|
{
|
||||||
|
@ -1637,6 +1689,9 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClas
|
||||||
|
|
||||||
// We don't have generic classes, so this assertion _should_ never be hit.
|
// We don't have generic classes, so this assertion _should_ never be hit.
|
||||||
LUAU_ASSERT(lookupType->typeParams.size() == 0 && lookupType->typePackParams.size() == 0);
|
LUAU_ASSERT(lookupType->typeParams.size() == 0 && lookupType->typePackParams.size() == 0);
|
||||||
|
if (FFlag::LuauNewSolverPrePopulateClasses)
|
||||||
|
superTy = follow(lookupType->type);
|
||||||
|
else
|
||||||
superTy = lookupType->type;
|
superTy = lookupType->type;
|
||||||
|
|
||||||
if (!get<ClassType>(follow(*superTy)))
|
if (!get<ClassType>(follow(*superTy)))
|
||||||
|
@ -1660,6 +1715,13 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClas
|
||||||
|
|
||||||
ctv->metatable = metaTy;
|
ctv->metatable = metaTy;
|
||||||
|
|
||||||
|
|
||||||
|
if (FFlag::LuauNewSolverPrePopulateClasses)
|
||||||
|
{
|
||||||
|
TypeId classBindTy = bindingIt->second.type;
|
||||||
|
emplaceType<BoundType>(asMutable(classBindTy), classTy);
|
||||||
|
}
|
||||||
|
else
|
||||||
scope->exportedTypeBindings[className] = TypeFun{{}, classTy};
|
scope->exportedTypeBindings[className] = TypeFun{{}, classTy};
|
||||||
|
|
||||||
if (declaredClass->indexer)
|
if (declaredClass->indexer)
|
||||||
|
@ -2365,8 +2427,17 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprFunction* fun
|
||||||
Checkpoint endCheckpoint = checkpoint(this);
|
Checkpoint endCheckpoint = checkpoint(this);
|
||||||
|
|
||||||
TypeId generalizedTy = arena->addType(BlockedType{});
|
TypeId generalizedTy = arena->addType(BlockedType{});
|
||||||
NotNull<Constraint> gc =
|
NotNull<Constraint> gc = addConstraint(
|
||||||
addConstraint(sig.signatureScope, func->location, GeneralizationConstraint{generalizedTy, sig.signature, std::move(interiorTypes.back())});
|
sig.signatureScope,
|
||||||
|
func->location,
|
||||||
|
GeneralizationConstraint{
|
||||||
|
generalizedTy, sig.signature, FFlag::LuauTrackInteriorFreeTypesOnScope ? std::vector<TypeId>{} : std::move(interiorTypes.back())
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||||
|
sig.signatureScope->interiorFreeTypes = std::move(interiorTypes.back());
|
||||||
|
|
||||||
getMutable<BlockedType>(generalizedTy)->setOwner(gc);
|
getMutable<BlockedType>(generalizedTy)->setOwner(gc);
|
||||||
interiorTypes.pop_back();
|
interiorTypes.pop_back();
|
||||||
|
|
||||||
|
@ -3898,19 +3969,6 @@ TypeId ConstraintGenerator::createTypeFunctionInstance(
|
||||||
|
|
||||||
TypeId ConstraintGenerator::simplifyUnion(const ScopePtr& scope, Location location, TypeId left, TypeId right)
|
TypeId ConstraintGenerator::simplifyUnion(const ScopePtr& scope, Location location, TypeId left, TypeId right)
|
||||||
{
|
{
|
||||||
if (FFlag::DebugLuauEqSatSimplification)
|
|
||||||
{
|
|
||||||
TypeId ty = arena->addType(UnionType{{left, right}});
|
|
||||||
std::optional<EqSatSimplificationResult> res = eqSatSimplify(simplifier, ty);
|
|
||||||
if (!res)
|
|
||||||
return ty;
|
|
||||||
|
|
||||||
for (TypeId tyFun : res->newTypeFunctions)
|
|
||||||
addConstraint(scope, location, ReduceConstraint{tyFun});
|
|
||||||
|
|
||||||
return res->result;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return ::Luau::simplifyUnion(builtinTypes, arena, left, right).result;
|
return ::Luau::simplifyUnion(builtinTypes, arena, left, right).result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification)
|
||||||
LUAU_FASTFLAG(LuauNewSolverPopulateTableLocations)
|
LUAU_FASTFLAG(LuauNewSolverPopulateTableLocations)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAllowNilAssignmentToIndexer)
|
LUAU_FASTFLAGVARIABLE(LuauAllowNilAssignmentToIndexer)
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunNoExtraConstraint)
|
LUAU_FASTFLAG(LuauUserTypeFunNoExtraConstraint)
|
||||||
|
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -724,8 +725,20 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<co
|
||||||
bind(constraint, c.generalizedType, builtinTypes->errorRecoveryType());
|
bind(constraint, c.generalizedType, builtinTypes->errorRecoveryType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||||
|
{
|
||||||
|
// We check if this member is initialized and then access it, but
|
||||||
|
// clang-tidy doesn't understand this is safe.
|
||||||
|
if (constraint->scope->interiorFreeTypes)
|
||||||
|
for (TypeId ty : *constraint->scope->interiorFreeTypes) // NOLINT(bugprone-unchecked-optional-access)
|
||||||
|
generalize(NotNull{arena}, builtinTypes, constraint->scope, generalizedTypes, ty, /* avoidSealingTables */ false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
for (TypeId ty : c.interiorTypes)
|
for (TypeId ty : c.interiorTypes)
|
||||||
generalize(NotNull{arena}, builtinTypes, constraint->scope, generalizedTypes, ty, /* avoidSealingTables */ false);
|
generalize(NotNull{arena}, builtinTypes, constraint->scope, generalizedTypes, ty, /* avoidSealingTables */ false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -801,6 +814,11 @@ bool ConstraintSolver::tryDispatch(const IterableConstraint& c, NotNull<const Co
|
||||||
{
|
{
|
||||||
TypeId keyTy = freshType(arena, builtinTypes, constraint->scope);
|
TypeId keyTy = freshType(arena, builtinTypes, constraint->scope);
|
||||||
TypeId valueTy = freshType(arena, builtinTypes, constraint->scope);
|
TypeId valueTy = freshType(arena, builtinTypes, constraint->scope);
|
||||||
|
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||||
|
{
|
||||||
|
trackInteriorFreeType(constraint->scope, keyTy);
|
||||||
|
trackInteriorFreeType(constraint->scope, valueTy);
|
||||||
|
}
|
||||||
TypeId tableTy =
|
TypeId tableTy =
|
||||||
arena->addType(TableType{TableType::Props{}, TableIndexer{keyTy, valueTy}, TypeLevel{}, constraint->scope, TableState::Free});
|
arena->addType(TableType{TableType::Props{}, TableIndexer{keyTy, valueTy}, TypeLevel{}, constraint->scope, TableState::Free});
|
||||||
|
|
||||||
|
@ -1445,7 +1463,8 @@ bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNull<con
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (expr->is<AstExprConstantBool>() || expr->is<AstExprConstantString>() || expr->is<AstExprConstantNumber>() || expr->is<AstExprConstantNil>())
|
else if (expr->is<AstExprConstantBool>() || expr->is<AstExprConstantString>() || expr->is<AstExprConstantNumber>() ||
|
||||||
|
expr->is<AstExprConstantNil>())
|
||||||
{
|
{
|
||||||
Unifier2 u2{arena, builtinTypes, constraint->scope, NotNull{&iceReporter}};
|
Unifier2 u2{arena, builtinTypes, constraint->scope, NotNull{&iceReporter}};
|
||||||
u2.unify(actualArgTy, expectedArgTy);
|
u2.unify(actualArgTy, expectedArgTy);
|
||||||
|
@ -2061,6 +2080,8 @@ bool ConstraintSolver::tryDispatch(const UnpackConstraint& c, NotNull<const Cons
|
||||||
// constitute any meaningful constraint, so we replace it
|
// constitute any meaningful constraint, so we replace it
|
||||||
// with a free type.
|
// with a free type.
|
||||||
TypeId f = freshType(arena, builtinTypes, constraint->scope);
|
TypeId f = freshType(arena, builtinTypes, constraint->scope);
|
||||||
|
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||||
|
trackInteriorFreeType(constraint->scope, f);
|
||||||
shiftReferences(resultTy, f);
|
shiftReferences(resultTy, f);
|
||||||
emplaceType<BoundType>(asMutable(resultTy), f);
|
emplaceType<BoundType>(asMutable(resultTy), f);
|
||||||
}
|
}
|
||||||
|
@ -2196,6 +2217,11 @@ bool ConstraintSolver::tryDispatchIterableTable(TypeId iteratorTy, const Iterabl
|
||||||
{
|
{
|
||||||
TypeId keyTy = freshType(arena, builtinTypes, constraint->scope);
|
TypeId keyTy = freshType(arena, builtinTypes, constraint->scope);
|
||||||
TypeId valueTy = freshType(arena, builtinTypes, constraint->scope);
|
TypeId valueTy = freshType(arena, builtinTypes, constraint->scope);
|
||||||
|
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||||
|
{
|
||||||
|
trackInteriorFreeType(constraint->scope, keyTy);
|
||||||
|
trackInteriorFreeType(constraint->scope, valueTy);
|
||||||
|
}
|
||||||
TypeId tableTy = arena->addType(TableType{TableState::Sealed, {}, constraint->scope});
|
TypeId tableTy = arena->addType(TableType{TableState::Sealed, {}, constraint->scope});
|
||||||
getMutable<TableType>(tableTy)->indexer = TableIndexer{keyTy, valueTy};
|
getMutable<TableType>(tableTy)->indexer = TableIndexer{keyTy, valueTy};
|
||||||
|
|
||||||
|
@ -2452,6 +2478,8 @@ TablePropLookupResult ConstraintSolver::lookupTableProp(
|
||||||
if (ttv->state == TableState::Free)
|
if (ttv->state == TableState::Free)
|
||||||
{
|
{
|
||||||
TypeId result = freshType(arena, builtinTypes, ttv->scope);
|
TypeId result = freshType(arena, builtinTypes, ttv->scope);
|
||||||
|
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||||
|
trackInteriorFreeType(ttv->scope, result);
|
||||||
switch (context)
|
switch (context)
|
||||||
{
|
{
|
||||||
case ValueContext::RValue:
|
case ValueContext::RValue:
|
||||||
|
@ -2561,6 +2589,9 @@ TablePropLookupResult ConstraintSolver::lookupTableProp(
|
||||||
LUAU_ASSERT(tt);
|
LUAU_ASSERT(tt);
|
||||||
TypeId propType = freshType(arena, builtinTypes, scope);
|
TypeId propType = freshType(arena, builtinTypes, scope);
|
||||||
|
|
||||||
|
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||||
|
trackInteriorFreeType(scope, propType);
|
||||||
|
|
||||||
switch (context)
|
switch (context)
|
||||||
{
|
{
|
||||||
case ValueContext::RValue:
|
case ValueContext::RValue:
|
||||||
|
|
|
@ -1,236 +1,13 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/BuiltinDefinitions.h"
|
#include "Luau/BuiltinDefinitions.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauMathMap)
|
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauVectorDefinitions)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauVectorDefinitionsExtra)
|
LUAU_FASTFLAGVARIABLE(LuauVectorDefinitionsExtra)
|
||||||
|
LUAU_FASTFLAG(LuauBufferBitMethods)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
// TODO: there has to be a better way, like splitting up per library
|
// TODO: there has to be a better way, like splitting up per library
|
||||||
static const std::string kBuiltinDefinitionLuaSrcChecked_DEPRECATED = 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
|
|
||||||
|
|
||||||
@checked 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?)
|
|
||||||
|
|
||||||
@checked 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: @checked (co: thread) -> "dead" | "running" | "normal" | "suspended",
|
|
||||||
wrap: <A..., R...>(f: (A...) -> R...) -> ((A...) -> R...),
|
|
||||||
yield: <A..., R...>(A...) -> R...,
|
|
||||||
isyieldable: () -> boolean,
|
|
||||||
close: @checked (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: @checked (...number) -> string,
|
|
||||||
charpattern: string,
|
|
||||||
codes: @checked (str: string) -> ((string, number) -> (number, number), string, number),
|
|
||||||
codepoint: @checked (str: string, i: number?, j: number?) -> ...number,
|
|
||||||
len: @checked (s: string, i: number?, j: number?) -> (number?, number?),
|
|
||||||
offset: @checked (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
|
|
||||||
|
|
||||||
|
|
||||||
--- Buffer API
|
|
||||||
declare buffer: {
|
|
||||||
create: @checked (size: number) -> buffer,
|
|
||||||
fromstring: @checked (str: string) -> buffer,
|
|
||||||
tostring: @checked (b: buffer) -> string,
|
|
||||||
len: @checked (b: buffer) -> number,
|
|
||||||
copy: @checked (target: buffer, targetOffset: number, source: buffer, sourceOffset: number?, count: number?) -> (),
|
|
||||||
fill: @checked (b: buffer, offset: number, value: number, count: number?) -> (),
|
|
||||||
readi8: @checked (b: buffer, offset: number) -> number,
|
|
||||||
readu8: @checked (b: buffer, offset: number) -> number,
|
|
||||||
readi16: @checked (b: buffer, offset: number) -> number,
|
|
||||||
readu16: @checked (b: buffer, offset: number) -> number,
|
|
||||||
readi32: @checked (b: buffer, offset: number) -> number,
|
|
||||||
readu32: @checked (b: buffer, offset: number) -> number,
|
|
||||||
readf32: @checked (b: buffer, offset: number) -> number,
|
|
||||||
readf64: @checked (b: buffer, offset: number) -> number,
|
|
||||||
writei8: @checked (b: buffer, offset: number, value: number) -> (),
|
|
||||||
writeu8: @checked (b: buffer, offset: number, value: number) -> (),
|
|
||||||
writei16: @checked (b: buffer, offset: number, value: number) -> (),
|
|
||||||
writeu16: @checked (b: buffer, offset: number, value: number) -> (),
|
|
||||||
writei32: @checked (b: buffer, offset: number, value: number) -> (),
|
|
||||||
writeu32: @checked (b: buffer, offset: number, value: number) -> (),
|
|
||||||
writef32: @checked (b: buffer, offset: number, value: number) -> (),
|
|
||||||
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?) -> (),
|
|
||||||
}
|
|
||||||
|
|
||||||
)BUILTIN_SRC";
|
|
||||||
|
|
||||||
static const std::string kBuiltinDefinitionLuaSrcChecked = R"BUILTIN_SRC(
|
static const std::string kBuiltinDefinitionLuaSrcChecked = R"BUILTIN_SRC(
|
||||||
|
|
||||||
declare bit32: {
|
declare bit32: {
|
||||||
|
@ -422,7 +199,9 @@ declare utf8: {
|
||||||
-- Cannot use `typeof` here because it will produce a polytype when we expect a monotype.
|
-- 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
|
declare function unpack<V>(tab: {V}, i: number?, j: number?): ...V
|
||||||
|
|
||||||
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
|
static const std::string kBuiltinDefinitionBufferSrc_DEPRECATED = R"BUILTIN_SRC(
|
||||||
--- Buffer API
|
--- Buffer API
|
||||||
declare buffer: {
|
declare buffer: {
|
||||||
create: @checked (size: number) -> buffer,
|
create: @checked (size: number) -> buffer,
|
||||||
|
@ -453,6 +232,39 @@ declare buffer: {
|
||||||
|
|
||||||
)BUILTIN_SRC";
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
|
static const std::string kBuiltinDefinitionBufferSrc = R"BUILTIN_SRC(
|
||||||
|
--- Buffer API
|
||||||
|
declare buffer: {
|
||||||
|
create: @checked (size: number) -> buffer,
|
||||||
|
fromstring: @checked (str: string) -> buffer,
|
||||||
|
tostring: @checked (b: buffer) -> string,
|
||||||
|
len: @checked (b: buffer) -> number,
|
||||||
|
copy: @checked (target: buffer, targetOffset: number, source: buffer, sourceOffset: number?, count: number?) -> (),
|
||||||
|
fill: @checked (b: buffer, offset: number, value: number, count: number?) -> (),
|
||||||
|
readi8: @checked (b: buffer, offset: number) -> number,
|
||||||
|
readu8: @checked (b: buffer, offset: number) -> number,
|
||||||
|
readi16: @checked (b: buffer, offset: number) -> number,
|
||||||
|
readu16: @checked (b: buffer, offset: number) -> number,
|
||||||
|
readi32: @checked (b: buffer, offset: number) -> number,
|
||||||
|
readu32: @checked (b: buffer, offset: number) -> number,
|
||||||
|
readf32: @checked (b: buffer, offset: number) -> number,
|
||||||
|
readf64: @checked (b: buffer, offset: number) -> number,
|
||||||
|
writei8: @checked (b: buffer, offset: number, value: number) -> (),
|
||||||
|
writeu8: @checked (b: buffer, offset: number, value: number) -> (),
|
||||||
|
writei16: @checked (b: buffer, offset: number, value: number) -> (),
|
||||||
|
writeu16: @checked (b: buffer, offset: number, value: number) -> (),
|
||||||
|
writei32: @checked (b: buffer, offset: number, value: number) -> (),
|
||||||
|
writeu32: @checked (b: buffer, offset: number, value: number) -> (),
|
||||||
|
writef32: @checked (b: buffer, offset: number, value: number) -> (),
|
||||||
|
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_DEPRECATED = R"BUILTIN_SRC(
|
static const std::string kBuiltinDefinitionVectorSrc_DEPRECATED = R"BUILTIN_SRC(
|
||||||
|
|
||||||
-- TODO: this will be replaced with a built-in primitive type
|
-- TODO: this will be replaced with a built-in primitive type
|
||||||
|
@ -511,11 +323,13 @@ declare vector: {
|
||||||
|
|
||||||
std::string getBuiltinDefinitionSource()
|
std::string getBuiltinDefinitionSource()
|
||||||
{
|
{
|
||||||
std::string result = FFlag::LuauMathMap ? kBuiltinDefinitionLuaSrcChecked : kBuiltinDefinitionLuaSrcChecked_DEPRECATED;
|
std::string result = kBuiltinDefinitionLuaSrcChecked;
|
||||||
|
|
||||||
|
result += FFlag::LuauBufferBitMethods ? kBuiltinDefinitionBufferSrc : kBuiltinDefinitionBufferSrc_DEPRECATED;
|
||||||
|
|
||||||
if (FFlag::LuauVectorDefinitionsExtra)
|
if (FFlag::LuauVectorDefinitionsExtra)
|
||||||
result += kBuiltinDefinitionVectorSrc;
|
result += kBuiltinDefinitionVectorSrc;
|
||||||
else if (FFlag::LuauVectorDefinitions)
|
else
|
||||||
result += kBuiltinDefinitionVectorSrc_DEPRECATED;
|
result += kBuiltinDefinitionVectorSrc_DEPRECATED;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -193,9 +193,8 @@ static bool areTerminalAndDefinitelyDisjoint(const EType& lhs, const EType& rhs)
|
||||||
// - Whether one of the enodes is a large semantic set such as TAny,
|
// - Whether one of the enodes is a large semantic set such as TAny,
|
||||||
// TUnknown, or TError.
|
// TUnknown, or TError.
|
||||||
return !(
|
return !(
|
||||||
lhs.index() == rhs.index() ||
|
lhs.index() == rhs.index() || lhs.get<TUnknown>() || rhs.get<TUnknown>() || lhs.get<TAny>() || rhs.get<TAny>() || lhs.get<TNoRefine>() ||
|
||||||
lhs.get<TUnknown>() || rhs.get<TUnknown>() || lhs.get<TAny>() || rhs.get<TAny>() || lhs.get<TNoRefine>() || rhs.get<TNoRefine>() ||
|
rhs.get<TNoRefine>() || lhs.get<TError>() || rhs.get<TError>() || lhs.get<TOpaque>() || rhs.get<TOpaque>()
|
||||||
lhs.get<TError>() || rhs.get<TError>() || lhs.get<TOpaque>() || rhs.get<TOpaque>()
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -694,7 +693,8 @@ TypeId flattenTableNode(
|
||||||
StringId propName = t->propNames[i];
|
StringId propName = t->propNames[i];
|
||||||
const Id propType = t->propTypes()[i];
|
const Id propType = t->propTypes()[i];
|
||||||
|
|
||||||
resultTable.props[strings.asString(propName)] = Property{fromId(egraph, strings, builtinTypes, arena, bestNodes, seen, newTypeFunctions, propType)};
|
resultTable.props[strings.asString(propName)] =
|
||||||
|
Property{fromId(egraph, strings, builtinTypes, arena, bestNodes, seen, newTypeFunctions, propType)};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -942,7 +942,15 @@ std::string mkDesc(
|
||||||
return rule + ":" + rulePadding + fromIdStr + toString(fromTy, opts) + " <=> " + toIdStr + toString(toTy, opts);
|
return rule + ":" + rulePadding + fromIdStr + toString(fromTy, opts) + " <=> " + toIdStr + toString(toTy, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string mkDesc(EGraph& egraph, const StringCache& strings, NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, Id from, Id to, const std::string& rule)
|
std::string mkDesc(
|
||||||
|
EGraph& egraph,
|
||||||
|
const StringCache& strings,
|
||||||
|
NotNull<TypeArena> arena,
|
||||||
|
NotNull<BuiltinTypes> builtinTypes,
|
||||||
|
Id from,
|
||||||
|
Id to,
|
||||||
|
const std::string& rule
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauLogSimplification)
|
if (!FFlag::DebugLuauLogSimplification)
|
||||||
return "";
|
return "";
|
||||||
|
@ -1879,7 +1887,12 @@ void Simplifier::intersectWithNegatedClass(Id id)
|
||||||
isTag<SBoolean>(iNode) || isTag<SString>(iNode) || isTag<TFunction>(iNode) || isTag<TNever>(iNode))
|
isTag<SBoolean>(iNode) || isTag<SString>(iNode) || isTag<TFunction>(iNode) || isTag<TNever>(iNode))
|
||||||
{
|
{
|
||||||
// eg string & ~SomeClass
|
// eg string & ~SomeClass
|
||||||
subst(id, iId, "intersectClassWithNegatedClass", {{id, intersectionIndex}, {iId, iIndex}, {jId, negationIndex}, {negated, negatedClassIndex}});
|
subst(
|
||||||
|
id,
|
||||||
|
iId,
|
||||||
|
"intersectClassWithNegatedClass",
|
||||||
|
{{id, intersectionIndex}, {iId, iIndex}, {jId, negationIndex}, {negated, negatedClassIndex}}
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1892,7 +1905,12 @@ void Simplifier::intersectWithNegatedClass(Id id)
|
||||||
// This cannot be meaningfully reduced.
|
// This cannot be meaningfully reduced.
|
||||||
continue;
|
continue;
|
||||||
case RightSuper:
|
case RightSuper:
|
||||||
subst(id, egraph.add(TNever{}), "intersectClassWithNegatedClass", {{id, intersectionIndex}, {iId, iIndex}, {jId, negationIndex}, {negated, negatedClassIndex}});
|
subst(
|
||||||
|
id,
|
||||||
|
egraph.add(TNever{}),
|
||||||
|
"intersectClassWithNegatedClass",
|
||||||
|
{{id, intersectionIndex}, {iId, iIndex}, {jId, negationIndex}, {negated, negatedClassIndex}}
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
case Unrelated:
|
case Unrelated:
|
||||||
// Part & ~Folder == Part
|
// Part & ~Folder == Part
|
||||||
|
@ -1906,7 +1924,12 @@ void Simplifier::intersectWithNegatedClass(Id id)
|
||||||
}
|
}
|
||||||
|
|
||||||
Id substId = egraph.add(Intersection{newParts.begin(), newParts.end()});
|
Id substId = egraph.add(Intersection{newParts.begin(), newParts.end()});
|
||||||
subst(id, substId, "intersectClassWithNegatedClass", {{id, intersectionIndex}, {iId, iIndex}, {jId, negationIndex}, {negated, negatedClassIndex}});
|
subst(
|
||||||
|
id,
|
||||||
|
substId,
|
||||||
|
"intersectClassWithNegatedClass",
|
||||||
|
{{id, intersectionIndex}, {iId, iIndex}, {jId, negationIndex}, {negated, negatedClassIndex}}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -245,7 +245,6 @@ FragmentParseResult parseFragment(
|
||||||
opts.captureComments = true;
|
opts.captureComments = true;
|
||||||
opts.parseFragment = FragmentParseResumeSettings{std::move(result.localMap), std::move(result.localStack), startPos};
|
opts.parseFragment = FragmentParseResumeSettings{std::move(result.localMap), std::move(result.localStack), startPos};
|
||||||
ParseResult p = Luau::Parser::parse(srcStart, parseLength, *nameTbl, *fragmentResult.alloc.get(), opts);
|
ParseResult p = Luau::Parser::parse(srcStart, parseLength, *nameTbl, *fragmentResult.alloc.get(), opts);
|
||||||
|
|
||||||
std::vector<AstNode*> fabricatedAncestry = std::move(result.ancestry);
|
std::vector<AstNode*> fabricatedAncestry = std::move(result.ancestry);
|
||||||
|
|
||||||
// Get the ancestry for the fragment at the offset cursor position.
|
// Get the ancestry for the fragment at the offset cursor position.
|
||||||
|
@ -258,6 +257,7 @@ FragmentParseResult parseFragment(
|
||||||
fragmentResult.root = std::move(p.root);
|
fragmentResult.root = std::move(p.root);
|
||||||
fragmentResult.ancestry = std::move(fabricatedAncestry);
|
fragmentResult.ancestry = std::move(fabricatedAncestry);
|
||||||
fragmentResult.nearestStatement = nearestStatement;
|
fragmentResult.nearestStatement = nearestStatement;
|
||||||
|
fragmentResult.commentLocations = std::move(p.commentLocations);
|
||||||
return fragmentResult;
|
return fragmentResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,7 +444,7 @@ FragmentTypeCheckResult typecheckFragment_(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
FragmentTypeCheckResult typecheckFragment(
|
std::pair<FragmentTypeCheckStatus, FragmentTypeCheckResult> typecheckFragment(
|
||||||
Frontend& frontend,
|
Frontend& frontend,
|
||||||
const ModuleName& moduleName,
|
const ModuleName& moduleName,
|
||||||
const Position& cursorPos,
|
const Position& cursorPos,
|
||||||
|
@ -469,12 +469,15 @@ FragmentTypeCheckResult typecheckFragment(
|
||||||
}
|
}
|
||||||
|
|
||||||
FragmentParseResult parseResult = parseFragment(*sourceModule, src, cursorPos, fragmentEndPosition);
|
FragmentParseResult parseResult = parseFragment(*sourceModule, src, cursorPos, fragmentEndPosition);
|
||||||
|
if (isWithinComment(parseResult.commentLocations, fragmentEndPosition.value_or(cursorPos)))
|
||||||
|
return {FragmentTypeCheckStatus::SkipAutocomplete, {}};
|
||||||
|
|
||||||
FrontendOptions frontendOptions = opts.value_or(frontend.options);
|
FrontendOptions frontendOptions = opts.value_or(frontend.options);
|
||||||
const ScopePtr& closestScope = findClosestScope(module, parseResult.nearestStatement);
|
const ScopePtr& closestScope = findClosestScope(module, parseResult.nearestStatement);
|
||||||
FragmentTypeCheckResult result =
|
FragmentTypeCheckResult result =
|
||||||
typecheckFragment_(frontend, parseResult.root, module, closestScope, cursorPos, std::move(parseResult.alloc), frontendOptions);
|
typecheckFragment_(frontend, parseResult.root, module, closestScope, cursorPos, std::move(parseResult.alloc), frontendOptions);
|
||||||
result.ancestry = std::move(parseResult.ancestry);
|
result.ancestry = std::move(parseResult.ancestry);
|
||||||
return result;
|
return {FragmentTypeCheckStatus::Success, result};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -498,7 +501,14 @@ FragmentAutocompleteResult fragmentAutocomplete(
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto tcResult = typecheckFragment(frontend, moduleName, cursorPosition, opts, src, fragmentEndPosition);
|
// If the cursor is within a comment in the stale source module we should avoid providing a recommendation
|
||||||
|
if (isWithinComment(*sourceModule, fragmentEndPosition.value_or(cursorPosition)))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
auto [tcStatus, tcResult] = typecheckFragment(frontend, moduleName, cursorPosition, opts, src, fragmentEndPosition);
|
||||||
|
if (tcStatus == FragmentTypeCheckStatus::SkipAutocomplete)
|
||||||
|
return {};
|
||||||
|
|
||||||
auto globalScope = (opts && opts->forAutocomplete) ? frontend.globalsForAutocomplete.globalScope.get() : frontend.globals.globalScope.get();
|
auto globalScope = (opts && opts->forAutocomplete) ? frontend.globalsForAutocomplete.globalScope.get() : frontend.globals.globalScope.get();
|
||||||
|
|
||||||
TypeArena arenaForFragmentAutocomplete;
|
TypeArena arenaForFragmentAutocomplete;
|
||||||
|
|
|
@ -977,7 +977,8 @@ struct TypeCacher : TypeOnceVisitor
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(TypePackId tp, const BoundTypePack& btp) override {
|
bool visit(TypePackId tp, const BoundTypePack& btp) override
|
||||||
|
{
|
||||||
traverse(btp.boundTo);
|
traverse(btp.boundTo);
|
||||||
if (isUncacheable(btp.boundTo))
|
if (isUncacheable(btp.boundTo))
|
||||||
markUncacheable(tp);
|
markUncacheable(tp);
|
||||||
|
|
|
@ -15,11 +15,12 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2);
|
LUAU_FASTFLAG(LuauSolverV2);
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauIncrementalAutocompleteCommentDetection)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
static bool contains(Position pos, Comment comment)
|
static bool contains_DEPRECATED(Position pos, Comment comment)
|
||||||
{
|
{
|
||||||
if (comment.location.contains(pos))
|
if (comment.location.contains(pos))
|
||||||
return true;
|
return true;
|
||||||
|
@ -32,7 +33,22 @@ static bool contains(Position pos, Comment comment)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isWithinComment(const std::vector<Comment>& commentLocations, Position pos)
|
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
|
||||||
|
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)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isWithinComment(const std::vector<Comment>& commentLocations, Position pos)
|
||||||
{
|
{
|
||||||
auto iter = std::lower_bound(
|
auto iter = std::lower_bound(
|
||||||
commentLocations.begin(),
|
commentLocations.begin(),
|
||||||
|
@ -40,6 +56,11 @@ static bool isWithinComment(const std::vector<Comment>& commentLocations, Positi
|
||||||
Comment{Lexeme::Comment, Location{pos, pos}},
|
Comment{Lexeme::Comment, Location{pos, pos}},
|
||||||
[](const Comment& a, const Comment& b)
|
[](const Comment& a, const Comment& b)
|
||||||
{
|
{
|
||||||
|
if (FFlag::LuauIncrementalAutocompleteCommentDetection)
|
||||||
|
{
|
||||||
|
if (a.type == Lexeme::Comment)
|
||||||
|
return a.location.end.line < b.location.end.line;
|
||||||
|
}
|
||||||
return a.location.end < b.location.end;
|
return a.location.end < b.location.end;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -47,7 +68,7 @@ static bool isWithinComment(const std::vector<Comment>& commentLocations, Positi
|
||||||
if (iter == commentLocations.end())
|
if (iter == commentLocations.end())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (contains(pos, *iter))
|
if (FFlag::LuauIncrementalAutocompleteCommentDetection ? contains(pos, *iter) : contains_DEPRECATED(pos, *iter))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Due to the nature of std::lower_bound, it is possible that iter points at a comment that ends
|
// Due to the nature of std::lower_bound, it is possible that iter points at a comment that ends
|
||||||
|
|
|
@ -1809,7 +1809,8 @@ NormalizationResult Normalizer::unionNormalWithTy(
|
||||||
}
|
}
|
||||||
else if (get<UnknownType>(here.tops))
|
else if (get<UnknownType>(here.tops))
|
||||||
return NormalizationResult::True;
|
return NormalizationResult::True;
|
||||||
else if (get<GenericType>(there) || get<FreeType>(there) || get<BlockedType>(there) || get<PendingExpansionType>(there) || get<TypeFunctionInstanceType>(there))
|
else if (get<GenericType>(there) || get<FreeType>(there) || get<BlockedType>(there) || get<PendingExpansionType>(there) ||
|
||||||
|
get<TypeFunctionInstanceType>(there))
|
||||||
{
|
{
|
||||||
if (tyvarIndex(there) <= ignoreSmallerTyvars)
|
if (tyvarIndex(there) <= ignoreSmallerTyvars)
|
||||||
return NormalizationResult::True;
|
return NormalizationResult::True;
|
||||||
|
@ -3162,7 +3163,8 @@ NormalizationResult Normalizer::intersectNormalWithTy(
|
||||||
}
|
}
|
||||||
return NormalizationResult::True;
|
return NormalizationResult::True;
|
||||||
}
|
}
|
||||||
else if (get<GenericType>(there) || get<FreeType>(there) || get<BlockedType>(there) || get<PendingExpansionType>(there) || get<TypeFunctionInstanceType>(there))
|
else if (get<GenericType>(there) || get<FreeType>(there) || get<BlockedType>(there) || get<PendingExpansionType>(there) ||
|
||||||
|
get<TypeFunctionInstanceType>(there))
|
||||||
{
|
{
|
||||||
NormalizedType thereNorm{builtinTypes};
|
NormalizedType thereNorm{builtinTypes};
|
||||||
NormalizedType topNorm{builtinTypes};
|
NormalizedType topNorm{builtinTypes};
|
||||||
|
|
|
@ -420,7 +420,8 @@ static std::optional<TypeId> selectOverload(
|
||||||
TypePackId argsPack
|
TypePackId argsPack
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
auto resolver = std::make_unique<OverloadResolver>(builtinTypes, arena, simplifier, normalizer, typeFunctionRuntime, scope, iceReporter, limits, location);
|
auto resolver =
|
||||||
|
std::make_unique<OverloadResolver>(builtinTypes, arena, simplifier, normalizer, typeFunctionRuntime, scope, iceReporter, limits, location);
|
||||||
auto [status, overload] = resolver->selectOverload(fn, argsPack);
|
auto [status, overload] = resolver->selectOverload(fn, argsPack);
|
||||||
|
|
||||||
if (status == OverloadResolver::Analysis::Ok)
|
if (status == OverloadResolver::Analysis::Ok)
|
||||||
|
|
|
@ -1480,9 +1480,8 @@ SubtypingResult Subtyping::isCovariantWith(
|
||||||
|
|
||||||
if (auto variadic = get<VariadicTypePack>(tail); variadic && variadic->hidden)
|
if (auto variadic = get<VariadicTypePack>(tail); variadic && variadic->hidden)
|
||||||
{
|
{
|
||||||
result.orElse(
|
result.orElse(isContravariantWith(env, subFunction->argTypes, arena->addTypePack(TypePack{arguments}), scope)
|
||||||
isContravariantWith(env, subFunction->argTypes, arena->addTypePack(TypePack{arguments}), scope).withBothComponent(TypePath::PackField::Arguments)
|
.withBothComponent(TypePath::PackField::Arguments));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1020,7 +1020,8 @@ void TypeChecker2::visit(AstStatForIn* forInStatement)
|
||||||
{
|
{
|
||||||
reportError(OptionalValueAccess{iteratorTy}, forInStatement->values.data[0]->location);
|
reportError(OptionalValueAccess{iteratorTy}, forInStatement->values.data[0]->location);
|
||||||
}
|
}
|
||||||
else if (std::optional<TypeId> iterMmTy = findMetatableEntry(builtinTypes, module->errors, iteratorTy, "__iter", forInStatement->values.data[0]->location))
|
else if (std::optional<TypeId> iterMmTy =
|
||||||
|
findMetatableEntry(builtinTypes, module->errors, iteratorTy, "__iter", forInStatement->values.data[0]->location))
|
||||||
{
|
{
|
||||||
Instantiation instantiation{TxnLog::empty(), &arena, builtinTypes, TypeLevel{}, scope};
|
Instantiation instantiation{TxnLog::empty(), &arena, builtinTypes, TypeLevel{}, scope};
|
||||||
|
|
||||||
|
|
|
@ -832,7 +832,12 @@ TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
|
||||||
{
|
{
|
||||||
if (FFlag::LuauUserTypeFunPrintToError)
|
if (FFlag::LuauUserTypeFunPrintToError)
|
||||||
return {
|
return {
|
||||||
std::nullopt, Reduction::Erroneous, {}, {}, format("'%s' type function: returned a non-type value", name.value), ctx->typeFunctionRuntime->messages
|
std::nullopt,
|
||||||
|
Reduction::Erroneous,
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
format("'%s' type function: returned a non-type value", name.value),
|
||||||
|
ctx->typeFunctionRuntime->messages
|
||||||
};
|
};
|
||||||
else
|
else
|
||||||
return {std::nullopt, Reduction::Erroneous, {}, {}, format("'%s' type function: returned a non-type value", name.value)};
|
return {std::nullopt, Reduction::Erroneous, {}, {}, format("'%s' type function: returned a non-type value", name.value)};
|
||||||
|
@ -2064,7 +2069,7 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
||||||
if (ctx->solver)
|
if (ctx->solver)
|
||||||
{
|
{
|
||||||
for (TypeId newTf : simplifyResult->newTypeFunctions)
|
for (TypeId newTf : simplifyResult->newTypeFunctions)
|
||||||
ctx->solver->pushConstraint(ctx->scope, ctx->constraint->location, ReduceConstraint{newTf});
|
ctx->pushConstraint(ReduceConstraint{newTf});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {simplifyResult->result, {}};
|
return {simplifyResult->result, {}};
|
||||||
|
|
|
@ -464,7 +464,9 @@ public:
|
||||||
, typeFunctionRuntime(state->ctx->typeFunctionRuntime)
|
, typeFunctionRuntime(state->ctx->typeFunctionRuntime)
|
||||||
, queue({})
|
, queue({})
|
||||||
, types({})
|
, types({})
|
||||||
, packs({}){};
|
, packs({})
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
TypeId deserialize(TypeFunctionTypeId ty)
|
TypeId deserialize(TypeFunctionTypeId ty)
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2);
|
LUAU_FASTFLAG(LuauSolverV2);
|
||||||
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete);
|
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete);
|
||||||
|
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope);
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -318,6 +319,8 @@ TypePack extendTypePack(
|
||||||
{
|
{
|
||||||
FreeType ft{ftp->scope, builtinTypes->neverType, builtinTypes->unknownType};
|
FreeType ft{ftp->scope, builtinTypes->neverType, builtinTypes->unknownType};
|
||||||
t = arena.addType(ft);
|
t = arena.addType(ft);
|
||||||
|
if (FFlag::LuauTrackInteriorFreeTypesOnScope)
|
||||||
|
trackInteriorFreeType(ftp->scope, t);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
t = arena.freshType(ftp->scope);
|
t = arena.freshType(ftp->scope);
|
||||||
|
@ -543,5 +546,21 @@ std::vector<TypeId> findBlockedArgTypesIn(AstExprCall* expr, NotNull<DenseHashMa
|
||||||
return toBlock;
|
return toBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void trackInteriorFreeType(Scope* scope, TypeId ty)
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(FFlag::LuauSolverV2 && FFlag::LuauTrackInteriorFreeTypesOnScope);
|
||||||
|
for (; scope; scope = scope->parent.get())
|
||||||
|
{
|
||||||
|
if (scope->interiorFreeTypes)
|
||||||
|
{
|
||||||
|
scope->interiorFreeTypes->push_back(ty);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// There should at least be *one* generalization constraint per module
|
||||||
|
// where `interiorFreeTypes` is present, which would be the one made
|
||||||
|
// by ConstraintGenerator::visitModuleRoot.
|
||||||
|
LUAU_ASSERT(!"No scopes in parent chain had a present `interiorFreeTypes` member.");
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -45,4 +45,4 @@ private:
|
||||||
size_t offset;
|
size_t offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
} // namespace Luau
|
||||||
|
|
|
@ -14,12 +14,37 @@ struct Position
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const Position& rhs) const;
|
bool operator==(const Position& rhs) const
|
||||||
bool operator!=(const Position& rhs) const;
|
{
|
||||||
bool operator<(const Position& rhs) const;
|
return this->column == rhs.column && this->line == rhs.line;
|
||||||
bool operator>(const Position& rhs) const;
|
}
|
||||||
bool operator<=(const Position& rhs) const;
|
|
||||||
bool operator>=(const Position& rhs) const;
|
bool operator!=(const Position& rhs) const
|
||||||
|
{
|
||||||
|
return !(*this == rhs);
|
||||||
|
}
|
||||||
|
bool operator<(const Position& rhs) const
|
||||||
|
{
|
||||||
|
if (line == rhs.line)
|
||||||
|
return column < rhs.column;
|
||||||
|
else
|
||||||
|
return line < rhs.line;
|
||||||
|
}
|
||||||
|
bool operator>(const Position& rhs) const
|
||||||
|
{
|
||||||
|
if (line == rhs.line)
|
||||||
|
return column > rhs.column;
|
||||||
|
else
|
||||||
|
return line > rhs.line;
|
||||||
|
}
|
||||||
|
bool operator<=(const Position& rhs) const
|
||||||
|
{
|
||||||
|
return *this == rhs || *this < rhs;
|
||||||
|
}
|
||||||
|
bool operator>=(const Position& rhs) const
|
||||||
|
{
|
||||||
|
return *this == rhs || *this > rhs;
|
||||||
|
}
|
||||||
|
|
||||||
void shift(const Position& start, const Position& oldEnd, const Position& newEnd);
|
void shift(const Position& start, const Position& oldEnd, const Position& newEnd);
|
||||||
};
|
};
|
||||||
|
@ -52,8 +77,14 @@ struct Location
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const Location& rhs) const;
|
bool operator==(const Location& rhs) const
|
||||||
bool operator!=(const Location& rhs) const;
|
{
|
||||||
|
return this->begin == rhs.begin && this->end == rhs.end;
|
||||||
|
}
|
||||||
|
bool operator!=(const Location& rhs) const
|
||||||
|
{
|
||||||
|
return !(*this == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
bool encloses(const Location& l) const;
|
bool encloses(const Location& l) const;
|
||||||
bool overlaps(const Location& l) const;
|
bool overlaps(const Location& l) const;
|
||||||
|
|
|
@ -63,4 +63,4 @@ void* Allocator::allocate(size_t size)
|
||||||
return page->data;
|
return page->data;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} // namespace Luau
|
||||||
|
|
|
@ -1151,10 +1151,7 @@ void AstTypePackGeneric::visit(AstVisitor* visitor)
|
||||||
|
|
||||||
bool isLValue(const AstExpr* expr)
|
bool isLValue(const AstExpr* expr)
|
||||||
{
|
{
|
||||||
return expr->is<AstExprLocal>()
|
return expr->is<AstExprLocal>() || expr->is<AstExprGlobal>() || expr->is<AstExprIndexName>() || expr->is<AstExprIndexExpr>();
|
||||||
|| expr->is<AstExprGlobal>()
|
|
||||||
|| expr->is<AstExprIndexName>()
|
|
||||||
|| expr->is<AstExprIndexExpr>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AstName getIdentifier(AstExpr* node)
|
AstName getIdentifier(AstExpr* node)
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LexerResumesFromPosition2)
|
LUAU_FASTFLAGVARIABLE(LexerResumesFromPosition2)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LexerFixInterpStringStart)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -759,7 +761,7 @@ Lexeme Lexer::readNext()
|
||||||
return Lexeme(Location(start, 1), '}');
|
return Lexeme(Location(start, 1), '}');
|
||||||
}
|
}
|
||||||
|
|
||||||
return readInterpolatedStringSection(position(), Lexeme::InterpStringMid, Lexeme::InterpStringEnd);
|
return readInterpolatedStringSection(FFlag::LexerFixInterpStringStart ? start : position(), Lexeme::InterpStringMid, Lexeme::InterpStringEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
case '=':
|
case '=':
|
||||||
|
|
|
@ -4,42 +4,6 @@
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
bool Position::operator==(const Position& rhs) const
|
|
||||||
{
|
|
||||||
return this->column == rhs.column && this->line == rhs.line;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Position::operator!=(const Position& rhs) const
|
|
||||||
{
|
|
||||||
return !(*this == rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Position::operator<(const Position& rhs) const
|
|
||||||
{
|
|
||||||
if (line == rhs.line)
|
|
||||||
return column < rhs.column;
|
|
||||||
else
|
|
||||||
return line < rhs.line;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Position::operator>(const Position& rhs) const
|
|
||||||
{
|
|
||||||
if (line == rhs.line)
|
|
||||||
return column > rhs.column;
|
|
||||||
else
|
|
||||||
return line > rhs.line;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Position::operator<=(const Position& rhs) const
|
|
||||||
{
|
|
||||||
return *this == rhs || *this < rhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Position::operator>=(const Position& rhs) const
|
|
||||||
{
|
|
||||||
return *this == rhs || *this > rhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Position::shift(const Position& start, const Position& oldEnd, const Position& newEnd)
|
void Position::shift(const Position& start, const Position& oldEnd, const Position& newEnd)
|
||||||
{
|
{
|
||||||
if (*this >= start)
|
if (*this >= start)
|
||||||
|
@ -54,16 +18,6 @@ void Position::shift(const Position& start, const Position& oldEnd, const Positi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Location::operator==(const Location& rhs) const
|
|
||||||
{
|
|
||||||
return this->begin == rhs.begin && this->end == rhs.end;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Location::operator!=(const Location& rhs) const
|
|
||||||
{
|
|
||||||
return !(*this == rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Location::encloses(const Location& l) const
|
bool Location::encloses(const Location& l) const
|
||||||
{
|
{
|
||||||
return begin <= l.begin && end >= l.end;
|
return begin <= l.begin && end >= l.end;
|
||||||
|
|
|
@ -18,7 +18,6 @@ LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100)
|
||||||
// flag so that we don't break production games by reverting syntax changes.
|
// flag so that we don't break production games by reverting syntax changes.
|
||||||
// See docs/SyntaxChanges.md for an explanation.
|
// See docs/SyntaxChanges.md for an explanation.
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSolverV2)
|
LUAU_FASTFLAGVARIABLE(LuauSolverV2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunParseExport)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAllowFragmentParsing)
|
LUAU_FASTFLAGVARIABLE(LuauAllowFragmentParsing)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAllowComplexTypesInGenericParams)
|
LUAU_FASTFLAGVARIABLE(LuauAllowComplexTypesInGenericParams)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauErrorRecoveryForTableTypes)
|
LUAU_FASTFLAGVARIABLE(LuauErrorRecoveryForTableTypes)
|
||||||
|
@ -936,12 +935,6 @@ AstStat* Parser::parseTypeFunction(const Location& start, bool exported)
|
||||||
Lexeme matchFn = lexer.current();
|
Lexeme matchFn = lexer.current();
|
||||||
nextLexeme();
|
nextLexeme();
|
||||||
|
|
||||||
if (!FFlag::LuauUserDefinedTypeFunParseExport)
|
|
||||||
{
|
|
||||||
if (exported)
|
|
||||||
report(start, "Type function cannot be exported");
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse the name of the type function
|
// parse the name of the type function
|
||||||
std::optional<Name> fnName = parseNameOpt("type function name");
|
std::optional<Name> fnName = parseNameOpt("type function name");
|
||||||
if (!fnName)
|
if (!fnName)
|
||||||
|
@ -2239,7 +2232,8 @@ std::optional<AstExprBinary::Op> Parser::checkBinaryConfusables(const BinaryOpPr
|
||||||
report(Location(start, next.location), "Unexpected '||'; did you mean 'or'?");
|
report(Location(start, next.location), "Unexpected '||'; did you mean 'or'?");
|
||||||
return AstExprBinary::Or;
|
return AstExprBinary::Or;
|
||||||
}
|
}
|
||||||
else if (curr.type == '!' && next.type == '=' && curr.location.end == next.location.begin && binaryPriority[AstExprBinary::CompareNe].left > limit)
|
else if (curr.type == '!' && next.type == '=' && curr.location.end == next.location.begin &&
|
||||||
|
binaryPriority[AstExprBinary::CompareNe].left > limit)
|
||||||
{
|
{
|
||||||
nextLexeme();
|
nextLexeme();
|
||||||
report(Location(start, next.location), "Unexpected '!='; did you mean '~='?");
|
report(Location(start, next.location), "Unexpected '!='; did you mean '~='?");
|
||||||
|
@ -2587,7 +2581,8 @@ AstExpr* Parser::parseSimpleExpr()
|
||||||
{
|
{
|
||||||
return parseNumber();
|
return parseNumber();
|
||||||
}
|
}
|
||||||
else if (lexer.current().type == Lexeme::RawString || lexer.current().type == Lexeme::QuotedString || lexer.current().type == Lexeme::InterpStringSimple)
|
else if (lexer.current().type == Lexeme::RawString || lexer.current().type == Lexeme::QuotedString ||
|
||||||
|
lexer.current().type == Lexeme::InterpStringSimple)
|
||||||
{
|
{
|
||||||
return parseString();
|
return parseString();
|
||||||
}
|
}
|
||||||
|
|
|
@ -341,7 +341,8 @@ static bool compileFile(const char* name, CompileFormat format, Luau::CodeGen::A
|
||||||
bcb.setDumpFlags(Luau::BytecodeBuilder::Dump_Source | Luau::BytecodeBuilder::Dump_Remarks);
|
bcb.setDumpFlags(Luau::BytecodeBuilder::Dump_Source | Luau::BytecodeBuilder::Dump_Remarks);
|
||||||
bcb.setDumpSource(*source);
|
bcb.setDumpSource(*source);
|
||||||
}
|
}
|
||||||
else if (format == CompileFormat::Codegen || format == CompileFormat::CodegenAsm || format == CompileFormat::CodegenIr || format == CompileFormat::CodegenVerbose)
|
else if (format == CompileFormat::Codegen || format == CompileFormat::CodegenAsm || format == CompileFormat::CodegenIr ||
|
||||||
|
format == CompileFormat::CodegenVerbose)
|
||||||
{
|
{
|
||||||
bcb.setDumpFlags(
|
bcb.setDumpFlags(
|
||||||
Luau::BytecodeBuilder::Dump_Code | Luau::BytecodeBuilder::Dump_Source | Luau::BytecodeBuilder::Dump_Locals |
|
Luau::BytecodeBuilder::Dump_Code | Luau::BytecodeBuilder::Dump_Source | Luau::BytecodeBuilder::Dump_Locals |
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
struct Proto;
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
namespace CodeGen
|
namespace CodeGen
|
||||||
|
@ -23,6 +25,7 @@ struct IrToStringContext
|
||||||
const std::vector<IrBlock>& blocks;
|
const std::vector<IrBlock>& blocks;
|
||||||
const std::vector<IrConst>& constants;
|
const std::vector<IrConst>& constants;
|
||||||
const CfgInfo& cfg;
|
const CfgInfo& cfg;
|
||||||
|
Proto* proto = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
void toString(IrToStringContext& ctx, const IrInst& inst, uint32_t index);
|
void toString(IrToStringContext& ctx, const IrInst& inst, uint32_t index);
|
||||||
|
|
|
@ -235,7 +235,7 @@ static uint8_t getBytecodeConstantTag(Proto* proto, unsigned ki)
|
||||||
return LBC_TYPE_ANY;
|
return LBC_TYPE_ANY;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void applyBuiltinCall(int bfid, BytecodeTypes& types)
|
static void applyBuiltinCall(LuauBuiltinFunction bfid, BytecodeTypes& types)
|
||||||
{
|
{
|
||||||
switch (bfid)
|
switch (bfid)
|
||||||
{
|
{
|
||||||
|
@ -549,6 +549,12 @@ static void applyBuiltinCall(int bfid, BytecodeTypes& types)
|
||||||
types.b = LBC_TYPE_VECTOR;
|
types.b = LBC_TYPE_VECTOR;
|
||||||
types.c = LBC_TYPE_VECTOR; // We can mark optional arguments
|
types.c = LBC_TYPE_VECTOR; // We can mark optional arguments
|
||||||
break;
|
break;
|
||||||
|
case LBF_MATH_LERP:
|
||||||
|
types.result = LBC_TYPE_NUMBER;
|
||||||
|
types.a = LBC_TYPE_NUMBER;
|
||||||
|
types.b = LBC_TYPE_NUMBER;
|
||||||
|
types.c = LBC_TYPE_NUMBER;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -842,7 +848,8 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks)
|
||||||
regTags[ra] = LBC_TYPE_NUMBER;
|
regTags[ra] = LBC_TYPE_NUMBER;
|
||||||
else if (bcType.a == LBC_TYPE_VECTOR && bcType.b == LBC_TYPE_VECTOR)
|
else if (bcType.a == LBC_TYPE_VECTOR && bcType.b == LBC_TYPE_VECTOR)
|
||||||
regTags[ra] = LBC_TYPE_VECTOR;
|
regTags[ra] = LBC_TYPE_VECTOR;
|
||||||
else if (hostHooks.userdataMetamethodBytecodeType && (isCustomUserdataBytecodeType(bcType.a) || isCustomUserdataBytecodeType(bcType.b)))
|
else if (hostHooks.userdataMetamethodBytecodeType &&
|
||||||
|
(isCustomUserdataBytecodeType(bcType.a) || isCustomUserdataBytecodeType(bcType.b)))
|
||||||
regTags[ra] = hostHooks.userdataMetamethodBytecodeType(bcType.a, bcType.b, opcodeToHostMetamethod(op));
|
regTags[ra] = hostHooks.userdataMetamethodBytecodeType(bcType.a, bcType.b, opcodeToHostMetamethod(op));
|
||||||
|
|
||||||
bcType.result = regTags[ra];
|
bcType.result = regTags[ra];
|
||||||
|
@ -873,7 +880,8 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks)
|
||||||
if (bcType.b == LBC_TYPE_NUMBER || bcType.b == LBC_TYPE_VECTOR)
|
if (bcType.b == LBC_TYPE_NUMBER || bcType.b == LBC_TYPE_VECTOR)
|
||||||
regTags[ra] = LBC_TYPE_VECTOR;
|
regTags[ra] = LBC_TYPE_VECTOR;
|
||||||
}
|
}
|
||||||
else if (hostHooks.userdataMetamethodBytecodeType && (isCustomUserdataBytecodeType(bcType.a) || isCustomUserdataBytecodeType(bcType.b)))
|
else if (hostHooks.userdataMetamethodBytecodeType &&
|
||||||
|
(isCustomUserdataBytecodeType(bcType.a) || isCustomUserdataBytecodeType(bcType.b)))
|
||||||
{
|
{
|
||||||
regTags[ra] = hostHooks.userdataMetamethodBytecodeType(bcType.a, bcType.b, opcodeToHostMetamethod(op));
|
regTags[ra] = hostHooks.userdataMetamethodBytecodeType(bcType.a, bcType.b, opcodeToHostMetamethod(op));
|
||||||
}
|
}
|
||||||
|
@ -895,7 +903,8 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks)
|
||||||
|
|
||||||
if (bcType.a == LBC_TYPE_NUMBER && bcType.b == LBC_TYPE_NUMBER)
|
if (bcType.a == LBC_TYPE_NUMBER && bcType.b == LBC_TYPE_NUMBER)
|
||||||
regTags[ra] = LBC_TYPE_NUMBER;
|
regTags[ra] = LBC_TYPE_NUMBER;
|
||||||
else if (hostHooks.userdataMetamethodBytecodeType && (isCustomUserdataBytecodeType(bcType.a) || isCustomUserdataBytecodeType(bcType.b)))
|
else if (hostHooks.userdataMetamethodBytecodeType &&
|
||||||
|
(isCustomUserdataBytecodeType(bcType.a) || isCustomUserdataBytecodeType(bcType.b)))
|
||||||
regTags[ra] = hostHooks.userdataMetamethodBytecodeType(bcType.a, bcType.b, opcodeToHostMetamethod(op));
|
regTags[ra] = hostHooks.userdataMetamethodBytecodeType(bcType.a, bcType.b, opcodeToHostMetamethod(op));
|
||||||
|
|
||||||
bcType.result = regTags[ra];
|
bcType.result = regTags[ra];
|
||||||
|
@ -917,7 +926,8 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks)
|
||||||
regTags[ra] = LBC_TYPE_NUMBER;
|
regTags[ra] = LBC_TYPE_NUMBER;
|
||||||
else if (bcType.a == LBC_TYPE_VECTOR && bcType.b == LBC_TYPE_VECTOR)
|
else if (bcType.a == LBC_TYPE_VECTOR && bcType.b == LBC_TYPE_VECTOR)
|
||||||
regTags[ra] = LBC_TYPE_VECTOR;
|
regTags[ra] = LBC_TYPE_VECTOR;
|
||||||
else if (hostHooks.userdataMetamethodBytecodeType && (isCustomUserdataBytecodeType(bcType.a) || isCustomUserdataBytecodeType(bcType.b)))
|
else if (hostHooks.userdataMetamethodBytecodeType &&
|
||||||
|
(isCustomUserdataBytecodeType(bcType.a) || isCustomUserdataBytecodeType(bcType.b)))
|
||||||
regTags[ra] = hostHooks.userdataMetamethodBytecodeType(bcType.a, bcType.b, opcodeToHostMetamethod(op));
|
regTags[ra] = hostHooks.userdataMetamethodBytecodeType(bcType.a, bcType.b, opcodeToHostMetamethod(op));
|
||||||
|
|
||||||
bcType.result = regTags[ra];
|
bcType.result = regTags[ra];
|
||||||
|
@ -948,7 +958,8 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks)
|
||||||
if (bcType.b == LBC_TYPE_NUMBER || bcType.b == LBC_TYPE_VECTOR)
|
if (bcType.b == LBC_TYPE_NUMBER || bcType.b == LBC_TYPE_VECTOR)
|
||||||
regTags[ra] = LBC_TYPE_VECTOR;
|
regTags[ra] = LBC_TYPE_VECTOR;
|
||||||
}
|
}
|
||||||
else if (hostHooks.userdataMetamethodBytecodeType && (isCustomUserdataBytecodeType(bcType.a) || isCustomUserdataBytecodeType(bcType.b)))
|
else if (hostHooks.userdataMetamethodBytecodeType &&
|
||||||
|
(isCustomUserdataBytecodeType(bcType.a) || isCustomUserdataBytecodeType(bcType.b)))
|
||||||
{
|
{
|
||||||
regTags[ra] = hostHooks.userdataMetamethodBytecodeType(bcType.a, bcType.b, opcodeToHostMetamethod(op));
|
regTags[ra] = hostHooks.userdataMetamethodBytecodeType(bcType.a, bcType.b, opcodeToHostMetamethod(op));
|
||||||
}
|
}
|
||||||
|
@ -970,7 +981,8 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks)
|
||||||
|
|
||||||
if (bcType.a == LBC_TYPE_NUMBER && bcType.b == LBC_TYPE_NUMBER)
|
if (bcType.a == LBC_TYPE_NUMBER && bcType.b == LBC_TYPE_NUMBER)
|
||||||
regTags[ra] = LBC_TYPE_NUMBER;
|
regTags[ra] = LBC_TYPE_NUMBER;
|
||||||
else if (hostHooks.userdataMetamethodBytecodeType && (isCustomUserdataBytecodeType(bcType.a) || isCustomUserdataBytecodeType(bcType.b)))
|
else if (hostHooks.userdataMetamethodBytecodeType &&
|
||||||
|
(isCustomUserdataBytecodeType(bcType.a) || isCustomUserdataBytecodeType(bcType.b)))
|
||||||
regTags[ra] = hostHooks.userdataMetamethodBytecodeType(bcType.a, bcType.b, opcodeToHostMetamethod(op));
|
regTags[ra] = hostHooks.userdataMetamethodBytecodeType(bcType.a, bcType.b, opcodeToHostMetamethod(op));
|
||||||
|
|
||||||
bcType.result = regTags[ra];
|
bcType.result = regTags[ra];
|
||||||
|
@ -991,7 +1003,8 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks)
|
||||||
regTags[ra] = LBC_TYPE_NUMBER;
|
regTags[ra] = LBC_TYPE_NUMBER;
|
||||||
else if (bcType.a == LBC_TYPE_VECTOR && bcType.b == LBC_TYPE_VECTOR)
|
else if (bcType.a == LBC_TYPE_VECTOR && bcType.b == LBC_TYPE_VECTOR)
|
||||||
regTags[ra] = LBC_TYPE_VECTOR;
|
regTags[ra] = LBC_TYPE_VECTOR;
|
||||||
else if (hostHooks.userdataMetamethodBytecodeType && (isCustomUserdataBytecodeType(bcType.a) || isCustomUserdataBytecodeType(bcType.b)))
|
else if (hostHooks.userdataMetamethodBytecodeType &&
|
||||||
|
(isCustomUserdataBytecodeType(bcType.a) || isCustomUserdataBytecodeType(bcType.b)))
|
||||||
regTags[ra] = hostHooks.userdataMetamethodBytecodeType(bcType.a, bcType.b, opcodeToHostMetamethod(op));
|
regTags[ra] = hostHooks.userdataMetamethodBytecodeType(bcType.a, bcType.b, opcodeToHostMetamethod(op));
|
||||||
|
|
||||||
bcType.result = regTags[ra];
|
bcType.result = regTags[ra];
|
||||||
|
@ -1020,7 +1033,8 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks)
|
||||||
if (bcType.b == LBC_TYPE_NUMBER || bcType.b == LBC_TYPE_VECTOR)
|
if (bcType.b == LBC_TYPE_NUMBER || bcType.b == LBC_TYPE_VECTOR)
|
||||||
regTags[ra] = LBC_TYPE_VECTOR;
|
regTags[ra] = LBC_TYPE_VECTOR;
|
||||||
}
|
}
|
||||||
else if (hostHooks.userdataMetamethodBytecodeType && (isCustomUserdataBytecodeType(bcType.a) || isCustomUserdataBytecodeType(bcType.b)))
|
else if (hostHooks.userdataMetamethodBytecodeType &&
|
||||||
|
(isCustomUserdataBytecodeType(bcType.a) || isCustomUserdataBytecodeType(bcType.b)))
|
||||||
{
|
{
|
||||||
regTags[ra] = hostHooks.userdataMetamethodBytecodeType(bcType.a, bcType.b, opcodeToHostMetamethod(op));
|
regTags[ra] = hostHooks.userdataMetamethodBytecodeType(bcType.a, bcType.b, opcodeToHostMetamethod(op));
|
||||||
}
|
}
|
||||||
|
@ -1086,7 +1100,7 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks)
|
||||||
CODEGEN_ASSERT(LUAU_INSN_OP(call) == LOP_CALL);
|
CODEGEN_ASSERT(LUAU_INSN_OP(call) == LOP_CALL);
|
||||||
int ra = LUAU_INSN_A(call);
|
int ra = LUAU_INSN_A(call);
|
||||||
|
|
||||||
applyBuiltinCall(bfid, bcType);
|
applyBuiltinCall(LuauBuiltinFunction(bfid), bcType);
|
||||||
regTags[ra + 1] = bcType.a;
|
regTags[ra + 1] = bcType.a;
|
||||||
regTags[ra + 2] = bcType.b;
|
regTags[ra + 2] = bcType.b;
|
||||||
regTags[ra + 3] = bcType.c;
|
regTags[ra + 3] = bcType.c;
|
||||||
|
@ -1105,7 +1119,7 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks)
|
||||||
CODEGEN_ASSERT(LUAU_INSN_OP(call) == LOP_CALL);
|
CODEGEN_ASSERT(LUAU_INSN_OP(call) == LOP_CALL);
|
||||||
int ra = LUAU_INSN_A(call);
|
int ra = LUAU_INSN_A(call);
|
||||||
|
|
||||||
applyBuiltinCall(bfid, bcType);
|
applyBuiltinCall(LuauBuiltinFunction(bfid), bcType);
|
||||||
|
|
||||||
regTags[LUAU_INSN_B(*pc)] = bcType.a;
|
regTags[LUAU_INSN_B(*pc)] = bcType.a;
|
||||||
regTags[ra] = bcType.result;
|
regTags[ra] = bcType.result;
|
||||||
|
@ -1122,7 +1136,7 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks)
|
||||||
CODEGEN_ASSERT(LUAU_INSN_OP(call) == LOP_CALL);
|
CODEGEN_ASSERT(LUAU_INSN_OP(call) == LOP_CALL);
|
||||||
int ra = LUAU_INSN_A(call);
|
int ra = LUAU_INSN_A(call);
|
||||||
|
|
||||||
applyBuiltinCall(bfid, bcType);
|
applyBuiltinCall(LuauBuiltinFunction(bfid), bcType);
|
||||||
|
|
||||||
regTags[LUAU_INSN_B(*pc)] = bcType.a;
|
regTags[LUAU_INSN_B(*pc)] = bcType.a;
|
||||||
regTags[int(pc[1])] = bcType.b;
|
regTags[int(pc[1])] = bcType.b;
|
||||||
|
@ -1141,7 +1155,7 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks)
|
||||||
CODEGEN_ASSERT(LUAU_INSN_OP(call) == LOP_CALL);
|
CODEGEN_ASSERT(LUAU_INSN_OP(call) == LOP_CALL);
|
||||||
int ra = LUAU_INSN_A(call);
|
int ra = LUAU_INSN_A(call);
|
||||||
|
|
||||||
applyBuiltinCall(bfid, bcType);
|
applyBuiltinCall(LuauBuiltinFunction(bfid), bcType);
|
||||||
|
|
||||||
regTags[LUAU_INSN_B(*pc)] = bcType.a;
|
regTags[LUAU_INSN_B(*pc)] = bcType.a;
|
||||||
regTags[aux & 0xff] = bcType.b;
|
regTags[aux & 0xff] = bcType.b;
|
||||||
|
|
|
@ -101,7 +101,7 @@ inline bool lowerImpl(
|
||||||
|
|
||||||
bool outputEnabled = options.includeAssembly || options.includeIr;
|
bool outputEnabled = options.includeAssembly || options.includeIr;
|
||||||
|
|
||||||
IrToStringContext ctx{build.text, function.blocks, function.constants, function.cfg};
|
IrToStringContext ctx{build.text, function.blocks, function.constants, function.cfg, function.proto};
|
||||||
|
|
||||||
// We use this to skip outlined fallback blocks from IR/asm text output
|
// We use this to skip outlined fallback blocks from IR/asm text output
|
||||||
size_t textSize = build.text.length();
|
size_t textSize = build.text.length();
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
LUAU_DYNAMIC_FASTFLAG(LuauPopIncompleteCi)
|
||||||
|
|
||||||
// All external function calls that can cause stack realloc or Lua calls have to be wrapped in VM_PROTECT
|
// All external function calls that can cause stack realloc or Lua calls have to be wrapped in VM_PROTECT
|
||||||
// This makes sure that we save the pc (in case the Lua call needs to generate a backtrace) before the call,
|
// This makes sure that we save the pc (in case the Lua call needs to generate a backtrace) before the call,
|
||||||
// and restores the stack pointer after in case stack gets reallocated
|
// and restores the stack pointer after in case stack gets reallocated
|
||||||
|
@ -191,7 +193,14 @@ Closure* callProlog(lua_State* L, TValue* ra, StkId argtop, int nresults)
|
||||||
// note: this reallocs stack, but we don't need to VM_PROTECT this
|
// note: this reallocs stack, but we don't need to VM_PROTECT this
|
||||||
// this is because we're going to modify base/savedpc manually anyhow
|
// this is because we're going to modify base/savedpc manually anyhow
|
||||||
// crucially, we can't use ra/argtop after this line
|
// crucially, we can't use ra/argtop after this line
|
||||||
|
if (DFFlag::LuauPopIncompleteCi)
|
||||||
|
{
|
||||||
|
luaD_checkstackfornewci(L, ccl->stacksize);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
luaD_checkstack(L, ccl->stacksize);
|
luaD_checkstack(L, ccl->stacksize);
|
||||||
|
}
|
||||||
|
|
||||||
return ccl;
|
return ccl;
|
||||||
}
|
}
|
||||||
|
@ -261,7 +270,14 @@ Closure* callFallback(lua_State* L, StkId ra, StkId argtop, int nresults)
|
||||||
// note: this reallocs stack, but we don't need to VM_PROTECT this
|
// note: this reallocs stack, but we don't need to VM_PROTECT this
|
||||||
// this is because we're going to modify base/savedpc manually anyhow
|
// this is because we're going to modify base/savedpc manually anyhow
|
||||||
// crucially, we can't use ra/argtop after this line
|
// crucially, we can't use ra/argtop after this line
|
||||||
|
if (DFFlag::LuauPopIncompleteCi)
|
||||||
|
{
|
||||||
|
luaD_checkstackfornewci(L, ccl->stacksize);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
luaD_checkstack(L, ccl->stacksize);
|
luaD_checkstack(L, ccl->stacksize);
|
||||||
|
}
|
||||||
|
|
||||||
LUAU_ASSERT(ci->top <= L->stack_last);
|
LUAU_ASSERT(ci->top <= L->stack_last);
|
||||||
|
|
||||||
|
|
|
@ -684,7 +684,7 @@ void computeCfgDominanceTreeChildren(IrFunction& function)
|
||||||
info.domChildrenOffsets[domParent]++;
|
info.domChildrenOffsets[domParent]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert counds to offsets using prefix sum
|
// Convert counts to offsets using prefix sum
|
||||||
uint32_t total = 0;
|
uint32_t total = 0;
|
||||||
|
|
||||||
for (size_t blockIdx = 0; blockIdx < function.blocks.size(); blockIdx++)
|
for (size_t blockIdx = 0; blockIdx < function.blocks.size(); blockIdx++)
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
#include "Luau/IrUtils.h"
|
#include "Luau/IrUtils.h"
|
||||||
|
|
||||||
#include "lua.h"
|
#include "lua.h"
|
||||||
|
#include "lobject.h"
|
||||||
|
#include "lstate.h"
|
||||||
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
@ -19,6 +21,7 @@ static const char* textForCondition[] =
|
||||||
static_assert(sizeof(textForCondition) / sizeof(textForCondition[0]) == size_t(IrCondition::Count), "all conditions have to be covered");
|
static_assert(sizeof(textForCondition) / sizeof(textForCondition[0]) == size_t(IrCondition::Count), "all conditions have to be covered");
|
||||||
|
|
||||||
const int kDetailsAlignColumn = 60;
|
const int kDetailsAlignColumn = 60;
|
||||||
|
const unsigned kMaxStringConstantPrintLength = 16;
|
||||||
|
|
||||||
LUAU_PRINTF_ATTR(2, 3)
|
LUAU_PRINTF_ATTR(2, 3)
|
||||||
static void append(std::string& result, const char* fmt, ...)
|
static void append(std::string& result, const char* fmt, ...)
|
||||||
|
@ -39,6 +42,17 @@ static void padToDetailColumn(std::string& result, size_t lineStart)
|
||||||
result.append(pad, ' ');
|
result.append(pad, ' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool isPrintableStringConstant(const char* str, size_t len)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < len; ++i)
|
||||||
|
{
|
||||||
|
if (unsigned(str[i]) < ' ')
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static const char* getTagName(uint8_t tag)
|
static const char* getTagName(uint8_t tag)
|
||||||
{
|
{
|
||||||
switch (tag)
|
switch (tag)
|
||||||
|
@ -431,6 +445,53 @@ void toString(IrToStringContext& ctx, const IrBlock& block, uint32_t index)
|
||||||
append(ctx.result, "%s_%u", getBlockKindName(block.kind), index);
|
append(ctx.result, "%s_%u", getBlockKindName(block.kind), index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void appendVmConstant(std::string& result, Proto* proto, int index)
|
||||||
|
{
|
||||||
|
TValue constant = proto->k[index];
|
||||||
|
|
||||||
|
if (constant.tt == LUA_TNIL)
|
||||||
|
{
|
||||||
|
append(result, "nil");
|
||||||
|
}
|
||||||
|
else if (constant.tt == LUA_TBOOLEAN)
|
||||||
|
{
|
||||||
|
append(result, constant.value.b != 0 ? "true" : "false");
|
||||||
|
}
|
||||||
|
else if (constant.tt == LUA_TNUMBER)
|
||||||
|
{
|
||||||
|
if (constant.value.n != constant.value.n)
|
||||||
|
append(result, "nan");
|
||||||
|
else
|
||||||
|
append(result, "%.17g", constant.value.n);
|
||||||
|
}
|
||||||
|
else if (constant.tt == LUA_TSTRING)
|
||||||
|
{
|
||||||
|
TString* str = gco2ts(constant.value.gc);
|
||||||
|
const char* data = getstr(str);
|
||||||
|
|
||||||
|
if (isPrintableStringConstant(data, str->len))
|
||||||
|
{
|
||||||
|
if (str->len < kMaxStringConstantPrintLength)
|
||||||
|
append(result, "'%.*s'", int(str->len), data);
|
||||||
|
else
|
||||||
|
append(result, "'%.*s'...", int(kMaxStringConstantPrintLength), data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (constant.tt == LUA_TVECTOR)
|
||||||
|
{
|
||||||
|
const float* v = constant.value.v;
|
||||||
|
|
||||||
|
#if LUA_VECTOR_SIZE == 4
|
||||||
|
if (v[3] != 0)
|
||||||
|
append(result, "%.9g, %.9g, %.9g, %.9g", v[0], v[1], v[2], v[3]);
|
||||||
|
else
|
||||||
|
append(result, "%.9g, %.9g, %.9g", v[0], v[1], v[2]);
|
||||||
|
#else
|
||||||
|
append(result, "%.9g, %.9g, %.9g", v[0], v[1], v[2]);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void toString(IrToStringContext& ctx, IrOp op)
|
void toString(IrToStringContext& ctx, IrOp op)
|
||||||
{
|
{
|
||||||
switch (op.kind)
|
switch (op.kind)
|
||||||
|
@ -458,6 +519,14 @@ void toString(IrToStringContext& ctx, IrOp op)
|
||||||
break;
|
break;
|
||||||
case IrOpKind::VmConst:
|
case IrOpKind::VmConst:
|
||||||
append(ctx.result, "K%d", vmConstOp(op));
|
append(ctx.result, "K%d", vmConstOp(op));
|
||||||
|
|
||||||
|
if (ctx.proto)
|
||||||
|
{
|
||||||
|
append(ctx.result, " (");
|
||||||
|
appendVmConstant(ctx.result, ctx.proto, vmConstOp(op));
|
||||||
|
append(ctx.result, ")");
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case IrOpKind::VmUpvalue:
|
case IrOpKind::VmUpvalue:
|
||||||
append(ctx.result, "U%d", vmUpvalueOp(op));
|
append(ctx.result, "U%d", vmUpvalueOp(op));
|
||||||
|
@ -770,7 +839,7 @@ void toStringDetailed(
|
||||||
std::string toString(const IrFunction& function, IncludeUseInfo includeUseInfo)
|
std::string toString(const IrFunction& function, IncludeUseInfo includeUseInfo)
|
||||||
{
|
{
|
||||||
std::string result;
|
std::string result;
|
||||||
IrToStringContext ctx{result, function.blocks, function.constants, function.cfg};
|
IrToStringContext ctx{result, function.blocks, function.constants, function.cfg, function.proto};
|
||||||
|
|
||||||
for (size_t i = 0; i < function.blocks.size(); i++)
|
for (size_t i = 0; i < function.blocks.size(); i++)
|
||||||
{
|
{
|
||||||
|
@ -877,7 +946,7 @@ static void appendBlocks(IrToStringContext& ctx, const IrFunction& function, boo
|
||||||
std::string toDot(const IrFunction& function, bool includeInst)
|
std::string toDot(const IrFunction& function, bool includeInst)
|
||||||
{
|
{
|
||||||
std::string result;
|
std::string result;
|
||||||
IrToStringContext ctx{result, function.blocks, function.constants, function.cfg};
|
IrToStringContext ctx{result, function.blocks, function.constants, function.cfg, function.proto};
|
||||||
|
|
||||||
append(ctx.result, "digraph CFG {\n");
|
append(ctx.result, "digraph CFG {\n");
|
||||||
append(ctx.result, "node[shape=record]\n");
|
append(ctx.result, "node[shape=record]\n");
|
||||||
|
@ -924,7 +993,7 @@ std::string toDot(const IrFunction& function, bool includeInst)
|
||||||
std::string toDotCfg(const IrFunction& function)
|
std::string toDotCfg(const IrFunction& function)
|
||||||
{
|
{
|
||||||
std::string result;
|
std::string result;
|
||||||
IrToStringContext ctx{result, function.blocks, function.constants, function.cfg};
|
IrToStringContext ctx{result, function.blocks, function.constants, function.cfg, function.proto};
|
||||||
|
|
||||||
append(ctx.result, "digraph CFG {\n");
|
append(ctx.result, "digraph CFG {\n");
|
||||||
append(ctx.result, "node[shape=record]\n");
|
append(ctx.result, "node[shape=record]\n");
|
||||||
|
@ -947,7 +1016,7 @@ std::string toDotCfg(const IrFunction& function)
|
||||||
std::string toDotDjGraph(const IrFunction& function)
|
std::string toDotDjGraph(const IrFunction& function)
|
||||||
{
|
{
|
||||||
std::string result;
|
std::string result;
|
||||||
IrToStringContext ctx{result, function.blocks, function.constants, function.cfg};
|
IrToStringContext ctx{result, function.blocks, function.constants, function.cfg, function.proto};
|
||||||
|
|
||||||
append(ctx.result, "digraph CFG {\n");
|
append(ctx.result, "digraph CFG {\n");
|
||||||
|
|
||||||
|
|
|
@ -18,9 +18,11 @@
|
||||||
LUAU_FASTINTVARIABLE(LuauCodeGenMinLinearBlockPath, 3)
|
LUAU_FASTINTVARIABLE(LuauCodeGenMinLinearBlockPath, 3)
|
||||||
LUAU_FASTINTVARIABLE(LuauCodeGenReuseSlotLimit, 64)
|
LUAU_FASTINTVARIABLE(LuauCodeGenReuseSlotLimit, 64)
|
||||||
LUAU_FASTINTVARIABLE(LuauCodeGenReuseUdataTagLimit, 64)
|
LUAU_FASTINTVARIABLE(LuauCodeGenReuseUdataTagLimit, 64)
|
||||||
|
LUAU_FASTINTVARIABLE(LuauCodeGenLiveSlotReuseLimit, 8)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauAbortingChecks)
|
LUAU_FASTFLAGVARIABLE(DebugLuauAbortingChecks)
|
||||||
LUAU_FASTFLAG(LuauVectorLibNativeDot);
|
LUAU_FASTFLAG(LuauVectorLibNativeDot)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCodeGenArithOpt);
|
LUAU_FASTFLAGVARIABLE(LuauCodeGenArithOpt)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauCodeGenLimitLiveSlotReuse)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -50,6 +52,14 @@ struct RegisterLink
|
||||||
uint32_t version = 0;
|
uint32_t version = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Reference to an instruction together with the position of that instruction in the current block chain and the last position of reuse
|
||||||
|
struct NumberedInstruction
|
||||||
|
{
|
||||||
|
uint32_t instIdx = 0;
|
||||||
|
uint32_t startPos = 0;
|
||||||
|
uint32_t finishPos = 0;
|
||||||
|
};
|
||||||
|
|
||||||
// Data we know about the current VM state
|
// Data we know about the current VM state
|
||||||
struct ConstPropState
|
struct ConstPropState
|
||||||
{
|
{
|
||||||
|
@ -190,7 +200,11 @@ struct ConstPropState
|
||||||
// Same goes for table array elements as well
|
// Same goes for table array elements as well
|
||||||
void invalidateHeapTableData()
|
void invalidateHeapTableData()
|
||||||
{
|
{
|
||||||
|
if (FFlag::LuauCodeGenLimitLiveSlotReuse)
|
||||||
getSlotNodeCache.clear();
|
getSlotNodeCache.clear();
|
||||||
|
else
|
||||||
|
getSlotNodeCache_DEPRECATED.clear();
|
||||||
|
|
||||||
checkSlotMatchCache.clear();
|
checkSlotMatchCache.clear();
|
||||||
|
|
||||||
getArrAddrCache.clear();
|
getArrAddrCache.clear();
|
||||||
|
@ -409,6 +423,64 @@ struct ConstPropState
|
||||||
valueMap[versionedVmRegLoad(loadCmd, storeInst.a)] = storeInst.b.index;
|
valueMap[versionedVmRegLoad(loadCmd, storeInst.a)] = storeInst.b.index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used to compute the pressure of the cached value 'set' on the spill registers
|
||||||
|
// We want to find out the maximum live range intersection count between the cached value at 'slot' and current instruction
|
||||||
|
// Note that this pressure is approximate, as some values that might have been live at one point could have been marked dead later
|
||||||
|
int getMaxInternalOverlap(std::vector<NumberedInstruction>& set, size_t slot)
|
||||||
|
{
|
||||||
|
CODEGEN_ASSERT(FFlag::LuauCodeGenLimitLiveSlotReuse);
|
||||||
|
|
||||||
|
// Start with one live range for the slot we want to reuse
|
||||||
|
int curr = 1;
|
||||||
|
|
||||||
|
// For any slots where lifetime began before the slot of interest, mark as live if lifetime end is still active
|
||||||
|
// This saves us from processing slots [0; slot] in the range sweep later, which requires sorting the lifetime end points
|
||||||
|
for (size_t i = 0; i < slot; i++)
|
||||||
|
{
|
||||||
|
if (set[i].finishPos >= set[slot].startPos)
|
||||||
|
curr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int max = curr;
|
||||||
|
|
||||||
|
// Collect lifetime end points and sort them
|
||||||
|
rangeEndTemp.clear();
|
||||||
|
|
||||||
|
for (size_t i = slot + 1; i < set.size(); i++)
|
||||||
|
rangeEndTemp.push_back(set[i].finishPos);
|
||||||
|
|
||||||
|
std::sort(rangeEndTemp.begin(), rangeEndTemp.end());
|
||||||
|
|
||||||
|
// Go over the lifetime begin/end ranges that we store as separate array and walk based on the smallest of values
|
||||||
|
for (size_t i1 = slot + 1, i2 = 0; i1 < set.size() && i2 < rangeEndTemp.size();)
|
||||||
|
{
|
||||||
|
if (rangeEndTemp[i2] == set[i1].startPos)
|
||||||
|
{
|
||||||
|
i1++;
|
||||||
|
i2++;
|
||||||
|
}
|
||||||
|
else if (rangeEndTemp[i2] < set[i1].startPos)
|
||||||
|
{
|
||||||
|
CODEGEN_ASSERT(curr > 0);
|
||||||
|
|
||||||
|
curr--;
|
||||||
|
i2++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
curr++;
|
||||||
|
i1++;
|
||||||
|
|
||||||
|
if (curr > max)
|
||||||
|
max = curr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We might have unprocessed lifetime end entries, but we will never have unprocessed lifetime start entries
|
||||||
|
// Not that lifetime end entries can only decrease the current value and do not affect the end result (maximum)
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
void clear()
|
void clear()
|
||||||
{
|
{
|
||||||
for (int i = 0; i <= maxReg; ++i)
|
for (int i = 0; i <= maxReg; ++i)
|
||||||
|
@ -416,6 +488,9 @@ struct ConstPropState
|
||||||
|
|
||||||
maxReg = 0;
|
maxReg = 0;
|
||||||
|
|
||||||
|
if (FFlag::LuauCodeGenLimitLiveSlotReuse)
|
||||||
|
instPos = 0u;
|
||||||
|
|
||||||
inSafeEnv = false;
|
inSafeEnv = false;
|
||||||
checkedGc = false;
|
checkedGc = false;
|
||||||
|
|
||||||
|
@ -436,6 +511,9 @@ struct ConstPropState
|
||||||
// For range/full invalidations, we only want to visit a limited number of data that we have recorded
|
// For range/full invalidations, we only want to visit a limited number of data that we have recorded
|
||||||
int maxReg = 0;
|
int maxReg = 0;
|
||||||
|
|
||||||
|
// Number of the instruction being processed
|
||||||
|
uint32_t instPos = 0;
|
||||||
|
|
||||||
bool inSafeEnv = false;
|
bool inSafeEnv = false;
|
||||||
bool checkedGc = false;
|
bool checkedGc = false;
|
||||||
|
|
||||||
|
@ -447,7 +525,8 @@ struct ConstPropState
|
||||||
std::vector<uint32_t> tryNumToIndexCache; // Fallback block argument might be different
|
std::vector<uint32_t> tryNumToIndexCache; // Fallback block argument might be different
|
||||||
|
|
||||||
// Heap changes might affect table state
|
// Heap changes might affect table state
|
||||||
std::vector<uint32_t> getSlotNodeCache; // Additionally, pcpos argument might be different
|
std::vector<NumberedInstruction> getSlotNodeCache; // Additionally, pcpos argument might be different
|
||||||
|
std::vector<uint32_t> getSlotNodeCache_DEPRECATED; // Additionally, pcpos argument might be different
|
||||||
std::vector<uint32_t> checkSlotMatchCache; // Additionally, fallback block argument might be different
|
std::vector<uint32_t> checkSlotMatchCache; // Additionally, fallback block argument might be different
|
||||||
|
|
||||||
std::vector<uint32_t> getArrAddrCache;
|
std::vector<uint32_t> getArrAddrCache;
|
||||||
|
@ -457,6 +536,8 @@ struct ConstPropState
|
||||||
|
|
||||||
// Userdata tag cache can point to both NEW_USERDATA and CHECK_USERDATA_TAG instructions
|
// Userdata tag cache can point to both NEW_USERDATA and CHECK_USERDATA_TAG instructions
|
||||||
std::vector<uint32_t> useradataTagCache; // Additionally, fallback block argument might be different
|
std::vector<uint32_t> useradataTagCache; // Additionally, fallback block argument might be different
|
||||||
|
|
||||||
|
std::vector<uint32_t> rangeEndTemp;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void handleBuiltinEffects(ConstPropState& state, LuauBuiltinFunction bfid, uint32_t firstReturnReg, int nresults)
|
static void handleBuiltinEffects(ConstPropState& state, LuauBuiltinFunction bfid, uint32_t firstReturnReg, int nresults)
|
||||||
|
@ -550,6 +631,7 @@ static void handleBuiltinEffects(ConstPropState& state, LuauBuiltinFunction bfid
|
||||||
case LBF_VECTOR_CLAMP:
|
case LBF_VECTOR_CLAMP:
|
||||||
case LBF_VECTOR_MIN:
|
case LBF_VECTOR_MIN:
|
||||||
case LBF_VECTOR_MAX:
|
case LBF_VECTOR_MAX:
|
||||||
|
case LBF_MATH_LERP:
|
||||||
break;
|
break;
|
||||||
case LBF_TABLE_INSERT:
|
case LBF_TABLE_INSERT:
|
||||||
state.invalidateHeap();
|
state.invalidateHeap();
|
||||||
|
@ -569,6 +651,9 @@ static void handleBuiltinEffects(ConstPropState& state, LuauBuiltinFunction bfid
|
||||||
|
|
||||||
static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction& function, IrBlock& block, IrInst& inst, uint32_t index)
|
static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction& function, IrBlock& block, IrInst& inst, uint32_t index)
|
||||||
{
|
{
|
||||||
|
if (FFlag::LuauCodeGenLimitLiveSlotReuse)
|
||||||
|
state.instPos++;
|
||||||
|
|
||||||
switch (inst.cmd)
|
switch (inst.cmd)
|
||||||
{
|
{
|
||||||
case IrCmd::LOAD_TAG:
|
case IrCmd::LOAD_TAG:
|
||||||
|
@ -771,7 +856,8 @@ static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction&
|
||||||
if (tag == LUA_TBOOLEAN &&
|
if (tag == LUA_TBOOLEAN &&
|
||||||
(value.kind == IrOpKind::Inst || (value.kind == IrOpKind::Constant && function.constOp(value).kind == IrConstKind::Int)))
|
(value.kind == IrOpKind::Inst || (value.kind == IrOpKind::Constant && function.constOp(value).kind == IrConstKind::Int)))
|
||||||
canSplitTvalueStore = true;
|
canSplitTvalueStore = true;
|
||||||
else if (tag == LUA_TNUMBER && (value.kind == IrOpKind::Inst || (value.kind == IrOpKind::Constant && function.constOp(value).kind == IrConstKind::Double)))
|
else if (tag == LUA_TNUMBER &&
|
||||||
|
(value.kind == IrOpKind::Inst || (value.kind == IrOpKind::Constant && function.constOp(value).kind == IrConstKind::Double)))
|
||||||
canSplitTvalueStore = true;
|
canSplitTvalueStore = true;
|
||||||
else if (tag != 0xff && isGCO(tag) && value.kind == IrOpKind::Inst)
|
else if (tag != 0xff && isGCO(tag) && value.kind == IrOpKind::Inst)
|
||||||
canSplitTvalueStore = true;
|
canSplitTvalueStore = true;
|
||||||
|
@ -1174,7 +1260,36 @@ static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction&
|
||||||
state.getArrAddrCache.push_back(index);
|
state.getArrAddrCache.push_back(index);
|
||||||
break;
|
break;
|
||||||
case IrCmd::GET_SLOT_NODE_ADDR:
|
case IrCmd::GET_SLOT_NODE_ADDR:
|
||||||
for (uint32_t prevIdx : state.getSlotNodeCache)
|
if (FFlag::LuauCodeGenLimitLiveSlotReuse)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < state.getSlotNodeCache.size(); i++)
|
||||||
|
{
|
||||||
|
auto&& [prevIdx, num, lastNum] = state.getSlotNodeCache[i];
|
||||||
|
|
||||||
|
const IrInst& prev = function.instructions[prevIdx];
|
||||||
|
|
||||||
|
if (prev.a == inst.a && prev.c == inst.c)
|
||||||
|
{
|
||||||
|
// Check if this reuse will increase the overall register pressure over the limit
|
||||||
|
int limit = FInt::LuauCodeGenLiveSlotReuseLimit;
|
||||||
|
|
||||||
|
if (int(state.getSlotNodeCache.size()) > limit && state.getMaxInternalOverlap(state.getSlotNodeCache, i) > limit)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Update live range of the value from the optimization standpoint
|
||||||
|
lastNum = state.instPos;
|
||||||
|
|
||||||
|
substitute(function, inst, IrOp{IrOpKind::Inst, prevIdx});
|
||||||
|
return; // Break out from both the loop and the switch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (int(state.getSlotNodeCache.size()) < FInt::LuauCodeGenReuseSlotLimit)
|
||||||
|
state.getSlotNodeCache.push_back({index, state.instPos, state.instPos});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (uint32_t prevIdx : state.getSlotNodeCache_DEPRECATED)
|
||||||
{
|
{
|
||||||
const IrInst& prev = function.instructions[prevIdx];
|
const IrInst& prev = function.instructions[prevIdx];
|
||||||
|
|
||||||
|
@ -1185,8 +1300,9 @@ static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction&
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (int(state.getSlotNodeCache.size()) < FInt::LuauCodeGenReuseSlotLimit)
|
if (int(state.getSlotNodeCache_DEPRECATED.size()) < FInt::LuauCodeGenReuseSlotLimit)
|
||||||
state.getSlotNodeCache.push_back(index);
|
state.getSlotNodeCache_DEPRECATED.push_back(index);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case IrCmd::GET_HASH_NODE_ADDR:
|
case IrCmd::GET_HASH_NODE_ADDR:
|
||||||
case IrCmd::GET_CLOSURE_UPVAL_ADDR:
|
case IrCmd::GET_CLOSURE_UPVAL_ADDR:
|
||||||
|
|
|
@ -613,6 +613,9 @@ enum LuauBuiltinFunction
|
||||||
LBF_VECTOR_CLAMP,
|
LBF_VECTOR_CLAMP,
|
||||||
LBF_VECTOR_MIN,
|
LBF_VECTOR_MIN,
|
||||||
LBF_VECTOR_MAX,
|
LBF_VECTOR_MAX,
|
||||||
|
|
||||||
|
// math.lerp
|
||||||
|
LBF_MATH_LERP,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Capture type, used in LOP_CAPTURE
|
// Capture type, used in LOP_CAPTURE
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauCompileMathLerp)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
namespace Compile
|
namespace Compile
|
||||||
|
@ -479,6 +481,19 @@ Constant foldBuiltin(int bfid, const Constant* args, size_t count)
|
||||||
return cvector(args[0].valueNumber, args[1].valueNumber, args[2].valueNumber, args[3].valueNumber);
|
return cvector(args[0].valueNumber, args[1].valueNumber, args[2].valueNumber, args[3].valueNumber);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case LBF_MATH_LERP:
|
||||||
|
if (FFlag::LuauCompileMathLerp && count == 3 && args[0].type == Constant::Type_Number && args[1].type == Constant::Type_Number &&
|
||||||
|
args[2].type == Constant::Type_Number)
|
||||||
|
{
|
||||||
|
double a = args[0].valueNumber;
|
||||||
|
double b = args[1].valueNumber;
|
||||||
|
double t = args[2].valueNumber;
|
||||||
|
|
||||||
|
double v = (t == 1.0) ? b : a + (b - a) * t;
|
||||||
|
return cnum(v);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return cvar();
|
return cvar();
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauVectorBuiltins)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCompileDisabledBuiltins)
|
LUAU_FASTFLAGVARIABLE(LuauCompileDisabledBuiltins)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauCompileMathLerp)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -140,6 +140,8 @@ static int getBuiltinFunctionId(const Builtin& builtin, const CompileOptions& op
|
||||||
return LBF_MATH_SIGN;
|
return LBF_MATH_SIGN;
|
||||||
if (builtin.method == "round")
|
if (builtin.method == "round")
|
||||||
return LBF_MATH_ROUND;
|
return LBF_MATH_ROUND;
|
||||||
|
if (FFlag::LuauCompileMathLerp && builtin.method == "lerp")
|
||||||
|
return LBF_MATH_LERP;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (builtin.object == "bit32")
|
if (builtin.object == "bit32")
|
||||||
|
@ -226,7 +228,7 @@ static int getBuiltinFunctionId(const Builtin& builtin, const CompileOptions& op
|
||||||
return LBF_BUFFER_WRITEF64;
|
return LBF_BUFFER_WRITEF64;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauVectorBuiltins && builtin.object == "vector")
|
if (builtin.object == "vector")
|
||||||
{
|
{
|
||||||
if (builtin.method == "create")
|
if (builtin.method == "create")
|
||||||
return LBF_VECTOR;
|
return LBF_VECTOR;
|
||||||
|
@ -556,6 +558,10 @@ BuiltinInfo getBuiltinInfo(int bfid)
|
||||||
case LBF_VECTOR_MIN:
|
case LBF_VECTOR_MIN:
|
||||||
case LBF_VECTOR_MAX:
|
case LBF_VECTOR_MAX:
|
||||||
return {-1, 1}; // variadic
|
return {-1, 1}; // variadic
|
||||||
|
|
||||||
|
case LBF_MATH_LERP:
|
||||||
|
LUAU_ASSERT(FFlag::LuauCompileMathLerp);
|
||||||
|
return {3, 1, BuiltinInfo::Flag_NoneSafe};
|
||||||
}
|
}
|
||||||
|
|
||||||
LUAU_UNREACHABLE();
|
LUAU_UNREACHABLE();
|
||||||
|
|
|
@ -1751,7 +1751,8 @@ void BytecodeBuilder::validateVariadic() const
|
||||||
// variadic sequence since they are never executed if FASTCALL does anything, so it's okay to skip their validation until CALL
|
// variadic sequence since they are never executed if FASTCALL does anything, so it's okay to skip their validation until CALL
|
||||||
// (we can't simply start a variadic sequence here because that would trigger assertions during linked CALL validation)
|
// (we can't simply start a variadic sequence here because that would trigger assertions during linked CALL validation)
|
||||||
}
|
}
|
||||||
else if (op == LOP_CLOSEUPVALS || op == LOP_NAMECALL || op == LOP_GETIMPORT || op == LOP_MOVE || op == LOP_GETUPVAL || op == LOP_GETGLOBAL || op == LOP_GETTABLEKS || op == LOP_COVERAGE)
|
else if (op == LOP_CLOSEUPVALS || op == LOP_NAMECALL || op == LOP_GETIMPORT || op == LOP_MOVE || op == LOP_GETUPVAL || op == LOP_GETGLOBAL ||
|
||||||
|
op == LOP_GETTABLEKS || op == LOP_COVERAGE)
|
||||||
{
|
{
|
||||||
// instructions inside a variadic sequence must be neutral (can't change L->top)
|
// instructions inside a variadic sequence must be neutral (can't change L->top)
|
||||||
// while there are many neutral instructions like this, here we check that the instruction is one of the few
|
// while there are many neutral instructions like this, here we check that the instruction is one of the few
|
||||||
|
|
|
@ -1624,7 +1624,8 @@ struct Compiler
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (FFlag::LuauCompileOptimizeRevArith && options.optimizationLevel >= 2 && (expr->op == AstExprBinary::Add || expr->op == AstExprBinary::Mul))
|
else if (FFlag::LuauCompileOptimizeRevArith && options.optimizationLevel >= 2 &&
|
||||||
|
(expr->op == AstExprBinary::Add || expr->op == AstExprBinary::Mul))
|
||||||
{
|
{
|
||||||
// Optimization: replace k*r with r*k when r is known to be a number (otherwise metamethods may be called)
|
// Optimization: replace k*r with r*k when r is known to be a number (otherwise metamethods may be called)
|
||||||
if (LuauBytecodeType* ty = exprTypes.find(expr); ty && *ty == LBC_TYPE_NUMBER)
|
if (LuauBytecodeType* ty = exprTypes.find(expr); ty && *ty == LBC_TYPE_NUMBER)
|
||||||
|
@ -4408,7 +4409,7 @@ void setCompileConstantString(CompileConstant* constant, const char* s, size_t l
|
||||||
CompileError::raise({}, "Exceeded custom string constant length limit");
|
CompileError::raise({}, "Exceeded custom string constant length limit");
|
||||||
|
|
||||||
target->type = Compile::Constant::Type_String;
|
target->type = Compile::Constant::Type_String;
|
||||||
target->stringLength = l;
|
target->stringLength = unsigned(l);
|
||||||
target->valueString = s;
|
target->valueString = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -130,7 +130,8 @@ struct CostVisitor : AstVisitor
|
||||||
{
|
{
|
||||||
return model(expr->expr);
|
return model(expr->expr);
|
||||||
}
|
}
|
||||||
else if (node->is<AstExprConstantNil>() || node->is<AstExprConstantBool>() || node->is<AstExprConstantNumber>() || node->is<AstExprConstantString>())
|
else if (node->is<AstExprConstantNil>() || node->is<AstExprConstantBool>() || node->is<AstExprConstantNumber>() ||
|
||||||
|
node->is<AstExprConstantString>())
|
||||||
{
|
{
|
||||||
return Cost(0, Cost::kLiteral);
|
return Cost(0, Cost::kLiteral);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
#include "Luau/BytecodeBuilder.h"
|
#include "Luau/BytecodeBuilder.h"
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCompileVectorTypeInfo)
|
|
||||||
LUAU_FASTFLAG(LuauCompileLibraryConstants)
|
LUAU_FASTFLAG(LuauCompileLibraryConstants)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
|
@ -32,7 +31,7 @@ static LuauBytecodeType getPrimitiveType(AstName name)
|
||||||
return LBC_TYPE_THREAD;
|
return LBC_TYPE_THREAD;
|
||||||
else if (name == "buffer")
|
else if (name == "buffer")
|
||||||
return LBC_TYPE_BUFFER;
|
return LBC_TYPE_BUFFER;
|
||||||
else if (FFlag::LuauCompileVectorTypeInfo && name == "vector")
|
else if (name == "vector")
|
||||||
return LBC_TYPE_VECTOR;
|
return LBC_TYPE_VECTOR;
|
||||||
else if (name == "any" || name == "unknown")
|
else if (name == "any" || name == "unknown")
|
||||||
return LBC_TYPE_ANY;
|
return LBC_TYPE_ANY;
|
||||||
|
@ -747,6 +746,7 @@ struct TypeMapVisitor : AstVisitor
|
||||||
case LBF_BUFFER_READF64:
|
case LBF_BUFFER_READF64:
|
||||||
case LBF_VECTOR_MAGNITUDE:
|
case LBF_VECTOR_MAGNITUDE:
|
||||||
case LBF_VECTOR_DOT:
|
case LBF_VECTOR_DOT:
|
||||||
|
case LBF_MATH_LERP:
|
||||||
recordResolvedType(node, &builtinTypes.numberType);
|
recordResolvedType(node, &builtinTypes.numberType);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -305,7 +305,8 @@ static Error parseJson(const std::string& contents, Action action)
|
||||||
arrayTop = (lexer.current().type == '[');
|
arrayTop = (lexer.current().type == '[');
|
||||||
next(lexer);
|
next(lexer);
|
||||||
}
|
}
|
||||||
else if (lexer.current().type == Lexeme::QuotedString || lexer.current().type == Lexeme::ReservedTrue || lexer.current().type == Lexeme::ReservedFalse)
|
else if (lexer.current().type == Lexeme::QuotedString || lexer.current().type == Lexeme::ReservedTrue ||
|
||||||
|
lexer.current().type == Lexeme::ReservedFalse)
|
||||||
{
|
{
|
||||||
std::string value = lexer.current().type == Lexeme::QuotedString
|
std::string value = lexer.current().type == Lexeme::QuotedString
|
||||||
? std::string(lexer.current().data, lexer.current().getLength())
|
? std::string(lexer.current().data, lexer.current().getLength())
|
||||||
|
|
|
@ -198,7 +198,13 @@ private:
|
||||||
{
|
{
|
||||||
// An e-node 𝑛 is canonical iff 𝑛 = canonicalize(𝑛), where
|
// An e-node 𝑛 is canonical iff 𝑛 = canonicalize(𝑛), where
|
||||||
// canonicalize(𝑓(𝑎1, 𝑎2, ...)) = 𝑓(find(𝑎1), find(𝑎2), ...).
|
// canonicalize(𝑓(𝑎1, 𝑎2, ...)) = 𝑓(find(𝑎1), find(𝑎2), ...).
|
||||||
Luau::EqSat::canonicalize(enode, [&](Id id) { return find(id); });
|
Luau::EqSat::canonicalize(
|
||||||
|
enode,
|
||||||
|
[&](Id id)
|
||||||
|
{
|
||||||
|
return find(id);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isCanonical(const L& enode) const
|
bool isCanonical(const L& enode) const
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauBufferBitMethods)
|
||||||
|
|
||||||
// while C API returns 'size_t' for binary compatibility in case of future extensions,
|
// while C API returns 'size_t' for binary compatibility in case of future extensions,
|
||||||
// in the current implementation, length and offset are limited to 31 bits
|
// in the current implementation, length and offset are limited to 31 bits
|
||||||
// because offset is limited to an integer, a single 64bit comparison can be used and will not overflow
|
// because offset is limited to an integer, a single 64bit comparison can be used and will not overflow
|
||||||
|
@ -247,7 +249,68 @@ static int buffer_fill(lua_State* L)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const luaL_Reg bufferlib[] = {
|
static int buffer_readbits(lua_State* L)
|
||||||
|
{
|
||||||
|
size_t len = 0;
|
||||||
|
void* buf = luaL_checkbuffer(L, 1, &len);
|
||||||
|
int64_t bitoffset = (int64_t)luaL_checknumber(L, 2);
|
||||||
|
int bitcount = luaL_checkinteger(L, 3);
|
||||||
|
|
||||||
|
if (bitoffset < 0)
|
||||||
|
luaL_error(L, "buffer access out of bounds");
|
||||||
|
|
||||||
|
if (unsigned(bitcount) > 32)
|
||||||
|
luaL_error(L, "bit count is out of range of [0; 32]");
|
||||||
|
|
||||||
|
if (uint64_t(bitoffset + bitcount) > len * 8)
|
||||||
|
luaL_error(L, "buffer access out of bounds");
|
||||||
|
|
||||||
|
unsigned startbyte = unsigned(bitoffset / 8);
|
||||||
|
unsigned endbyte = unsigned((bitoffset + bitcount + 7) / 8);
|
||||||
|
|
||||||
|
uint64_t data = 0;
|
||||||
|
memcpy(&data, (char*)buf + startbyte, endbyte - startbyte);
|
||||||
|
|
||||||
|
uint64_t subbyteoffset = bitoffset & 0x7;
|
||||||
|
uint64_t mask = (1ull << bitcount) - 1;
|
||||||
|
|
||||||
|
lua_pushunsigned(L, unsigned((data >> subbyteoffset) & mask));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int buffer_writebits(lua_State* L)
|
||||||
|
{
|
||||||
|
size_t len = 0;
|
||||||
|
void* buf = luaL_checkbuffer(L, 1, &len);
|
||||||
|
int64_t bitoffset = (int64_t)luaL_checknumber(L, 2);
|
||||||
|
int bitcount = luaL_checkinteger(L, 3);
|
||||||
|
unsigned value = luaL_checkunsigned(L, 4);
|
||||||
|
|
||||||
|
if (bitoffset < 0)
|
||||||
|
luaL_error(L, "buffer access out of bounds");
|
||||||
|
|
||||||
|
if (unsigned(bitcount) > 32)
|
||||||
|
luaL_error(L, "bit count is out of range of [0; 32]");
|
||||||
|
|
||||||
|
if (uint64_t(bitoffset + bitcount) > len * 8)
|
||||||
|
luaL_error(L, "buffer access out of bounds");
|
||||||
|
|
||||||
|
unsigned startbyte = unsigned(bitoffset / 8);
|
||||||
|
unsigned endbyte = unsigned((bitoffset + bitcount + 7) / 8);
|
||||||
|
|
||||||
|
uint64_t data = 0;
|
||||||
|
memcpy(&data, (char*)buf + startbyte, endbyte - startbyte);
|
||||||
|
|
||||||
|
uint64_t subbyteoffset = bitoffset & 0x7;
|
||||||
|
uint64_t mask = ((1ull << bitcount) - 1) << subbyteoffset;
|
||||||
|
|
||||||
|
data = (data & ~mask) | ((uint64_t(value) << subbyteoffset) & mask);
|
||||||
|
|
||||||
|
memcpy((char*)buf + startbyte, &data, endbyte - startbyte);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const luaL_Reg bufferlib_DEPRECATED[] = {
|
||||||
{"create", buffer_create},
|
{"create", buffer_create},
|
||||||
{"fromstring", buffer_fromstring},
|
{"fromstring", buffer_fromstring},
|
||||||
{"tostring", buffer_tostring},
|
{"tostring", buffer_tostring},
|
||||||
|
@ -275,9 +338,39 @@ static const luaL_Reg bufferlib[] = {
|
||||||
{NULL, NULL},
|
{NULL, NULL},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const luaL_Reg bufferlib[] = {
|
||||||
|
{"create", buffer_create},
|
||||||
|
{"fromstring", buffer_fromstring},
|
||||||
|
{"tostring", buffer_tostring},
|
||||||
|
{"readi8", buffer_readinteger<int8_t>},
|
||||||
|
{"readu8", buffer_readinteger<uint8_t>},
|
||||||
|
{"readi16", buffer_readinteger<int16_t>},
|
||||||
|
{"readu16", buffer_readinteger<uint16_t>},
|
||||||
|
{"readi32", buffer_readinteger<int32_t>},
|
||||||
|
{"readu32", buffer_readinteger<uint32_t>},
|
||||||
|
{"readf32", buffer_readfp<float, uint32_t>},
|
||||||
|
{"readf64", buffer_readfp<double, uint64_t>},
|
||||||
|
{"writei8", buffer_writeinteger<int8_t>},
|
||||||
|
{"writeu8", buffer_writeinteger<uint8_t>},
|
||||||
|
{"writei16", buffer_writeinteger<int16_t>},
|
||||||
|
{"writeu16", buffer_writeinteger<uint16_t>},
|
||||||
|
{"writei32", buffer_writeinteger<int32_t>},
|
||||||
|
{"writeu32", buffer_writeinteger<uint32_t>},
|
||||||
|
{"writef32", buffer_writefp<float, uint32_t>},
|
||||||
|
{"writef64", buffer_writefp<double, uint64_t>},
|
||||||
|
{"readstring", buffer_readstring},
|
||||||
|
{"writestring", buffer_writestring},
|
||||||
|
{"len", buffer_len},
|
||||||
|
{"copy", buffer_copy},
|
||||||
|
{"fill", buffer_fill},
|
||||||
|
{"readbits", buffer_readbits},
|
||||||
|
{"writebits", buffer_writebits},
|
||||||
|
{NULL, NULL},
|
||||||
|
};
|
||||||
|
|
||||||
int luaopen_buffer(lua_State* L)
|
int luaopen_buffer(lua_State* L)
|
||||||
{
|
{
|
||||||
luaL_register(L, LUA_BUFFERLIBNAME, bufferlib);
|
luaL_register(L, LUA_BUFFERLIBNAME, FFlag::LuauBufferBitMethods ? bufferlib : bufferlib_DEPRECATED);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1694,6 +1694,23 @@ static int luauF_vectormax(lua_State* L, StkId res, TValue* arg0, int nresults,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int luauF_lerp(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
|
||||||
|
{
|
||||||
|
if (nparams >= 3 && nresults <= 1 && ttisnumber(arg0) && ttisnumber(args) && ttisnumber(args + 1))
|
||||||
|
{
|
||||||
|
double a = nvalue(arg0);
|
||||||
|
double b = nvalue(args);
|
||||||
|
double t = nvalue(args + 1);
|
||||||
|
|
||||||
|
double r = (t == 1.0) ? b : a + (b - a) * t;
|
||||||
|
|
||||||
|
setnvalue(res, r);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
static int luauF_missing(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
|
static int luauF_missing(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -1889,6 +1906,8 @@ const luau_FastFunction luauF_table[256] = {
|
||||||
luauF_vectormin,
|
luauF_vectormin,
|
||||||
luauF_vectormax,
|
luauF_vectormax,
|
||||||
|
|
||||||
|
luauF_lerp,
|
||||||
|
|
||||||
// When adding builtins, add them above this line; what follows is 64 "dummy" entries with luauF_missing fallback.
|
// When adding builtins, add them above this line; what follows is 64 "dummy" entries with luauF_missing fallback.
|
||||||
// This is important so that older versions of the runtime that don't support newer builtins automatically fall back via luauF_missing.
|
// This is important so that older versions of the runtime that don't support newer builtins automatically fall back via luauF_missing.
|
||||||
// Given the builtin addition velocity this should always provide a larger compatibility window than bytecode versions suggest.
|
// Given the builtin addition velocity this should always provide a larger compatibility window than bytecode versions suggest.
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauStackLimit, false)
|
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauStackLimit, false)
|
||||||
|
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauPopIncompleteCi, false)
|
||||||
|
|
||||||
// keep max stack allocation request under 1GB
|
// keep max stack allocation request under 1GB
|
||||||
#define MAX_STACK_SIZE (int(1024 / sizeof(TValue)) * 1024 * 1024)
|
#define MAX_STACK_SIZE (int(1024 / sizeof(TValue)) * 1024 * 1024)
|
||||||
|
@ -179,11 +180,23 @@ static void correctstack(lua_State* L, TValue* oldstack)
|
||||||
L->base = (L->base - oldstack) + L->stack;
|
L->base = (L->base - oldstack) + L->stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
void luaD_reallocstack(lua_State* L, int newsize)
|
void luaD_reallocstack(lua_State* L, int newsize, int fornewci)
|
||||||
{
|
{
|
||||||
// throw 'out of memory' error because space for a custom error message cannot be guaranteed here
|
// throw 'out of memory' error because space for a custom error message cannot be guaranteed here
|
||||||
if (DFFlag::LuauStackLimit && newsize > MAX_STACK_SIZE)
|
if (DFFlag::LuauStackLimit && newsize > MAX_STACK_SIZE)
|
||||||
|
{
|
||||||
|
// reallocation was performaed to setup a new CallInfo frame, which we have to remove
|
||||||
|
if (DFFlag::LuauPopIncompleteCi && fornewci)
|
||||||
|
{
|
||||||
|
CallInfo* cip = L->ci - 1;
|
||||||
|
|
||||||
|
L->ci = cip;
|
||||||
|
L->base = cip->base;
|
||||||
|
L->top = cip->top;
|
||||||
|
}
|
||||||
|
|
||||||
luaD_throw(L, LUA_ERRMEM);
|
luaD_throw(L, LUA_ERRMEM);
|
||||||
|
}
|
||||||
|
|
||||||
TValue* oldstack = L->stack;
|
TValue* oldstack = L->stack;
|
||||||
int realsize = newsize + EXTRA_STACK;
|
int realsize = newsize + EXTRA_STACK;
|
||||||
|
@ -208,10 +221,17 @@ void luaD_reallocCI(lua_State* L, int newsize)
|
||||||
|
|
||||||
void luaD_growstack(lua_State* L, int n)
|
void luaD_growstack(lua_State* L, int n)
|
||||||
{
|
{
|
||||||
if (n <= L->stacksize) // double size is enough?
|
if (DFFlag::LuauPopIncompleteCi)
|
||||||
luaD_reallocstack(L, 2 * L->stacksize);
|
{
|
||||||
|
luaD_reallocstack(L, getgrownstacksize(L, n), 0);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
luaD_reallocstack(L, L->stacksize + n);
|
{
|
||||||
|
if (n <= L->stacksize) // double size is enough?
|
||||||
|
luaD_reallocstack(L, 2 * L->stacksize, 0);
|
||||||
|
else
|
||||||
|
luaD_reallocstack(L, L->stacksize + n, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CallInfo* luaD_growCI(lua_State* L)
|
CallInfo* luaD_growCI(lua_State* L)
|
||||||
|
|
14
VM/src/ldo.h
14
VM/src/ldo.h
|
@ -7,11 +7,21 @@
|
||||||
#include "luaconf.h"
|
#include "luaconf.h"
|
||||||
#include "ldebug.h"
|
#include "ldebug.h"
|
||||||
|
|
||||||
|
// returns target stack for 'n' extra elements to reallocate
|
||||||
|
// if possible, stack size growth factor is 2x
|
||||||
|
#define getgrownstacksize(L, n) ((n) <= L->stacksize ? 2 * L->stacksize : L->stacksize + (n))
|
||||||
|
|
||||||
|
#define luaD_checkstackfornewci(L, n) \
|
||||||
|
if ((char*)L->stack_last - (char*)L->top <= (n) * (int)sizeof(TValue)) \
|
||||||
|
luaD_reallocstack(L, getgrownstacksize(L, (n)), 1); \
|
||||||
|
else \
|
||||||
|
condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK, 1));
|
||||||
|
|
||||||
#define luaD_checkstack(L, n) \
|
#define luaD_checkstack(L, n) \
|
||||||
if ((char*)L->stack_last - (char*)L->top <= (n) * (int)sizeof(TValue)) \
|
if ((char*)L->stack_last - (char*)L->top <= (n) * (int)sizeof(TValue)) \
|
||||||
luaD_growstack(L, n); \
|
luaD_growstack(L, n); \
|
||||||
else \
|
else \
|
||||||
condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK));
|
condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK, 0));
|
||||||
|
|
||||||
#define incr_top(L) \
|
#define incr_top(L) \
|
||||||
{ \
|
{ \
|
||||||
|
@ -47,7 +57,7 @@ LUAI_FUNC CallInfo* luaD_growCI(lua_State* L);
|
||||||
LUAI_FUNC void luaD_call(lua_State* L, StkId func, int nresults);
|
LUAI_FUNC void luaD_call(lua_State* L, StkId func, int nresults);
|
||||||
LUAI_FUNC int luaD_pcall(lua_State* L, Pfunc func, void* u, ptrdiff_t oldtop, ptrdiff_t ef);
|
LUAI_FUNC int luaD_pcall(lua_State* L, Pfunc func, void* u, ptrdiff_t oldtop, ptrdiff_t ef);
|
||||||
LUAI_FUNC void luaD_reallocCI(lua_State* L, int newsize);
|
LUAI_FUNC void luaD_reallocCI(lua_State* L, int newsize);
|
||||||
LUAI_FUNC void luaD_reallocstack(lua_State* L, int newsize);
|
LUAI_FUNC void luaD_reallocstack(lua_State* L, int newsize, int fornewci);
|
||||||
LUAI_FUNC void luaD_growstack(lua_State* L, int n);
|
LUAI_FUNC void luaD_growstack(lua_State* L, int n);
|
||||||
LUAI_FUNC void luaD_checkCstack(lua_State* L);
|
LUAI_FUNC void luaD_checkCstack(lua_State* L);
|
||||||
|
|
||||||
|
|
|
@ -442,7 +442,7 @@ static void shrinkstack(lua_State* L)
|
||||||
condhardstacktests(luaD_reallocCI(L, ci_used + 1));
|
condhardstacktests(luaD_reallocCI(L, ci_used + 1));
|
||||||
|
|
||||||
if (3 * size_t(s_used) < size_t(L->stacksize) && 2 * (BASIC_STACK_SIZE + EXTRA_STACK) < L->stacksize)
|
if (3 * size_t(s_used) < size_t(L->stacksize) && 2 * (BASIC_STACK_SIZE + EXTRA_STACK) < L->stacksize)
|
||||||
luaD_reallocstack(L, L->stacksize / 2); // still big enough...
|
luaD_reallocstack(L, L->stacksize / 2, 0); // still big enough...
|
||||||
condhardstacktests(luaD_reallocstack(L, s_used));
|
condhardstacktests(luaD_reallocstack(L, s_used));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauMathMap)
|
LUAU_FASTFLAGVARIABLE(LuauMathLerp)
|
||||||
|
|
||||||
#undef PI
|
#undef PI
|
||||||
#define PI (3.14159265358979323846)
|
#define PI (3.14159265358979323846)
|
||||||
|
@ -418,6 +418,17 @@ static int math_map(lua_State* L)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int math_lerp(lua_State* L)
|
||||||
|
{
|
||||||
|
double a = luaL_checknumber(L, 1);
|
||||||
|
double b = luaL_checknumber(L, 2);
|
||||||
|
double t = luaL_checknumber(L, 3);
|
||||||
|
|
||||||
|
double r = (t == 1.0) ? b : a + (b - a) * t;
|
||||||
|
lua_pushnumber(L, r);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static const luaL_Reg mathlib[] = {
|
static const luaL_Reg mathlib[] = {
|
||||||
{"abs", math_abs},
|
{"abs", math_abs},
|
||||||
{"acos", math_acos},
|
{"acos", math_acos},
|
||||||
|
@ -451,6 +462,7 @@ static const luaL_Reg mathlib[] = {
|
||||||
{"clamp", math_clamp},
|
{"clamp", math_clamp},
|
||||||
{"sign", math_sign},
|
{"sign", math_sign},
|
||||||
{"round", math_round},
|
{"round", math_round},
|
||||||
|
{"map", math_map},
|
||||||
{NULL, NULL},
|
{NULL, NULL},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -471,10 +483,10 @@ int luaopen_math(lua_State* L)
|
||||||
lua_pushnumber(L, HUGE_VAL);
|
lua_pushnumber(L, HUGE_VAL);
|
||||||
lua_setfield(L, -2, "huge");
|
lua_setfield(L, -2, "huge");
|
||||||
|
|
||||||
if (FFlag::LuauMathMap)
|
if (FFlag::LuauMathLerp)
|
||||||
{
|
{
|
||||||
lua_pushcfunction(L, math_map, "map");
|
lua_pushcfunction(L, math_lerp, "lerp");
|
||||||
lua_setfield(L, -2, "map");
|
lua_setfield(L, -2, "lerp");
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -149,7 +149,7 @@ void lua_resetthread(lua_State* L)
|
||||||
L->nCcalls = L->baseCcalls = 0;
|
L->nCcalls = L->baseCcalls = 0;
|
||||||
// clear thread stack
|
// clear thread stack
|
||||||
if (L->stacksize != BASIC_STACK_SIZE + EXTRA_STACK)
|
if (L->stacksize != BASIC_STACK_SIZE + EXTRA_STACK)
|
||||||
luaD_reallocstack(L, BASIC_STACK_SIZE);
|
luaD_reallocstack(L, BASIC_STACK_SIZE, 0);
|
||||||
for (int i = 0; i < L->stacksize; i++)
|
for (int i = 0; i < L->stacksize; i++)
|
||||||
setnilvalue(L->stack + i);
|
setnilvalue(L->stack + i);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
LUAU_DYNAMIC_FASTFLAG(LuauPopIncompleteCi)
|
||||||
|
|
||||||
// Disable c99-designator to avoid the warning in CGOTO dispatch table
|
// Disable c99-designator to avoid the warning in CGOTO dispatch table
|
||||||
#ifdef __clang__
|
#ifdef __clang__
|
||||||
#if __has_warning("-Wc99-designator")
|
#if __has_warning("-Wc99-designator")
|
||||||
|
@ -935,7 +937,14 @@ reentry:
|
||||||
// note: this reallocs stack, but we don't need to VM_PROTECT this
|
// note: this reallocs stack, but we don't need to VM_PROTECT this
|
||||||
// this is because we're going to modify base/savedpc manually anyhow
|
// this is because we're going to modify base/savedpc manually anyhow
|
||||||
// crucially, we can't use ra/argtop after this line
|
// crucially, we can't use ra/argtop after this line
|
||||||
|
if (DFFlag::LuauPopIncompleteCi)
|
||||||
|
{
|
||||||
|
luaD_checkstackfornewci(L, ccl->stacksize);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
luaD_checkstack(L, ccl->stacksize);
|
luaD_checkstack(L, ccl->stacksize);
|
||||||
|
}
|
||||||
|
|
||||||
LUAU_ASSERT(ci->top <= L->stack_last);
|
LUAU_ASSERT(ci->top <= L->stack_last);
|
||||||
|
|
||||||
|
@ -3071,7 +3080,14 @@ int luau_precall(lua_State* L, StkId func, int nresults)
|
||||||
L->base = ci->base;
|
L->base = ci->base;
|
||||||
// Note: L->top is assigned externally
|
// Note: L->top is assigned externally
|
||||||
|
|
||||||
|
if (DFFlag::LuauPopIncompleteCi)
|
||||||
|
{
|
||||||
|
luaD_checkstackfornewci(L, ccl->stacksize);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
luaD_checkstack(L, ccl->stacksize);
|
luaD_checkstack(L, ccl->stacksize);
|
||||||
|
}
|
||||||
LUAU_ASSERT(ci->top <= L->stack_last);
|
LUAU_ASSERT(ci->top <= L->stack_last);
|
||||||
|
|
||||||
if (!ccl->isC)
|
if (!ccl->isC)
|
||||||
|
|
|
@ -18,6 +18,7 @@ LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(DebugLuauFreezeArena)
|
LUAU_FASTFLAG(DebugLuauFreezeArena)
|
||||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||||
LUAU_FASTFLAG(StudioReportLuauAny2)
|
LUAU_FASTFLAG(StudioReportLuauAny2)
|
||||||
|
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
||||||
|
|
||||||
|
|
||||||
struct ATSFixture : BuiltinsFixture
|
struct ATSFixture : BuiltinsFixture
|
||||||
|
@ -97,7 +98,10 @@ end
|
||||||
|
|
||||||
LUAU_ASSERT(module->ats.typeInfo.size() == 3);
|
LUAU_ASSERT(module->ats.typeInfo.size() == 3);
|
||||||
LUAU_ASSERT(module->ats.typeInfo[1].code == Pattern::TypePk);
|
LUAU_ASSERT(module->ats.typeInfo[1].code == Pattern::TypePk);
|
||||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "local function fallible(t: number): ...any\n if t > 0 then\n return true, t\n end\n return false, 'must be positive'\nend");
|
LUAU_ASSERT(
|
||||||
|
module->ats.typeInfo[0].node ==
|
||||||
|
"local function fallible(t: number): ...any\n if t > 0 then\n return true, t\n end\n return false, 'must be positive'\nend"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ATSFixture, "typepacks_no_ret")
|
TEST_CASE_FIXTURE(ATSFixture, "typepacks_no_ret")
|
||||||
|
@ -581,6 +585,9 @@ TEST_CASE_FIXTURE(ATSFixture, "racing_spawning_1")
|
||||||
ScopedFastFlag sff[] = {
|
ScopedFastFlag sff[] = {
|
||||||
{FFlag::LuauSolverV2, true},
|
{FFlag::LuauSolverV2, true},
|
||||||
{FFlag::StudioReportLuauAny2, true},
|
{FFlag::StudioReportLuauAny2, true},
|
||||||
|
// Previously we'd report an error because number <: 'a is not a
|
||||||
|
// supertype.
|
||||||
|
{FFlag::LuauTrackInteriorFreeTypesOnScope, true}
|
||||||
};
|
};
|
||||||
|
|
||||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||||
|
@ -632,7 +639,7 @@ initialize()
|
||||||
)";
|
)";
|
||||||
|
|
||||||
CheckResult result1 = frontend.check("game/Gui/Modules/A");
|
CheckResult result1 = frontend.check("game/Gui/Modules/A");
|
||||||
LUAU_REQUIRE_ERROR_COUNT(5, result1);
|
LUAU_REQUIRE_ERROR_COUNT(4, result1);
|
||||||
|
|
||||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||||
|
|
||||||
|
|
|
@ -4293,8 +4293,7 @@ end
|
||||||
foo(@1)
|
foo(@1)
|
||||||
)");
|
)");
|
||||||
|
|
||||||
const std::optional<std::string> EXPECTED_INSERT =
|
const std::optional<std::string> EXPECTED_INSERT = FFlag::LuauSolverV2 ? "function(...: number): number end" : "function(...): number end";
|
||||||
FFlag::LuauSolverV2 ? "function(...: number): number end" : "function(...): number end";
|
|
||||||
|
|
||||||
auto ac = autocomplete('1');
|
auto ac = autocomplete('1');
|
||||||
|
|
||||||
|
|
|
@ -23,10 +23,8 @@ LUAU_FASTINT(LuauCompileInlineThresholdMaxBoost)
|
||||||
LUAU_FASTINT(LuauCompileLoopUnrollThreshold)
|
LUAU_FASTINT(LuauCompileLoopUnrollThreshold)
|
||||||
LUAU_FASTINT(LuauCompileLoopUnrollThresholdMaxBoost)
|
LUAU_FASTINT(LuauCompileLoopUnrollThresholdMaxBoost)
|
||||||
LUAU_FASTINT(LuauRecursionLimit)
|
LUAU_FASTINT(LuauRecursionLimit)
|
||||||
LUAU_FASTFLAG(LuauCompileVectorTypeInfo)
|
|
||||||
LUAU_FASTFLAG(LuauCompileOptimizeRevArith)
|
LUAU_FASTFLAG(LuauCompileOptimizeRevArith)
|
||||||
LUAU_FASTFLAG(LuauCompileLibraryConstants)
|
LUAU_FASTFLAG(LuauCompileLibraryConstants)
|
||||||
LUAU_FASTFLAG(LuauVectorBuiltins)
|
|
||||||
LUAU_FASTFLAG(LuauVectorFolding)
|
LUAU_FASTFLAG(LuauVectorFolding)
|
||||||
LUAU_FASTFLAG(LuauCompileDisabledBuiltins)
|
LUAU_FASTFLAG(LuauCompileDisabledBuiltins)
|
||||||
|
|
||||||
|
@ -1492,7 +1490,6 @@ RETURN R0 1
|
||||||
|
|
||||||
TEST_CASE("ConstantFoldVectorArith")
|
TEST_CASE("ConstantFoldVectorArith")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauVectorBuiltins{FFlag::LuauVectorBuiltins, true};
|
|
||||||
ScopedFastFlag luauVectorFolding{FFlag::LuauVectorFolding, true};
|
ScopedFastFlag luauVectorFolding{FFlag::LuauVectorFolding, true};
|
||||||
|
|
||||||
CHECK_EQ("\n" + compileFunction("local n = 2; local a, b = vector.create(1, 2, 3), vector.create(2, 4, 8); return a + b", 0, 2), R"(
|
CHECK_EQ("\n" + compileFunction("local n = 2; local a, b = vector.create(1, 2, 3), vector.create(2, 4, 8); return a + b", 0, 2), R"(
|
||||||
|
@ -1560,7 +1557,6 @@ RETURN R0 1
|
||||||
|
|
||||||
TEST_CASE("ConstantFoldVectorArith4Wide")
|
TEST_CASE("ConstantFoldVectorArith4Wide")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauVectorBuiltins{FFlag::LuauVectorBuiltins, true};
|
|
||||||
ScopedFastFlag luauVectorFolding{FFlag::LuauVectorFolding, true};
|
ScopedFastFlag luauVectorFolding{FFlag::LuauVectorFolding, true};
|
||||||
|
|
||||||
CHECK_EQ("\n" + compileFunction("local n = 2; local a, b = vector.create(1, 2, 3, 4), vector.create(2, 4, 8, 1); return a + b", 0, 2), R"(
|
CHECK_EQ("\n" + compileFunction("local n = 2; local a, b = vector.create(1, 2, 3, 4), vector.create(2, 4, 8, 1); return a + b", 0, 2), R"(
|
||||||
|
@ -5137,7 +5133,6 @@ RETURN R0 1
|
||||||
|
|
||||||
TEST_CASE("VectorConstantFields")
|
TEST_CASE("VectorConstantFields")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauVectorBuiltins{FFlag::LuauVectorBuiltins, true};
|
|
||||||
ScopedFastFlag luauCompileLibraryConstants{FFlag::LuauCompileLibraryConstants, true};
|
ScopedFastFlag luauCompileLibraryConstants{FFlag::LuauCompileLibraryConstants, true};
|
||||||
|
|
||||||
CHECK_EQ("\n" + compileFunction("return vector.one, vector.zero", 0, 2), R"(
|
CHECK_EQ("\n" + compileFunction("return vector.one, vector.zero", 0, 2), R"(
|
||||||
|
@ -8666,8 +8661,6 @@ end
|
||||||
|
|
||||||
TEST_CASE("BuiltinTypeVector")
|
TEST_CASE("BuiltinTypeVector")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauCompileVectorTypeInfo{FFlag::LuauCompileVectorTypeInfo, true};
|
|
||||||
|
|
||||||
CHECK_EQ(
|
CHECK_EQ(
|
||||||
"\n" + compileTypeTable(R"(
|
"\n" + compileTypeTable(R"(
|
||||||
function myfunc(test: Instance, pos: vector)
|
function myfunc(test: Instance, pos: vector)
|
||||||
|
|
|
@ -31,16 +31,16 @@ extern int optimizationLevel;
|
||||||
void luaC_fullgc(lua_State* L);
|
void luaC_fullgc(lua_State* L);
|
||||||
void luaC_validate(lua_State* L);
|
void luaC_validate(lua_State* L);
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauMathMap)
|
LUAU_FASTFLAG(LuauMathLerp)
|
||||||
LUAU_FASTFLAG(DebugLuauAbortingChecks)
|
LUAU_FASTFLAG(DebugLuauAbortingChecks)
|
||||||
LUAU_FASTINT(CodegenHeuristicsInstructionLimit)
|
LUAU_FASTINT(CodegenHeuristicsInstructionLimit)
|
||||||
LUAU_DYNAMIC_FASTFLAG(LuauStackLimit)
|
LUAU_DYNAMIC_FASTFLAG(LuauStackLimit)
|
||||||
LUAU_FASTFLAG(LuauVectorDefinitions)
|
|
||||||
LUAU_DYNAMIC_FASTFLAG(LuauDebugInfoInvArgLeftovers)
|
LUAU_DYNAMIC_FASTFLAG(LuauDebugInfoInvArgLeftovers)
|
||||||
LUAU_FASTFLAG(LuauVectorLibNativeCodegen)
|
LUAU_FASTFLAG(LuauVectorLibNativeCodegen)
|
||||||
LUAU_FASTFLAG(LuauVectorLibNativeDot)
|
LUAU_FASTFLAG(LuauVectorLibNativeDot)
|
||||||
LUAU_FASTFLAG(LuauVectorBuiltins)
|
|
||||||
LUAU_FASTFLAG(LuauVectorMetatable)
|
LUAU_FASTFLAG(LuauVectorMetatable)
|
||||||
|
LUAU_FASTFLAG(LuauBufferBitMethods)
|
||||||
|
LUAU_FASTFLAG(LuauCodeGenLimitLiveSlotReuse)
|
||||||
|
|
||||||
static lua_CompileOptions defaultOptions()
|
static lua_CompileOptions defaultOptions()
|
||||||
{
|
{
|
||||||
|
@ -654,12 +654,14 @@ TEST_CASE("Basic")
|
||||||
|
|
||||||
TEST_CASE("Buffers")
|
TEST_CASE("Buffers")
|
||||||
{
|
{
|
||||||
|
ScopedFastFlag luauBufferBitMethods{FFlag::LuauBufferBitMethods, true};
|
||||||
|
|
||||||
runConformance("buffers.lua");
|
runConformance("buffers.lua");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Math")
|
TEST_CASE("Math")
|
||||||
{
|
{
|
||||||
ScopedFastFlag LuauMathMap{FFlag::LuauMathMap, true};
|
ScopedFastFlag LuauMathLerp{FFlag::LuauMathLerp, true};
|
||||||
|
|
||||||
runConformance("math.lua");
|
runConformance("math.lua");
|
||||||
}
|
}
|
||||||
|
@ -891,7 +893,6 @@ TEST_CASE("Vector")
|
||||||
|
|
||||||
TEST_CASE("VectorLibrary")
|
TEST_CASE("VectorLibrary")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauVectorBuiltins{FFlag::LuauVectorBuiltins, true};
|
|
||||||
ScopedFastFlag luauVectorLibNativeCodegen{FFlag::LuauVectorLibNativeCodegen, true};
|
ScopedFastFlag luauVectorLibNativeCodegen{FFlag::LuauVectorLibNativeCodegen, true};
|
||||||
ScopedFastFlag luauVectorLibNativeDot{FFlag::LuauVectorLibNativeDot, true};
|
ScopedFastFlag luauVectorLibNativeDot{FFlag::LuauVectorLibNativeDot, true};
|
||||||
ScopedFastFlag luauVectorMetatable{FFlag::LuauVectorMetatable, true};
|
ScopedFastFlag luauVectorMetatable{FFlag::LuauVectorMetatable, true};
|
||||||
|
@ -911,9 +912,7 @@ TEST_CASE("VectorLibrary")
|
||||||
copts.optimizationLevel = 2;
|
copts.optimizationLevel = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
runConformance(
|
runConformance("vector_library.lua", [](lua_State* L) {}, nullptr, nullptr, &copts);
|
||||||
"vector_library.lua", [](lua_State* L) {}, nullptr, nullptr, &copts
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void populateRTTI(lua_State* L, Luau::TypeId type)
|
static void populateRTTI(lua_State* L, Luau::TypeId type)
|
||||||
|
@ -987,7 +986,7 @@ static void populateRTTI(lua_State* L, Luau::TypeId type)
|
||||||
|
|
||||||
TEST_CASE("Types")
|
TEST_CASE("Types")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauVectorDefinitions{FFlag::LuauVectorDefinitions, true};
|
ScopedFastFlag luauMathLerp{FFlag::LuauMathLerp, false}; // waiting for math.lerp to be added to embedded type definitions
|
||||||
|
|
||||||
runConformance(
|
runConformance(
|
||||||
"types.lua",
|
"types.lua",
|
||||||
|
@ -2578,6 +2577,8 @@ TEST_CASE("SafeEnv")
|
||||||
|
|
||||||
TEST_CASE("Native")
|
TEST_CASE("Native")
|
||||||
{
|
{
|
||||||
|
ScopedFastFlag luauCodeGenLimitLiveSlotReuse{FFlag::LuauCodeGenLimitLiveSlotReuse, true};
|
||||||
|
|
||||||
// This tests requires code to run natively, otherwise all 'is_native' checks will fail
|
// This tests requires code to run natively, otherwise all 'is_native' checks will fail
|
||||||
if (!codegen || !luau_codegen_supported())
|
if (!codegen || !luau_codegen_supported())
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -23,15 +23,11 @@ struct ESFixture : Fixture
|
||||||
TypeId genericT = arena_.addType(GenericType{"T"});
|
TypeId genericT = arena_.addType(GenericType{"T"});
|
||||||
TypeId genericU = arena_.addType(GenericType{"U"});
|
TypeId genericU = arena_.addType(GenericType{"U"});
|
||||||
|
|
||||||
TypeId numberToString = arena_.addType(FunctionType{
|
TypeId numberToString =
|
||||||
arena_.addTypePack({builtinTypes->numberType}),
|
arena_.addType(FunctionType{arena_.addTypePack({builtinTypes->numberType}), arena_.addTypePack({builtinTypes->stringType})});
|
||||||
arena_.addTypePack({builtinTypes->stringType})
|
|
||||||
});
|
|
||||||
|
|
||||||
TypeId stringToNumber = arena_.addType(FunctionType{
|
TypeId stringToNumber =
|
||||||
arena_.addTypePack({builtinTypes->stringType}),
|
arena_.addType(FunctionType{arena_.addTypePack({builtinTypes->stringType}), arena_.addTypePack({builtinTypes->numberType})});
|
||||||
arena_.addTypePack({builtinTypes->numberType})
|
|
||||||
});
|
|
||||||
|
|
||||||
ESFixture()
|
ESFixture()
|
||||||
: simplifier(newSimplifier(arena, builtinTypes))
|
: simplifier(newSimplifier(arena, builtinTypes))
|
||||||
|
@ -163,10 +159,11 @@ TEST_CASE_FIXTURE(ESFixture, "never & string")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "string & (unknown | never)")
|
TEST_CASE_FIXTURE(ESFixture, "string & (unknown | never)")
|
||||||
{
|
{
|
||||||
CHECK("string" == simplifyStr(arena->addType(IntersectionType{{
|
CHECK(
|
||||||
builtinTypes->stringType,
|
"string" == simplifyStr(arena->addType(
|
||||||
arena->addType(UnionType{{builtinTypes->unknownType, builtinTypes->neverType}})
|
IntersectionType{{builtinTypes->stringType, arena->addType(UnionType{{builtinTypes->unknownType, builtinTypes->neverType}})}}
|
||||||
}})));
|
))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "true | false")
|
TEST_CASE_FIXTURE(ESFixture, "true | false")
|
||||||
|
@ -211,23 +208,24 @@ TEST_CASE_FIXTURE(ESFixture, "error | unknown")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "\"hello\" | string")
|
TEST_CASE_FIXTURE(ESFixture, "\"hello\" | string")
|
||||||
{
|
{
|
||||||
CHECK("string" == simplifyStr(arena->addType(UnionType{{
|
CHECK("string" == simplifyStr(arena->addType(UnionType{{arena->addType(SingletonType{StringSingleton{"hello"}}), builtinTypes->stringType}})));
|
||||||
arena->addType(SingletonType{StringSingleton{"hello"}}), builtinTypes->stringType
|
|
||||||
}})));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "\"hello\" | \"world\" | \"hello\"")
|
TEST_CASE_FIXTURE(ESFixture, "\"hello\" | \"world\" | \"hello\"")
|
||||||
{
|
{
|
||||||
CHECK("\"hello\" | \"world\"" == simplifyStr(arena->addType(UnionType{{
|
CHECK(
|
||||||
|
"\"hello\" | \"world\"" == simplifyStr(arena->addType(UnionType{{
|
||||||
arena->addType(SingletonType{StringSingleton{"hello"}}),
|
arena->addType(SingletonType{StringSingleton{"hello"}}),
|
||||||
arena->addType(SingletonType{StringSingleton{"world"}}),
|
arena->addType(SingletonType{StringSingleton{"world"}}),
|
||||||
arena->addType(SingletonType{StringSingleton{"hello"}}),
|
arena->addType(SingletonType{StringSingleton{"hello"}}),
|
||||||
}})));
|
}}))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "nil | boolean | number | string | thread | function | table | class | buffer")
|
TEST_CASE_FIXTURE(ESFixture, "nil | boolean | number | string | thread | function | table | class | buffer")
|
||||||
{
|
{
|
||||||
CHECK("unknown" == simplifyStr(arena->addType(UnionType{{
|
CHECK(
|
||||||
|
"unknown" == simplifyStr(arena->addType(UnionType{{
|
||||||
builtinTypes->nilType,
|
builtinTypes->nilType,
|
||||||
builtinTypes->booleanType,
|
builtinTypes->booleanType,
|
||||||
builtinTypes->numberType,
|
builtinTypes->numberType,
|
||||||
|
@ -237,86 +235,70 @@ TEST_CASE_FIXTURE(ESFixture, "nil | boolean | number | string | thread | functio
|
||||||
builtinTypes->tableType,
|
builtinTypes->tableType,
|
||||||
builtinTypes->classType,
|
builtinTypes->classType,
|
||||||
builtinTypes->bufferType,
|
builtinTypes->bufferType,
|
||||||
}})));
|
}}))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "Parent & number")
|
TEST_CASE_FIXTURE(ESFixture, "Parent & number")
|
||||||
{
|
{
|
||||||
CHECK("never" == simplifyStr(arena->addType(IntersectionType{{
|
CHECK("never" == simplifyStr(arena->addType(IntersectionType{{parentClass, builtinTypes->numberType}})));
|
||||||
parentClass, builtinTypes->numberType
|
|
||||||
}})));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "Child & Parent")
|
TEST_CASE_FIXTURE(ESFixture, "Child & Parent")
|
||||||
{
|
{
|
||||||
CHECK("Child" == simplifyStr(arena->addType(IntersectionType{{
|
CHECK("Child" == simplifyStr(arena->addType(IntersectionType{{childClass, parentClass}})));
|
||||||
childClass, parentClass
|
|
||||||
}})));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "Child & Unrelated")
|
TEST_CASE_FIXTURE(ESFixture, "Child & Unrelated")
|
||||||
{
|
{
|
||||||
CHECK("never" == simplifyStr(arena->addType(IntersectionType{{
|
CHECK("never" == simplifyStr(arena->addType(IntersectionType{{childClass, unrelatedClass}})));
|
||||||
childClass, unrelatedClass
|
|
||||||
}})));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "Child | Parent")
|
TEST_CASE_FIXTURE(ESFixture, "Child | Parent")
|
||||||
{
|
{
|
||||||
CHECK("Parent" == simplifyStr(arena->addType(UnionType{{
|
CHECK("Parent" == simplifyStr(arena->addType(UnionType{{childClass, parentClass}})));
|
||||||
childClass, parentClass
|
|
||||||
}})));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "class | Child")
|
TEST_CASE_FIXTURE(ESFixture, "class | Child")
|
||||||
{
|
{
|
||||||
CHECK("class" == simplifyStr(arena->addType(UnionType{{
|
CHECK("class" == simplifyStr(arena->addType(UnionType{{builtinTypes->classType, childClass}})));
|
||||||
builtinTypes->classType, childClass
|
|
||||||
}})));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "Parent | class | Child")
|
TEST_CASE_FIXTURE(ESFixture, "Parent | class | Child")
|
||||||
{
|
{
|
||||||
CHECK("class" == simplifyStr(arena->addType(UnionType{{
|
CHECK("class" == simplifyStr(arena->addType(UnionType{{parentClass, builtinTypes->classType, childClass}})));
|
||||||
parentClass, builtinTypes->classType, childClass
|
|
||||||
}})));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "Parent | Unrelated")
|
TEST_CASE_FIXTURE(ESFixture, "Parent | Unrelated")
|
||||||
{
|
{
|
||||||
CHECK("Parent | Unrelated" == simplifyStr(arena->addType(UnionType{{
|
CHECK("Parent | Unrelated" == simplifyStr(arena->addType(UnionType{{parentClass, unrelatedClass}})));
|
||||||
parentClass, unrelatedClass
|
|
||||||
}})));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "never | Parent | Unrelated")
|
TEST_CASE_FIXTURE(ESFixture, "never | Parent | Unrelated")
|
||||||
{
|
{
|
||||||
CHECK("Parent | Unrelated" == simplifyStr(arena->addType(UnionType{{
|
CHECK("Parent | Unrelated" == simplifyStr(arena->addType(UnionType{{builtinTypes->neverType, parentClass, unrelatedClass}})));
|
||||||
builtinTypes->neverType, parentClass, unrelatedClass
|
|
||||||
}})));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "never | Parent | (number & string) | Unrelated")
|
TEST_CASE_FIXTURE(ESFixture, "never | Parent | (number & string) | Unrelated")
|
||||||
{
|
{
|
||||||
CHECK("Parent | Unrelated" == simplifyStr(arena->addType(UnionType{{
|
CHECK(
|
||||||
builtinTypes->neverType, parentClass,
|
"Parent | Unrelated" == simplifyStr(arena->addType(UnionType{
|
||||||
|
{builtinTypes->neverType,
|
||||||
|
parentClass,
|
||||||
arena->addType(IntersectionType{{builtinTypes->numberType, builtinTypes->stringType}}),
|
arena->addType(IntersectionType{{builtinTypes->numberType, builtinTypes->stringType}}),
|
||||||
unrelatedClass
|
unrelatedClass}
|
||||||
}})));
|
}))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "T & U")
|
TEST_CASE_FIXTURE(ESFixture, "T & U")
|
||||||
{
|
{
|
||||||
CHECK("T & U" == simplifyStr(arena->addType(IntersectionType{{
|
CHECK("T & U" == simplifyStr(arena->addType(IntersectionType{{genericT, genericU}})));
|
||||||
genericT, genericU
|
|
||||||
}})));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "boolean & true")
|
TEST_CASE_FIXTURE(ESFixture, "boolean & true")
|
||||||
{
|
{
|
||||||
CHECK("true" == simplifyStr(arena->addType(IntersectionType{{
|
CHECK("true" == simplifyStr(arena->addType(IntersectionType{{builtinTypes->booleanType, builtinTypes->trueType}})));
|
||||||
builtinTypes->booleanType, builtinTypes->trueType
|
|
||||||
}})));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "boolean & (true | number | string | thread | function | table | class | buffer)")
|
TEST_CASE_FIXTURE(ESFixture, "boolean & (true | number | string | thread | function | table | class | buffer)")
|
||||||
|
@ -332,23 +314,17 @@ TEST_CASE_FIXTURE(ESFixture, "boolean & (true | number | string | thread | funct
|
||||||
builtinTypes->bufferType,
|
builtinTypes->bufferType,
|
||||||
}});
|
}});
|
||||||
|
|
||||||
CHECK("true" == simplifyStr(arena->addType(IntersectionType{{
|
CHECK("true" == simplifyStr(arena->addType(IntersectionType{{builtinTypes->booleanType, truthy}})));
|
||||||
builtinTypes->booleanType, truthy
|
|
||||||
}})));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "boolean & ~(false?)")
|
TEST_CASE_FIXTURE(ESFixture, "boolean & ~(false?)")
|
||||||
{
|
{
|
||||||
CHECK("true" == simplifyStr(arena->addType(IntersectionType{{
|
CHECK("true" == simplifyStr(arena->addType(IntersectionType{{builtinTypes->booleanType, builtinTypes->truthyType}})));
|
||||||
builtinTypes->booleanType, builtinTypes->truthyType
|
|
||||||
}})));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "false & ~(false?)")
|
TEST_CASE_FIXTURE(ESFixture, "false & ~(false?)")
|
||||||
{
|
{
|
||||||
CHECK("never" == simplifyStr(arena->addType(IntersectionType{{
|
CHECK("never" == simplifyStr(arena->addType(IntersectionType{{builtinTypes->falseType, builtinTypes->truthyType}})));
|
||||||
builtinTypes->falseType, builtinTypes->truthyType
|
|
||||||
}})));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "(number) -> string & (number) -> string")
|
TEST_CASE_FIXTURE(ESFixture, "(number) -> string & (number) -> string")
|
||||||
|
@ -399,28 +375,25 @@ TEST_CASE_FIXTURE(ESFixture, "(number) -> string | (string) -> number")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "add<number, number>")
|
TEST_CASE_FIXTURE(ESFixture, "add<number, number>")
|
||||||
{
|
{
|
||||||
CHECK("number" == simplifyStr(arena->addType(
|
CHECK(
|
||||||
TypeFunctionInstanceType{builtinTypeFunctions().addFunc, {
|
"number" ==
|
||||||
builtinTypes->numberType, builtinTypes->numberType
|
simplifyStr(arena->addType(TypeFunctionInstanceType{builtinTypeFunctions().addFunc, {builtinTypes->numberType, builtinTypes->numberType}}))
|
||||||
}}
|
);
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "union<number, number>")
|
TEST_CASE_FIXTURE(ESFixture, "union<number, number>")
|
||||||
{
|
{
|
||||||
CHECK("number" == simplifyStr(arena->addType(
|
CHECK(
|
||||||
TypeFunctionInstanceType{builtinTypeFunctions().unionFunc, {
|
"number" ==
|
||||||
builtinTypes->numberType, builtinTypes->numberType
|
simplifyStr(arena->addType(TypeFunctionInstanceType{builtinTypeFunctions().unionFunc, {builtinTypes->numberType, builtinTypes->numberType}}))
|
||||||
}}
|
);
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "never & ~string")
|
TEST_CASE_FIXTURE(ESFixture, "never & ~string")
|
||||||
{
|
{
|
||||||
CHECK("never" == simplifyStr(arena->addType(IntersectionType{{
|
CHECK(
|
||||||
builtinTypes->neverType,
|
"never" == simplifyStr(arena->addType(IntersectionType{{builtinTypes->neverType, arena->addType(NegationType{builtinTypes->stringType})}}))
|
||||||
arena->addType(NegationType{builtinTypes->stringType})
|
);
|
||||||
}})));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "blocked & never")
|
TEST_CASE_FIXTURE(ESFixture, "blocked & never")
|
||||||
|
@ -444,7 +417,9 @@ TEST_CASE_FIXTURE(ESFixture, "blocked & ~number & function")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "(number | boolean | string | nil | table) & (false | nil)")
|
TEST_CASE_FIXTURE(ESFixture, "(number | boolean | string | nil | table) & (false | nil)")
|
||||||
{
|
{
|
||||||
const TypeId t1 = arena->addType(UnionType{{builtinTypes->numberType, builtinTypes->booleanType, builtinTypes->stringType, builtinTypes->nilType, builtinTypes->tableType}});
|
const TypeId t1 = arena->addType(
|
||||||
|
UnionType{{builtinTypes->numberType, builtinTypes->booleanType, builtinTypes->stringType, builtinTypes->nilType, builtinTypes->tableType}}
|
||||||
|
);
|
||||||
|
|
||||||
CHECK("false?" == simplifyStr(arena->addType(IntersectionType{{t1, builtinTypes->falsyType}})));
|
CHECK("false?" == simplifyStr(arena->addType(IntersectionType{{t1, builtinTypes->falsyType}})));
|
||||||
}
|
}
|
||||||
|
@ -493,26 +468,17 @@ TEST_CASE_FIXTURE(ESFixture, "(blocked & number) | (blocked & number)")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "{} & unknown")
|
TEST_CASE_FIXTURE(ESFixture, "{} & unknown")
|
||||||
{
|
{
|
||||||
CHECK("{ }" == simplifyStr(arena->addType(IntersectionType{{
|
CHECK("{ }" == simplifyStr(arena->addType(IntersectionType{{tbl({}), builtinTypes->unknownType}})));
|
||||||
tbl({}),
|
|
||||||
builtinTypes->unknownType
|
|
||||||
}})));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "{} & table")
|
TEST_CASE_FIXTURE(ESFixture, "{} & table")
|
||||||
{
|
{
|
||||||
CHECK("{ }" == simplifyStr(arena->addType(IntersectionType{{
|
CHECK("{ }" == simplifyStr(arena->addType(IntersectionType{{tbl({}), builtinTypes->tableType}})));
|
||||||
tbl({}),
|
|
||||||
builtinTypes->tableType
|
|
||||||
}})));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "{} & ~(false?)")
|
TEST_CASE_FIXTURE(ESFixture, "{} & ~(false?)")
|
||||||
{
|
{
|
||||||
CHECK("{ }" == simplifyStr(arena->addType(IntersectionType{{
|
CHECK("{ }" == simplifyStr(arena->addType(IntersectionType{{tbl({}), builtinTypes->truthyType}})));
|
||||||
tbl({}),
|
|
||||||
builtinTypes->truthyType
|
|
||||||
}})));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "{x: number?} & {x: number}")
|
TEST_CASE_FIXTURE(ESFixture, "{x: number?} & {x: number}")
|
||||||
|
@ -606,10 +572,7 @@ TEST_CASE_FIXTURE(ESFixture, "{ x: number } & ~boolean")
|
||||||
{
|
{
|
||||||
const TypeId tblTy = tbl(TableType::Props{{"x", builtinTypes->numberType}});
|
const TypeId tblTy = tbl(TableType::Props{{"x", builtinTypes->numberType}});
|
||||||
|
|
||||||
const TypeId ty = arena->addType(IntersectionType{{
|
const TypeId ty = arena->addType(IntersectionType{{tblTy, arena->addType(NegationType{builtinTypes->booleanType})}});
|
||||||
tblTy,
|
|
||||||
arena->addType(NegationType{builtinTypes->booleanType})
|
|
||||||
}});
|
|
||||||
|
|
||||||
CHECK("{ x: number }" == simplifyStr(ty));
|
CHECK("{ x: number }" == simplifyStr(ty));
|
||||||
}
|
}
|
||||||
|
@ -634,10 +597,7 @@ TEST_CASE_FIXTURE(ESFixture, "string & (\"hi\" | \"bye\")")
|
||||||
const TypeId hi = arena->addType(SingletonType{StringSingleton{"hi"}});
|
const TypeId hi = arena->addType(SingletonType{StringSingleton{"hi"}});
|
||||||
const TypeId bye = arena->addType(SingletonType{StringSingleton{"bye"}});
|
const TypeId bye = arena->addType(SingletonType{StringSingleton{"bye"}});
|
||||||
|
|
||||||
CHECK("\"bye\" | \"hi\"" == simplifyStr(arena->addType(IntersectionType{{
|
CHECK("\"bye\" | \"hi\"" == simplifyStr(arena->addType(IntersectionType{{builtinTypes->stringType, arena->addType(UnionType{{hi, bye}})}})));
|
||||||
builtinTypes->stringType,
|
|
||||||
arena->addType(UnionType{{hi, bye}})
|
|
||||||
}})));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "(\"err\" | \"ok\") & ~\"ok\"")
|
TEST_CASE_FIXTURE(ESFixture, "(\"err\" | \"ok\") & ~\"ok\"")
|
||||||
|
@ -646,46 +606,32 @@ TEST_CASE_FIXTURE(ESFixture, "(\"err\" | \"ok\") & ~\"ok\"")
|
||||||
TypeId ok1 = arena->addType(SingletonType{StringSingleton{"ok"}});
|
TypeId ok1 = arena->addType(SingletonType{StringSingleton{"ok"}});
|
||||||
TypeId ok2 = arena->addType(SingletonType{StringSingleton{"ok"}});
|
TypeId ok2 = arena->addType(SingletonType{StringSingleton{"ok"}});
|
||||||
|
|
||||||
TypeId ty = arena->addType(IntersectionType{{
|
TypeId ty = arena->addType(IntersectionType{{arena->addType(UnionType{{err, ok1}}), arena->addType(NegationType{ok2})}});
|
||||||
arena->addType(UnionType{{err, ok1}}),
|
|
||||||
arena->addType(NegationType{ok2})
|
|
||||||
}});
|
|
||||||
|
|
||||||
CHECK("\"err\"" == simplifyStr(ty));
|
CHECK("\"err\"" == simplifyStr(ty));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "(Child | Unrelated) & ~Child")
|
TEST_CASE_FIXTURE(ESFixture, "(Child | Unrelated) & ~Child")
|
||||||
{
|
{
|
||||||
const TypeId ty = arena->addType(IntersectionType{{
|
const TypeId ty =
|
||||||
arena->addType(UnionType{{childClass, unrelatedClass}}),
|
arena->addType(IntersectionType{{arena->addType(UnionType{{childClass, unrelatedClass}}), arena->addType(NegationType{childClass})}});
|
||||||
arena->addType(NegationType{childClass})
|
|
||||||
}});
|
|
||||||
|
|
||||||
CHECK("Unrelated" == simplifyStr(ty));
|
CHECK("Unrelated" == simplifyStr(ty));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "string & ~Child")
|
TEST_CASE_FIXTURE(ESFixture, "string & ~Child")
|
||||||
{
|
{
|
||||||
CHECK("string" == simplifyStr(arena->addType(IntersectionType{{
|
CHECK("string" == simplifyStr(arena->addType(IntersectionType{{builtinTypes->stringType, arena->addType(NegationType{childClass})}})));
|
||||||
builtinTypes->stringType,
|
|
||||||
arena->addType(NegationType{childClass})
|
|
||||||
}})));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "(Child | Unrelated) & Child")
|
TEST_CASE_FIXTURE(ESFixture, "(Child | Unrelated) & Child")
|
||||||
{
|
{
|
||||||
CHECK("Child" == simplifyStr(arena->addType(IntersectionType{{
|
CHECK("Child" == simplifyStr(arena->addType(IntersectionType{{arena->addType(UnionType{{childClass, unrelatedClass}}), childClass}})));
|
||||||
arena->addType(UnionType{{childClass, unrelatedClass}}),
|
|
||||||
childClass
|
|
||||||
}})));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "(Child | AnotherChild) & ~Child")
|
TEST_CASE_FIXTURE(ESFixture, "(Child | AnotherChild) & ~Child")
|
||||||
{
|
{
|
||||||
CHECK("Child" == simplifyStr(arena->addType(IntersectionType{{
|
CHECK("Child" == simplifyStr(arena->addType(IntersectionType{{arena->addType(UnionType{{childClass, anotherChild}}), childClass}})));
|
||||||
arena->addType(UnionType{{childClass, anotherChild}}),
|
|
||||||
childClass
|
|
||||||
}})));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ESFixture, "{ tag: \"Part\", x: never }")
|
TEST_CASE_FIXTURE(ESFixture, "{ tag: \"Part\", x: never }")
|
||||||
|
@ -706,11 +652,7 @@ TEST_CASE_FIXTURE(ESFixture, "{ tag: \"Part\", x: number? } & { x: string }")
|
||||||
TEST_CASE_FIXTURE(ESFixture, "Child & add<Child | AnotherChild | string, Parent>")
|
TEST_CASE_FIXTURE(ESFixture, "Child & add<Child | AnotherChild | string, Parent>")
|
||||||
{
|
{
|
||||||
const TypeId u = arena->addType(UnionType{{childClass, anotherChild, builtinTypes->stringType}});
|
const TypeId u = arena->addType(UnionType{{childClass, anotherChild, builtinTypes->stringType}});
|
||||||
const TypeId intersectTf = arena->addType(TypeFunctionInstanceType{
|
const TypeId intersectTf = arena->addType(TypeFunctionInstanceType{builtinTypeFunctions().addFunc, {u, parentClass}, {}});
|
||||||
builtinTypeFunctions().addFunc,
|
|
||||||
{u, parentClass},
|
|
||||||
{}
|
|
||||||
});
|
|
||||||
|
|
||||||
const TypeId intersection = arena->addType(IntersectionType{{childClass, intersectTf}});
|
const TypeId intersection = arena->addType(IntersectionType{{childClass, intersectTf}});
|
||||||
|
|
||||||
|
@ -720,11 +662,7 @@ TEST_CASE_FIXTURE(ESFixture, "Child & add<Child | AnotherChild | string, Parent>
|
||||||
TEST_CASE_FIXTURE(ESFixture, "Child & intersect<Child | AnotherChild | string, Parent>")
|
TEST_CASE_FIXTURE(ESFixture, "Child & intersect<Child | AnotherChild | string, Parent>")
|
||||||
{
|
{
|
||||||
const TypeId u = arena->addType(UnionType{{childClass, anotherChild, builtinTypes->stringType}});
|
const TypeId u = arena->addType(UnionType{{childClass, anotherChild, builtinTypes->stringType}});
|
||||||
const TypeId intersectTf = arena->addType(TypeFunctionInstanceType{
|
const TypeId intersectTf = arena->addType(TypeFunctionInstanceType{builtinTypeFunctions().intersectFunc, {u, parentClass}, {}});
|
||||||
builtinTypeFunctions().intersectFunc,
|
|
||||||
{u, parentClass},
|
|
||||||
{}
|
|
||||||
});
|
|
||||||
|
|
||||||
const TypeId intersection = arena->addType(IntersectionType{{childClass, intersectTf}});
|
const TypeId intersection = arena->addType(IntersectionType{{childClass, intersectTf}});
|
||||||
|
|
||||||
|
@ -740,7 +678,8 @@ TEST_CASE_FIXTURE(ESFixture, "lt<number, _> == boolean")
|
||||||
{arena->addType(BlockedType{}), builtinTypes->stringType},
|
{arena->addType(BlockedType{}), builtinTypes->stringType},
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const auto& [lhs, rhs] : cases) {
|
for (const auto& [lhs, rhs] : cases)
|
||||||
|
{
|
||||||
const TypeId tfun = arena->addType(TypeFunctionInstanceType{builtinTypeFunctions().ltFunc, {lhs, rhs}});
|
const TypeId tfun = arena->addType(TypeFunctionInstanceType{builtinTypeFunctions().ltFunc, {lhs, rhs}});
|
||||||
CHECK("boolean" == simplifyStr(tfun));
|
CHECK("boolean" == simplifyStr(tfun));
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,8 +29,7 @@ LUAU_FASTFLAG(DebugLuauFreezeArena)
|
||||||
LUAU_FASTFLAG(DebugLuauForceAllNewSolverTests)
|
LUAU_FASTFLAG(DebugLuauForceAllNewSolverTests)
|
||||||
LUAU_FASTFLAG(LuauVectorDefinitionsExtra)
|
LUAU_FASTFLAG(LuauVectorDefinitionsExtra)
|
||||||
|
|
||||||
#define DOES_NOT_PASS_NEW_SOLVER_GUARD_IMPL(line) \
|
#define DOES_NOT_PASS_NEW_SOLVER_GUARD_IMPL(line) ScopedFastFlag sff_##line{FFlag::LuauSolverV2, FFlag::DebugLuauForceAllNewSolverTests};
|
||||||
ScopedFastFlag sff_##line{FFlag::LuauSolverV2, FFlag::DebugLuauForceAllNewSolverTests};
|
|
||||||
|
|
||||||
#define DOES_NOT_PASS_NEW_SOLVER_GUARD() DOES_NOT_PASS_NEW_SOLVER_GUARD_IMPL(__LINE__)
|
#define DOES_NOT_PASS_NEW_SOLVER_GUARD() DOES_NOT_PASS_NEW_SOLVER_GUARD_IMPL(__LINE__)
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete)
|
||||||
LUAU_FASTFLAG(LuauSymbolEquality);
|
LUAU_FASTFLAG(LuauSymbolEquality);
|
||||||
LUAU_FASTFLAG(LuauStoreSolverTypeOnModule);
|
LUAU_FASTFLAG(LuauStoreSolverTypeOnModule);
|
||||||
LUAU_FASTFLAG(LexerResumesFromPosition2)
|
LUAU_FASTFLAG(LexerResumesFromPosition2)
|
||||||
|
LUAU_FASTFLAG(LuauIncrementalAutocompleteCommentDetection)
|
||||||
|
|
||||||
static std::optional<AutocompleteEntryMap> nullCallback(std::string tag, std::optional<const ClassType*> ptr, std::optional<std::string> contents)
|
static std::optional<AutocompleteEntryMap> nullCallback(std::string tag, std::optional<const ClassType*> ptr, std::optional<std::string> contents)
|
||||||
{
|
{
|
||||||
|
@ -91,7 +92,8 @@ struct FragmentAutocompleteFixtureImpl : BaseType
|
||||||
std::optional<Position> fragmentEndPosition = std::nullopt
|
std::optional<Position> fragmentEndPosition = std::nullopt
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return Luau::typecheckFragment(this->frontend, "MainModule", cursorPos, getOptions(), document, fragmentEndPosition);
|
auto [_, result] = Luau::typecheckFragment(this->frontend, "MainModule", cursorPos, getOptions(), document, fragmentEndPosition);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
FragmentAutocompleteResult autocompleteFragment(
|
FragmentAutocompleteResult autocompleteFragment(
|
||||||
|
@ -1383,4 +1385,177 @@ t
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "no_recs_for_comments_simple")
|
||||||
|
{
|
||||||
|
const std::string source = R"(
|
||||||
|
-- sel
|
||||||
|
-- retur
|
||||||
|
-- fo
|
||||||
|
-- if
|
||||||
|
-- end
|
||||||
|
-- the
|
||||||
|
)";
|
||||||
|
ScopedFastFlag sff{FFlag::LuauIncrementalAutocompleteCommentDetection, true};
|
||||||
|
autocompleteFragmentInBothSolvers(
|
||||||
|
source,
|
||||||
|
source,
|
||||||
|
Position{4, 6},
|
||||||
|
[](FragmentAutocompleteResult& result)
|
||||||
|
{
|
||||||
|
CHECK(result.acResults.entryMap.empty());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "no_recs_for_comments_blocks")
|
||||||
|
{
|
||||||
|
const std::string source = R"(
|
||||||
|
--[[
|
||||||
|
comment 1
|
||||||
|
]] local
|
||||||
|
-- [[ comment 2]]
|
||||||
|
--
|
||||||
|
-- sdfsdfsdf
|
||||||
|
--[[comment 3]]
|
||||||
|
--[[
|
||||||
|
foo
|
||||||
|
bar
|
||||||
|
baz
|
||||||
|
]]
|
||||||
|
)";
|
||||||
|
ScopedFastFlag sff{FFlag::LuauIncrementalAutocompleteCommentDetection, true};
|
||||||
|
autocompleteFragmentInBothSolvers(
|
||||||
|
source,
|
||||||
|
source,
|
||||||
|
Position{3, 0},
|
||||||
|
[](FragmentAutocompleteResult& result)
|
||||||
|
{
|
||||||
|
CHECK(result.acResults.entryMap.empty());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
autocompleteFragmentInBothSolvers(
|
||||||
|
source,
|
||||||
|
source,
|
||||||
|
Position{3, 2},
|
||||||
|
[](FragmentAutocompleteResult& result)
|
||||||
|
{
|
||||||
|
CHECK(!result.acResults.entryMap.empty());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
autocompleteFragmentInBothSolvers(
|
||||||
|
source,
|
||||||
|
source,
|
||||||
|
Position{8, 6},
|
||||||
|
[](FragmentAutocompleteResult& result)
|
||||||
|
{
|
||||||
|
CHECK(result.acResults.entryMap.empty());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
autocompleteFragmentInBothSolvers(
|
||||||
|
source,
|
||||||
|
source,
|
||||||
|
Position{10, 0},
|
||||||
|
[](FragmentAutocompleteResult& result)
|
||||||
|
{
|
||||||
|
CHECK(result.acResults.entryMap.empty());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "no_recs_for_comments")
|
||||||
|
{
|
||||||
|
const std::string source = R"(
|
||||||
|
-- sel
|
||||||
|
-- retur
|
||||||
|
-- fo
|
||||||
|
--[[ sel ]]
|
||||||
|
local -- hello
|
||||||
|
)";
|
||||||
|
ScopedFastFlag sff{FFlag::LuauIncrementalAutocompleteCommentDetection, true};
|
||||||
|
autocompleteFragmentInBothSolvers(
|
||||||
|
source,
|
||||||
|
source,
|
||||||
|
Position{1, 7},
|
||||||
|
[](FragmentAutocompleteResult& result)
|
||||||
|
{
|
||||||
|
CHECK(result.acResults.entryMap.empty());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
autocompleteFragmentInBothSolvers(
|
||||||
|
source,
|
||||||
|
source,
|
||||||
|
Position{2, 9},
|
||||||
|
[](FragmentAutocompleteResult& result)
|
||||||
|
{
|
||||||
|
CHECK(result.acResults.entryMap.empty());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
autocompleteFragmentInBothSolvers(
|
||||||
|
source,
|
||||||
|
source,
|
||||||
|
Position{3, 6},
|
||||||
|
[](FragmentAutocompleteResult& result)
|
||||||
|
{
|
||||||
|
CHECK(result.acResults.entryMap.empty());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
autocompleteFragmentInBothSolvers(
|
||||||
|
source,
|
||||||
|
source,
|
||||||
|
Position{4, 9},
|
||||||
|
[](FragmentAutocompleteResult& result)
|
||||||
|
{
|
||||||
|
CHECK(result.acResults.entryMap.empty());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
autocompleteFragmentInBothSolvers(
|
||||||
|
source,
|
||||||
|
source,
|
||||||
|
Position{5, 6},
|
||||||
|
[](FragmentAutocompleteResult& result)
|
||||||
|
{
|
||||||
|
CHECK(!result.acResults.entryMap.empty());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
autocompleteFragmentInBothSolvers(
|
||||||
|
source,
|
||||||
|
source,
|
||||||
|
Position{5, 14},
|
||||||
|
[](FragmentAutocompleteResult& result)
|
||||||
|
{
|
||||||
|
CHECK(result.acResults.entryMap.empty());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "no_recs_for_comments_in_incremental_fragment")
|
||||||
|
{
|
||||||
|
const std::string source = R"(
|
||||||
|
local x = 5
|
||||||
|
if x == 5
|
||||||
|
)";
|
||||||
|
const std::string updated = R"(
|
||||||
|
local x = 5
|
||||||
|
if x == 5 then -- a comment
|
||||||
|
)";
|
||||||
|
ScopedFastFlag sff{FFlag::LuauIncrementalAutocompleteCommentDetection, true};
|
||||||
|
autocompleteFragmentInBothSolvers(
|
||||||
|
source,
|
||||||
|
updated,
|
||||||
|
Position{2, 28},
|
||||||
|
[](FragmentAutocompleteResult& result)
|
||||||
|
{
|
||||||
|
CHECK(result.acResults.entryMap.empty());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -520,9 +520,9 @@ bb_bytecode_0:
|
||||||
JUMP bb_2
|
JUMP bb_2
|
||||||
bb_2:
|
bb_2:
|
||||||
CHECK_SAFE_ENV exit(3)
|
CHECK_SAFE_ENV exit(3)
|
||||||
JUMP_EQ_TAG K1, tnil, bb_fallback_4, bb_3
|
JUMP_EQ_TAG K1 (nil), tnil, bb_fallback_4, bb_3
|
||||||
bb_3:
|
bb_3:
|
||||||
%9 = LOAD_TVALUE K1
|
%9 = LOAD_TVALUE K1 (nil)
|
||||||
STORE_TVALUE R1, %9
|
STORE_TVALUE R1, %9
|
||||||
JUMP bb_5
|
JUMP bb_5
|
||||||
bb_5:
|
bb_5:
|
||||||
|
@ -575,7 +575,7 @@ bb_0:
|
||||||
bb_2:
|
bb_2:
|
||||||
JUMP bb_bytecode_1
|
JUMP bb_bytecode_1
|
||||||
bb_bytecode_1:
|
bb_bytecode_1:
|
||||||
%4 = LOAD_TVALUE K0, 0i, tvector
|
%4 = LOAD_TVALUE K0 (1, 2, 3), 0i, tvector
|
||||||
%11 = LOAD_TVALUE R0
|
%11 = LOAD_TVALUE R0
|
||||||
%12 = ADD_VEC %4, %11
|
%12 = ADD_VEC %4, %11
|
||||||
%13 = TAG_VECTOR %12
|
%13 = TAG_VECTOR %12
|
||||||
|
@ -602,7 +602,7 @@ bb_0:
|
||||||
bb_2:
|
bb_2:
|
||||||
JUMP bb_bytecode_1
|
JUMP bb_bytecode_1
|
||||||
bb_bytecode_1:
|
bb_bytecode_1:
|
||||||
FALLBACK_NAMECALL 0u, R1, R0, K0
|
FALLBACK_NAMECALL 0u, R1, R0, K0 ('Abs')
|
||||||
INTERRUPT 2u
|
INTERRUPT 2u
|
||||||
SET_SAVEDPC 3u
|
SET_SAVEDPC 3u
|
||||||
CALL R1, 1i, -1i
|
CALL R1, 1i, -1i
|
||||||
|
@ -628,8 +628,8 @@ bb_0:
|
||||||
bb_2:
|
bb_2:
|
||||||
JUMP bb_bytecode_1
|
JUMP bb_bytecode_1
|
||||||
bb_bytecode_1:
|
bb_bytecode_1:
|
||||||
FALLBACK_GETTABLEKS 0u, R3, R0, K0
|
FALLBACK_GETTABLEKS 0u, R3, R0, K0 ('XX')
|
||||||
FALLBACK_GETTABLEKS 2u, R4, R0, K1
|
FALLBACK_GETTABLEKS 2u, R4, R0, K1 ('YY')
|
||||||
CHECK_TAG R3, tnumber, bb_fallback_3
|
CHECK_TAG R3, tnumber, bb_fallback_3
|
||||||
CHECK_TAG R4, tnumber, bb_fallback_3
|
CHECK_TAG R4, tnumber, bb_fallback_3
|
||||||
%14 = LOAD_DOUBLE R3
|
%14 = LOAD_DOUBLE R3
|
||||||
|
@ -639,7 +639,7 @@ bb_bytecode_1:
|
||||||
JUMP bb_4
|
JUMP bb_4
|
||||||
bb_4:
|
bb_4:
|
||||||
CHECK_TAG R0, tvector, exit(5)
|
CHECK_TAG R0, tvector, exit(5)
|
||||||
FALLBACK_GETTABLEKS 5u, R3, R0, K2
|
FALLBACK_GETTABLEKS 5u, R3, R0, K2 ('ZZ')
|
||||||
CHECK_TAG R2, tnumber, bb_fallback_5
|
CHECK_TAG R2, tnumber, bb_fallback_5
|
||||||
CHECK_TAG R3, tnumber, bb_fallback_5
|
CHECK_TAG R3, tnumber, bb_fallback_5
|
||||||
%30 = LOAD_DOUBLE R2
|
%30 = LOAD_DOUBLE R2
|
||||||
|
@ -857,8 +857,8 @@ bb_2:
|
||||||
JUMP bb_bytecode_1
|
JUMP bb_bytecode_1
|
||||||
bb_bytecode_1:
|
bb_bytecode_1:
|
||||||
%8 = LOAD_POINTER R0
|
%8 = LOAD_POINTER R0
|
||||||
%9 = GET_SLOT_NODE_ADDR %8, 0u, K1
|
%9 = GET_SLOT_NODE_ADDR %8, 0u, K1 ('n')
|
||||||
CHECK_SLOT_MATCH %9, K1, bb_fallback_3
|
CHECK_SLOT_MATCH %9, K1 ('n'), bb_fallback_3
|
||||||
%11 = LOAD_TVALUE %9, 0i
|
%11 = LOAD_TVALUE %9, 0i
|
||||||
STORE_TVALUE R3, %11
|
STORE_TVALUE R3, %11
|
||||||
JUMP bb_4
|
JUMP bb_4
|
||||||
|
@ -885,8 +885,8 @@ bb_4:
|
||||||
STORE_VECTOR R3, %30, %33, %36
|
STORE_VECTOR R3, %30, %33, %36
|
||||||
CHECK_TAG R0, ttable, exit(6)
|
CHECK_TAG R0, ttable, exit(6)
|
||||||
%41 = LOAD_POINTER R0
|
%41 = LOAD_POINTER R0
|
||||||
%42 = GET_SLOT_NODE_ADDR %41, 6u, K3
|
%42 = GET_SLOT_NODE_ADDR %41, 6u, K3 ('b')
|
||||||
CHECK_SLOT_MATCH %42, K3, bb_fallback_5
|
CHECK_SLOT_MATCH %42, K3 ('b'), bb_fallback_5
|
||||||
%44 = LOAD_TVALUE %42, 0i
|
%44 = LOAD_TVALUE %42, 0i
|
||||||
STORE_TVALUE R5, %44
|
STORE_TVALUE R5, %44
|
||||||
JUMP bb_6
|
JUMP bb_6
|
||||||
|
@ -929,8 +929,8 @@ bb_0:
|
||||||
bb_2:
|
bb_2:
|
||||||
JUMP bb_bytecode_1
|
JUMP bb_bytecode_1
|
||||||
bb_bytecode_1:
|
bb_bytecode_1:
|
||||||
FALLBACK_GETTABLEKS 0u, R2, R0, K0
|
FALLBACK_GETTABLEKS 0u, R2, R0, K0 ('x')
|
||||||
FALLBACK_GETTABLEKS 2u, R3, R0, K1
|
FALLBACK_GETTABLEKS 2u, R3, R0, K1 ('y')
|
||||||
CHECK_TAG R2, tnumber, bb_fallback_3
|
CHECK_TAG R2, tnumber, bb_fallback_3
|
||||||
CHECK_TAG R3, tnumber, bb_fallback_3
|
CHECK_TAG R3, tnumber, bb_fallback_3
|
||||||
%14 = LOAD_DOUBLE R2
|
%14 = LOAD_DOUBLE R2
|
||||||
|
@ -964,9 +964,9 @@ bb_2:
|
||||||
bb_bytecode_1:
|
bb_bytecode_1:
|
||||||
STORE_DOUBLE R1, 3
|
STORE_DOUBLE R1, 3
|
||||||
STORE_TAG R1, tnumber
|
STORE_TAG R1, tnumber
|
||||||
FALLBACK_SETTABLEKS 1u, R1, R0, K0
|
FALLBACK_SETTABLEKS 1u, R1, R0, K0 ('x')
|
||||||
STORE_DOUBLE R1, 4
|
STORE_DOUBLE R1, 4
|
||||||
FALLBACK_SETTABLEKS 4u, R1, R0, K1
|
FALLBACK_SETTABLEKS 4u, R1, R0, K1 ('y')
|
||||||
INTERRUPT 6u
|
INTERRUPT 6u
|
||||||
RETURN R0, 0i
|
RETURN R0, 0i
|
||||||
)"
|
)"
|
||||||
|
@ -989,11 +989,11 @@ bb_0:
|
||||||
bb_2:
|
bb_2:
|
||||||
JUMP bb_bytecode_1
|
JUMP bb_bytecode_1
|
||||||
bb_bytecode_1:
|
bb_bytecode_1:
|
||||||
FALLBACK_NAMECALL 0u, R2, R0, K0
|
FALLBACK_NAMECALL 0u, R2, R0, K0 ('GetX')
|
||||||
INTERRUPT 2u
|
INTERRUPT 2u
|
||||||
SET_SAVEDPC 3u
|
SET_SAVEDPC 3u
|
||||||
CALL R2, 1i, 1i
|
CALL R2, 1i, 1i
|
||||||
FALLBACK_NAMECALL 3u, R3, R0, K1
|
FALLBACK_NAMECALL 3u, R3, R0, K1 ('GetY')
|
||||||
INTERRUPT 5u
|
INTERRUPT 5u
|
||||||
SET_SAVEDPC 6u
|
SET_SAVEDPC 6u
|
||||||
CALL R3, 1i, 1i
|
CALL R3, 1i, 1i
|
||||||
|
@ -1367,8 +1367,8 @@ bb_bytecode_1:
|
||||||
bb_4:
|
bb_4:
|
||||||
CHECK_TAG R2, ttable, exit(1)
|
CHECK_TAG R2, ttable, exit(1)
|
||||||
%23 = LOAD_POINTER R2
|
%23 = LOAD_POINTER R2
|
||||||
%24 = GET_SLOT_NODE_ADDR %23, 1u, K0
|
%24 = GET_SLOT_NODE_ADDR %23, 1u, K0 ('pos')
|
||||||
CHECK_SLOT_MATCH %24, K0, bb_fallback_5
|
CHECK_SLOT_MATCH %24, K0 ('pos'), bb_fallback_5
|
||||||
%26 = LOAD_TVALUE %24, 0i
|
%26 = LOAD_TVALUE %24, 0i
|
||||||
STORE_TVALUE R4, %26
|
STORE_TVALUE R4, %26
|
||||||
JUMP bb_6
|
JUMP bb_6
|
||||||
|
@ -1476,13 +1476,13 @@ bb_bytecode_1:
|
||||||
bb_4:
|
bb_4:
|
||||||
CHECK_TAG R3, ttable, bb_fallback_5
|
CHECK_TAG R3, ttable, bb_fallback_5
|
||||||
%23 = LOAD_POINTER R3
|
%23 = LOAD_POINTER R3
|
||||||
%24 = GET_SLOT_NODE_ADDR %23, 1u, K0
|
%24 = GET_SLOT_NODE_ADDR %23, 1u, K0 ('normal')
|
||||||
CHECK_SLOT_MATCH %24, K0, bb_fallback_5
|
CHECK_SLOT_MATCH %24, K0 ('normal'), bb_fallback_5
|
||||||
%26 = LOAD_TVALUE %24, 0i
|
%26 = LOAD_TVALUE %24, 0i
|
||||||
STORE_TVALUE R2, %26
|
STORE_TVALUE R2, %26
|
||||||
JUMP bb_6
|
JUMP bb_6
|
||||||
bb_6:
|
bb_6:
|
||||||
%31 = LOAD_TVALUE K1, 0i, tvector
|
%31 = LOAD_TVALUE K1 (0.707000017, 0, 0.707000017), 0i, tvector
|
||||||
STORE_TVALUE R4, %31
|
STORE_TVALUE R4, %31
|
||||||
CHECK_TAG R2, tvector, exit(4)
|
CHECK_TAG R2, tvector, exit(4)
|
||||||
%37 = LOAD_FLOAT R2, 0i
|
%37 = LOAD_FLOAT R2, 0i
|
||||||
|
@ -1603,9 +1603,9 @@ bb_bytecode_1:
|
||||||
STORE_DOUBLE R1, 0
|
STORE_DOUBLE R1, 0
|
||||||
STORE_TAG R1, tnumber
|
STORE_TAG R1, tnumber
|
||||||
CHECK_SAFE_ENV exit(1)
|
CHECK_SAFE_ENV exit(1)
|
||||||
JUMP_EQ_TAG K1, tnil, bb_fallback_6, bb_5
|
JUMP_EQ_TAG K1 (nil), tnil, bb_fallback_6, bb_5
|
||||||
bb_5:
|
bb_5:
|
||||||
%9 = LOAD_TVALUE K1
|
%9 = LOAD_TVALUE K1 (nil)
|
||||||
STORE_TVALUE R2, %9
|
STORE_TVALUE R2, %9
|
||||||
JUMP bb_7
|
JUMP bb_7
|
||||||
bb_7:
|
bb_7:
|
||||||
|
@ -1627,8 +1627,8 @@ bb_9:
|
||||||
bb_bytecode_2:
|
bb_bytecode_2:
|
||||||
CHECK_TAG R6, ttable, exit(6)
|
CHECK_TAG R6, ttable, exit(6)
|
||||||
%35 = LOAD_POINTER R6
|
%35 = LOAD_POINTER R6
|
||||||
%36 = GET_SLOT_NODE_ADDR %35, 6u, K2
|
%36 = GET_SLOT_NODE_ADDR %35, 6u, K2 ('pos')
|
||||||
CHECK_SLOT_MATCH %36, K2, bb_fallback_10
|
CHECK_SLOT_MATCH %36, K2 ('pos'), bb_fallback_10
|
||||||
%38 = LOAD_TVALUE %36, 0i
|
%38 = LOAD_TVALUE %36, 0i
|
||||||
STORE_TVALUE R8, %38
|
STORE_TVALUE R8, %38
|
||||||
JUMP bb_11
|
JUMP bb_11
|
||||||
|
@ -1829,8 +1829,8 @@ bb_0:
|
||||||
bb_2:
|
bb_2:
|
||||||
JUMP bb_bytecode_1
|
JUMP bb_bytecode_1
|
||||||
bb_bytecode_1:
|
bb_bytecode_1:
|
||||||
FALLBACK_GETTABLEKS 0u, R2, R0, K0
|
FALLBACK_GETTABLEKS 0u, R2, R0, K0 ('Row1')
|
||||||
FALLBACK_GETTABLEKS 2u, R3, R0, K1
|
FALLBACK_GETTABLEKS 2u, R3, R0, K1 ('Row2')
|
||||||
CHECK_TAG R2, tvector, exit(4)
|
CHECK_TAG R2, tvector, exit(4)
|
||||||
CHECK_TAG R3, tvector, exit(4)
|
CHECK_TAG R3, tvector, exit(4)
|
||||||
%14 = LOAD_TVALUE R2
|
%14 = LOAD_TVALUE R2
|
||||||
|
@ -2138,10 +2138,10 @@ bb_0:
|
||||||
bb_2:
|
bb_2:
|
||||||
JUMP bb_bytecode_1
|
JUMP bb_bytecode_1
|
||||||
bb_bytecode_1:
|
bb_bytecode_1:
|
||||||
%4 = LOAD_TVALUE K0, 0i, tvector
|
%4 = LOAD_TVALUE K0 (1, 0, 0), 0i, tvector
|
||||||
%11 = LOAD_TVALUE R0
|
%11 = LOAD_TVALUE R0
|
||||||
%12 = MUL_VEC %4, %11
|
%12 = MUL_VEC %4, %11
|
||||||
%15 = LOAD_TVALUE K1, 0i, tvector
|
%15 = LOAD_TVALUE K1 (0, 1, 0), 0i, tvector
|
||||||
%23 = ADD_VEC %12, %15
|
%23 = ADD_VEC %12, %15
|
||||||
%24 = TAG_VECTOR %23
|
%24 = TAG_VECTOR %23
|
||||||
STORE_TVALUE R1, %24
|
STORE_TVALUE R1, %24
|
||||||
|
@ -2176,7 +2176,7 @@ bb_0:
|
||||||
bb_2:
|
bb_2:
|
||||||
JUMP bb_bytecode_1
|
JUMP bb_bytecode_1
|
||||||
bb_bytecode_1:
|
bb_bytecode_1:
|
||||||
%4 = LOAD_TVALUE K0, 0i, tvector
|
%4 = LOAD_TVALUE K0 (0, 0, 0), 0i, tvector
|
||||||
%11 = LOAD_TVALUE R0
|
%11 = LOAD_TVALUE R0
|
||||||
%12 = ADD_VEC %4, %11
|
%12 = ADD_VEC %4, %11
|
||||||
%13 = TAG_VECTOR %12
|
%13 = TAG_VECTOR %12
|
||||||
|
@ -2208,9 +2208,9 @@ bb_bytecode_0:
|
||||||
STORE_TAG R1, tboolean
|
STORE_TAG R1, tboolean
|
||||||
STORE_DOUBLE R2, 4.75
|
STORE_DOUBLE R2, 4.75
|
||||||
STORE_TAG R2, tnumber
|
STORE_TAG R2, tnumber
|
||||||
%5 = LOAD_TVALUE K1, 0i, tvector
|
%5 = LOAD_TVALUE K1 (1, 2, 4), 0i, tvector
|
||||||
STORE_TVALUE R3, %5
|
STORE_TVALUE R3, %5
|
||||||
%7 = LOAD_TVALUE K2, 0i, tstring
|
%7 = LOAD_TVALUE K2 ('test'), 0i, tstring
|
||||||
STORE_TVALUE R4, %7
|
STORE_TVALUE R4, %7
|
||||||
INTERRUPT 5u
|
INTERRUPT 5u
|
||||||
RETURN R0, 5i
|
RETURN R0, 5i
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LexerFixInterpStringStart)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("LexerTests");
|
TEST_SUITE_BEGIN("LexerTests");
|
||||||
|
|
||||||
TEST_CASE("broken_string_works")
|
TEST_CASE("broken_string_works")
|
||||||
|
@ -153,6 +155,8 @@ TEST_CASE("string_interpolation_basic")
|
||||||
|
|
||||||
Lexeme interpEnd = lexer.next();
|
Lexeme interpEnd = lexer.next();
|
||||||
CHECK_EQ(interpEnd.type, Lexeme::InterpStringEnd);
|
CHECK_EQ(interpEnd.type, Lexeme::InterpStringEnd);
|
||||||
|
// The InterpStringEnd should start with }, not `.
|
||||||
|
CHECK_EQ(interpEnd.location.begin.column, FFlag::LexerFixInterpStringStart ? 11 : 12);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("string_interpolation_full")
|
TEST_CASE("string_interpolation_full")
|
||||||
|
@ -173,6 +177,7 @@ TEST_CASE("string_interpolation_full")
|
||||||
Lexeme interpMid = lexer.next();
|
Lexeme interpMid = lexer.next();
|
||||||
CHECK_EQ(interpMid.type, Lexeme::InterpStringMid);
|
CHECK_EQ(interpMid.type, Lexeme::InterpStringMid);
|
||||||
CHECK_EQ(interpMid.toString(), "} {");
|
CHECK_EQ(interpMid.toString(), "} {");
|
||||||
|
CHECK_EQ(interpMid.location.begin.column, FFlag::LexerFixInterpStringStart ? 11 : 12);
|
||||||
|
|
||||||
Lexeme quote2 = lexer.next();
|
Lexeme quote2 = lexer.next();
|
||||||
CHECK_EQ(quote2.type, Lexeme::QuotedString);
|
CHECK_EQ(quote2.type, Lexeme::QuotedString);
|
||||||
|
@ -181,6 +186,7 @@ TEST_CASE("string_interpolation_full")
|
||||||
Lexeme interpEnd = lexer.next();
|
Lexeme interpEnd = lexer.next();
|
||||||
CHECK_EQ(interpEnd.type, Lexeme::InterpStringEnd);
|
CHECK_EQ(interpEnd.type, Lexeme::InterpStringEnd);
|
||||||
CHECK_EQ(interpEnd.toString(), "} end`");
|
CHECK_EQ(interpEnd.toString(), "} end`");
|
||||||
|
CHECK_EQ(interpEnd.location.begin.column, FFlag::LexerFixInterpStringStart ? 19 : 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("string_interpolation_double_brace")
|
TEST_CASE("string_interpolation_double_brace")
|
||||||
|
|
|
@ -16,7 +16,6 @@ LUAU_FASTINT(LuauRecursionLimit)
|
||||||
LUAU_FASTINT(LuauTypeLengthLimit)
|
LUAU_FASTINT(LuauTypeLengthLimit)
|
||||||
LUAU_FASTINT(LuauParseErrorLimit)
|
LUAU_FASTINT(LuauParseErrorLimit)
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauUserDefinedTypeFunParseExport)
|
|
||||||
LUAU_FASTFLAG(LuauAllowComplexTypesInGenericParams)
|
LUAU_FASTFLAG(LuauAllowComplexTypesInGenericParams)
|
||||||
LUAU_FASTFLAG(LuauErrorRecoveryForTableTypes)
|
LUAU_FASTFLAG(LuauErrorRecoveryForTableTypes)
|
||||||
LUAU_FASTFLAG(LuauErrorRecoveryForClassNames)
|
LUAU_FASTFLAG(LuauErrorRecoveryForClassNames)
|
||||||
|
@ -447,38 +446,62 @@ TEST_CASE_FIXTURE(Fixture, "type_alias_span_is_correct")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "parse_error_messages")
|
TEST_CASE_FIXTURE(Fixture, "parse_error_messages")
|
||||||
{
|
{
|
||||||
matchParseError(R"(
|
matchParseError(
|
||||||
|
R"(
|
||||||
local a: (number, number) -> (string
|
local a: (number, number) -> (string
|
||||||
)", "Expected ')' (to close '(' at line 2), got <eof>");
|
)",
|
||||||
|
"Expected ')' (to close '(' at line 2), got <eof>"
|
||||||
|
);
|
||||||
|
|
||||||
matchParseError(R"(
|
matchParseError(
|
||||||
|
R"(
|
||||||
local a: (number, number) -> (
|
local a: (number, number) -> (
|
||||||
string
|
string
|
||||||
)", "Expected ')' (to close '(' at line 2), got <eof>");
|
)",
|
||||||
|
"Expected ')' (to close '(' at line 2), got <eof>"
|
||||||
|
);
|
||||||
|
|
||||||
matchParseError(R"(
|
matchParseError(
|
||||||
|
R"(
|
||||||
local a: (number, number)
|
local a: (number, number)
|
||||||
)", "Expected '->' when parsing function type, got <eof>");
|
)",
|
||||||
|
"Expected '->' when parsing function type, got <eof>"
|
||||||
|
);
|
||||||
|
|
||||||
matchParseError(R"(
|
matchParseError(
|
||||||
|
R"(
|
||||||
local a: (number, number
|
local a: (number, number
|
||||||
)", "Expected ')' (to close '(' at line 2), got <eof>");
|
)",
|
||||||
|
"Expected ')' (to close '(' at line 2), got <eof>"
|
||||||
|
);
|
||||||
|
|
||||||
matchParseError(R"(
|
matchParseError(
|
||||||
|
R"(
|
||||||
local a: {foo: string,
|
local a: {foo: string,
|
||||||
)", "Expected identifier when parsing table field, got <eof>");
|
)",
|
||||||
|
"Expected identifier when parsing table field, got <eof>"
|
||||||
|
);
|
||||||
|
|
||||||
matchParseError(R"(
|
matchParseError(
|
||||||
|
R"(
|
||||||
local a: {foo: string
|
local a: {foo: string
|
||||||
)", "Expected '}' (to close '{' at line 2), got <eof>");
|
)",
|
||||||
|
"Expected '}' (to close '{' at line 2), got <eof>"
|
||||||
|
);
|
||||||
|
|
||||||
matchParseError(R"(
|
matchParseError(
|
||||||
|
R"(
|
||||||
local a: { [string]: number, [number]: string }
|
local a: { [string]: number, [number]: string }
|
||||||
)", "Cannot have more than one table indexer");
|
)",
|
||||||
|
"Cannot have more than one table indexer"
|
||||||
|
);
|
||||||
|
|
||||||
matchParseError(R"(
|
matchParseError(
|
||||||
|
R"(
|
||||||
type T = <a>foo
|
type T = <a>foo
|
||||||
)", "Expected '(' when parsing function parameters, got 'foo'");
|
)",
|
||||||
|
"Expected '(' when parsing function parameters, got 'foo'"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "mixed_intersection_and_union_not_allowed")
|
TEST_CASE_FIXTURE(Fixture, "mixed_intersection_and_union_not_allowed")
|
||||||
|
@ -613,9 +636,12 @@ TEST_CASE_FIXTURE(Fixture, "vertical_space")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "parse_error_type_name")
|
TEST_CASE_FIXTURE(Fixture, "parse_error_type_name")
|
||||||
{
|
{
|
||||||
matchParseError(R"(
|
matchParseError(
|
||||||
|
R"(
|
||||||
local a: Foo.=
|
local a: Foo.=
|
||||||
)", "Expected identifier when parsing field name, got '='");
|
)",
|
||||||
|
"Expected identifier when parsing field name, got '='"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "parse_numbers_decimal")
|
TEST_CASE_FIXTURE(Fixture, "parse_numbers_decimal")
|
||||||
|
@ -677,9 +703,12 @@ TEST_CASE_FIXTURE(Fixture, "break_return_not_last_error")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "error_on_unicode")
|
TEST_CASE_FIXTURE(Fixture, "error_on_unicode")
|
||||||
{
|
{
|
||||||
matchParseError(R"(
|
matchParseError(
|
||||||
|
R"(
|
||||||
local ☃ = 10
|
local ☃ = 10
|
||||||
)", "Expected identifier when parsing variable name, got Unicode character U+2603");
|
)",
|
||||||
|
"Expected identifier when parsing variable name, got Unicode character U+2603"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "allow_unicode_in_string")
|
TEST_CASE_FIXTURE(Fixture, "allow_unicode_in_string")
|
||||||
|
@ -690,9 +719,12 @@ TEST_CASE_FIXTURE(Fixture, "allow_unicode_in_string")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "error_on_confusable")
|
TEST_CASE_FIXTURE(Fixture, "error_on_confusable")
|
||||||
{
|
{
|
||||||
matchParseError(R"(
|
matchParseError(
|
||||||
|
R"(
|
||||||
local pi = 3․13
|
local pi = 3․13
|
||||||
)", "Expected identifier when parsing expression, got Unicode character U+2024 (did you mean '.'?)");
|
)",
|
||||||
|
"Expected identifier when parsing expression, got Unicode character U+2024 (did you mean '.'?)"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "error_on_non_utf8_sequence")
|
TEST_CASE_FIXTURE(Fixture, "error_on_non_utf8_sequence")
|
||||||
|
@ -2341,8 +2373,6 @@ TEST_CASE_FIXTURE(Fixture, "invalid_type_forms")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "parse_user_defined_type_functions")
|
TEST_CASE_FIXTURE(Fixture, "parse_user_defined_type_functions")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff2{FFlag::LuauUserDefinedTypeFunParseExport, true};
|
|
||||||
|
|
||||||
AstStat* stat = parse(R"(
|
AstStat* stat = parse(R"(
|
||||||
type function foo()
|
type function foo()
|
||||||
return types.number
|
return types.number
|
||||||
|
@ -3702,11 +3732,14 @@ TEST_CASE_FIXTURE(Fixture, "recover_from_bad_table_type")
|
||||||
ScopedFastFlag _{FFlag::LuauErrorRecoveryForTableTypes, true};
|
ScopedFastFlag _{FFlag::LuauErrorRecoveryForTableTypes, true};
|
||||||
ParseOptions opts;
|
ParseOptions opts;
|
||||||
opts.allowDeclarationSyntax = true;
|
opts.allowDeclarationSyntax = true;
|
||||||
const auto result = tryParse(R"(
|
const auto result = tryParse(
|
||||||
|
R"(
|
||||||
declare class Widget
|
declare class Widget
|
||||||
state: {string: function(string, Widget)}
|
state: {string: function(string, Widget)}
|
||||||
end
|
end
|
||||||
)", opts);
|
)",
|
||||||
|
opts
|
||||||
|
);
|
||||||
CHECK_EQ(result.errors.size(), 2);
|
CHECK_EQ(result.errors.size(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,6 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauMathMap)
|
|
||||||
|
|
||||||
struct Completion
|
struct Completion
|
||||||
{
|
{
|
||||||
std::string completion;
|
std::string completion;
|
||||||
|
@ -175,7 +173,7 @@ TEST_CASE_FIXTURE(ReplFixture, "CompleteGlobalVariables")
|
||||||
CHECK(checkCompletion(completions, prefix, "myvariable1"));
|
CHECK(checkCompletion(completions, prefix, "myvariable1"));
|
||||||
CHECK(checkCompletion(completions, prefix, "myvariable2"));
|
CHECK(checkCompletion(completions, prefix, "myvariable2"));
|
||||||
}
|
}
|
||||||
if (FFlag::LuauMathMap)
|
|
||||||
{
|
{
|
||||||
// Try completing some builtin functions
|
// Try completing some builtin functions
|
||||||
CompletionSet completions = getCompletionSet("math.m");
|
CompletionSet completions = getCompletionSet("math.m");
|
||||||
|
|
|
@ -211,8 +211,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "exhaustive_toString_of_cyclic_table")
|
||||||
CHECK(
|
CHECK(
|
||||||
"t2 where "
|
"t2 where "
|
||||||
"t1 = { __index: t1, __mul: ((t2, number) -> t2) & ((t2, t2) -> t2), new: () -> t2 } ; "
|
"t1 = { __index: t1, __mul: ((t2, number) -> t2) & ((t2, t2) -> t2), new: () -> t2 } ; "
|
||||||
"t2 = { @metatable t1, { x: number, y: number, z: number } }" ==
|
"t2 = { @metatable t1, { x: number, y: number, z: number } }" == a
|
||||||
a
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -11,7 +11,6 @@ LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunFixNoReadWrite)
|
LUAU_FASTFLAG(LuauUserTypeFunFixNoReadWrite)
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunPrintToError)
|
LUAU_FASTFLAG(LuauUserTypeFunPrintToError)
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunExportedAndLocal)
|
LUAU_FASTFLAG(LuauUserTypeFunExportedAndLocal)
|
||||||
LUAU_FASTFLAG(LuauUserDefinedTypeFunParseExport)
|
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunThreadBuffer)
|
LUAU_FASTFLAG(LuauUserTypeFunThreadBuffer)
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunUpdateAllEnvs)
|
LUAU_FASTFLAG(LuauUserTypeFunUpdateAllEnvs)
|
||||||
|
|
||||||
|
@ -1309,7 +1308,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "explicit_export")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
ScopedFastFlag luauUserTypeFunExportedAndLocal{FFlag::LuauUserTypeFunExportedAndLocal, true};
|
ScopedFastFlag luauUserTypeFunExportedAndLocal{FFlag::LuauUserTypeFunExportedAndLocal, true};
|
||||||
ScopedFastFlag luauUserDefinedTypeFunParseExport{FFlag::LuauUserDefinedTypeFunParseExport, true};
|
|
||||||
|
|
||||||
fileResolver.source["game/A"] = R"(
|
fileResolver.source["game/A"] = R"(
|
||||||
export type function concat(a, b)
|
export type function concat(a, b)
|
||||||
|
|
|
@ -665,12 +665,11 @@ TEST_CASE_FIXTURE(ClassFixture, "indexable_classes")
|
||||||
)");
|
)");
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
CHECK(
|
CHECK("Type 'boolean' could not be converted into 'number | string'" == toString(result.errors.at(0)));
|
||||||
"Type 'boolean' could not be converted into 'number | string'" == toString(result.errors.at(0))
|
|
||||||
);
|
|
||||||
else
|
else
|
||||||
CHECK_EQ(
|
CHECK_EQ(
|
||||||
toString(result.errors.at(0)), "Type 'boolean' could not be converted into 'number | string'; none of the union options are compatible"
|
toString(result.errors.at(0)),
|
||||||
|
"Type 'boolean' could not be converted into 'number | string'; none of the union options are compatible"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
@ -680,12 +679,11 @@ TEST_CASE_FIXTURE(ClassFixture, "indexable_classes")
|
||||||
)");
|
)");
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
CHECK(
|
CHECK("Type 'boolean' could not be converted into 'number | string'" == toString(result.errors.at(0)));
|
||||||
"Type 'boolean' could not be converted into 'number | string'" == toString(result.errors.at(0))
|
|
||||||
);
|
|
||||||
else
|
else
|
||||||
CHECK_EQ(
|
CHECK_EQ(
|
||||||
toString(result.errors.at(0)), "Type 'boolean' could not be converted into 'number | string'; none of the union options are compatible"
|
toString(result.errors.at(0)),
|
||||||
|
"Type 'boolean' could not be converted into 'number | string'; none of the union options are compatible"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauNewSolverPrePopulateClasses)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("DefinitionTests");
|
TEST_SUITE_BEGIN("DefinitionTests");
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "definition_file_simple")
|
TEST_CASE_FIXTURE(Fixture, "definition_file_simple")
|
||||||
|
@ -492,11 +494,8 @@ TEST_CASE_FIXTURE(Fixture, "class_definition_indexer")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "class_definitions_reference_other_classes")
|
TEST_CASE_FIXTURE(Fixture, "class_definitions_reference_other_classes")
|
||||||
{
|
{
|
||||||
unfreeze(frontend.globals.globalTypes);
|
ScopedFastFlag _{FFlag::LuauNewSolverPrePopulateClasses, true};
|
||||||
LoadDefinitionFileResult result = frontend.loadDefinitionFile(
|
loadDefinition(R"(
|
||||||
frontend.globals,
|
|
||||||
frontend.globals.globalScope,
|
|
||||||
R"(
|
|
||||||
declare class Channel
|
declare class Channel
|
||||||
Messages: { Message }
|
Messages: { Message }
|
||||||
OnMessage: (message: Message) -> ()
|
OnMessage: (message: Message) -> ()
|
||||||
|
@ -506,13 +505,19 @@ TEST_CASE_FIXTURE(Fixture, "class_definitions_reference_other_classes")
|
||||||
Text: string
|
Text: string
|
||||||
Channel: Channel
|
Channel: Channel
|
||||||
end
|
end
|
||||||
)",
|
)");
|
||||||
"@test",
|
|
||||||
/* captureComments */ false
|
|
||||||
);
|
|
||||||
freeze(frontend.globals.globalTypes);
|
|
||||||
|
|
||||||
REQUIRE(result.success);
|
CheckResult result = check(R"(
|
||||||
|
local a: Channel
|
||||||
|
local b = a.Messages[1]
|
||||||
|
local c = b.Channel
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
|
CHECK_EQ(toString(requireType("a")), "Channel");
|
||||||
|
CHECK_EQ(toString(requireType("b")), "Message");
|
||||||
|
CHECK_EQ(toString(requireType("c")), "Channel");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "definition_file_has_source_module_name_set")
|
TEST_CASE_FIXTURE(Fixture, "definition_file_has_source_module_name_set")
|
||||||
|
|
|
@ -2566,10 +2566,7 @@ end
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "tf_suggest_return_type")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "tf_suggest_return_type")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauDontRefCountTypesInTypeFunctions, true}};
|
||||||
{FFlag::LuauSolverV2, true},
|
|
||||||
{FFlag::LuauDontRefCountTypesInTypeFunctions, true}
|
|
||||||
};
|
|
||||||
|
|
||||||
// CLI-114134: This test:
|
// CLI-114134: This test:
|
||||||
// a) Has a kind of weird result (suggesting `number | false` is not great);
|
// a) Has a kind of weird result (suggesting `number | false` is not great);
|
||||||
|
|
|
@ -800,7 +800,10 @@ TEST_CASE_FIXTURE(Fixture, "strict_binary_op_where_lhs_unknown")
|
||||||
"Operator '+' could not be applied to operands of types unknown and unknown; there is no corresponding overload for __add",
|
"Operator '+' could not be applied to operands of types unknown and unknown; there is no corresponding overload for __add",
|
||||||
toString(result.errors[0])
|
toString(result.errors[0])
|
||||||
);
|
);
|
||||||
CHECK_EQ("Operator '-' could not be applied to operands of types unknown and unknown; there is no corresponding overload for __sub", toString(result.errors[1]));
|
CHECK_EQ(
|
||||||
|
"Operator '-' could not be applied to operands of types unknown and unknown; there is no corresponding overload for __sub",
|
||||||
|
toString(result.errors[1])
|
||||||
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,8 +12,6 @@
|
||||||
|
|
||||||
#include "doctest.h"
|
#include "doctest.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauVectorDefinitions)
|
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TypeInferPrimitives");
|
TEST_SUITE_BEGIN("TypeInferPrimitives");
|
||||||
|
|
|
@ -69,9 +69,9 @@ TEST_CASE_FIXTURE(Fixture, "typeguard_inference_incomplete")
|
||||||
const std::string expectedWithEqSat = R"(
|
const std::string expectedWithEqSat = R"(
|
||||||
function f(a:{fn:()->(unknown,...unknown)}): ()
|
function f(a:{fn:()->(unknown,...unknown)}): ()
|
||||||
if type(a) == 'boolean'then
|
if type(a) == 'boolean'then
|
||||||
local a1:never=a
|
local a1:{fn:()->(unknown,...unknown)}&boolean=a
|
||||||
elseif a.fn()then
|
elseif a.fn()then
|
||||||
local a2:{fn:()->(unknown,...unknown)}=a
|
local a2:{fn:()->(unknown,...unknown)}&negate<boolean>=a
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
)";
|
)";
|
||||||
|
|
|
@ -743,10 +743,14 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "nonoptional_type_can_narrow_to_nil_if_sense_
|
||||||
{
|
{
|
||||||
// CLI-115281 Types produced by refinements do not consistently get simplified
|
// CLI-115281 Types produced by refinements do not consistently get simplified
|
||||||
CHECK_EQ("(nil & string)?", toString(requireTypeAtPosition({4, 24}))); // type(v) == "nil"
|
CHECK_EQ("(nil & string)?", toString(requireTypeAtPosition({4, 24}))); // type(v) == "nil"
|
||||||
CHECK_EQ("(boolean | buffer | class | function | number | string | table | thread) & string", toString(requireTypeAtPosition({6, 24}))); // type(v) ~= "nil"
|
CHECK_EQ(
|
||||||
|
"(boolean | buffer | class | function | number | string | table | thread) & string", toString(requireTypeAtPosition({6, 24}))
|
||||||
|
); // type(v) ~= "nil"
|
||||||
|
|
||||||
CHECK_EQ("(nil & string)?", toString(requireTypeAtPosition({10, 24}))); // equivalent to type(v) == "nil"
|
CHECK_EQ("(nil & string)?", toString(requireTypeAtPosition({10, 24}))); // equivalent to type(v) == "nil"
|
||||||
CHECK_EQ("(boolean | buffer | class | function | number | string | table | thread) & string", toString(requireTypeAtPosition({12, 24}))); // equivalent to type(v) ~= "nil"
|
CHECK_EQ(
|
||||||
|
"(boolean | buffer | class | function | number | string | table | thread) & string", toString(requireTypeAtPosition({12, 24}))
|
||||||
|
); // equivalent to type(v) ~= "nil"
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,6 +21,7 @@ LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering)
|
||||||
LUAU_FASTFLAG(LuauRetrySubtypingWithoutHiddenPack)
|
LUAU_FASTFLAG(LuauRetrySubtypingWithoutHiddenPack)
|
||||||
LUAU_FASTFLAG(LuauTableKeysAreRValues)
|
LUAU_FASTFLAG(LuauTableKeysAreRValues)
|
||||||
LUAU_FASTFLAG(LuauAllowNilAssignmentToIndexer)
|
LUAU_FASTFLAG(LuauAllowNilAssignmentToIndexer)
|
||||||
|
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TableTests");
|
TEST_SUITE_BEGIN("TableTests");
|
||||||
|
|
||||||
|
@ -3815,6 +3816,8 @@ TEST_CASE_FIXTURE(Fixture, "a_free_shape_can_turn_into_a_scalar_if_it_is_compati
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "a_free_shape_cannot_turn_into_a_scalar_if_it_is_not_compatible")
|
TEST_CASE_FIXTURE(Fixture, "a_free_shape_cannot_turn_into_a_scalar_if_it_is_not_compatible")
|
||||||
{
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauTrackInteriorFreeTypesOnScope, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function f(s): string
|
local function f(s): string
|
||||||
local foo = s:absolutely_no_scalar_has_this_method()
|
local foo = s:absolutely_no_scalar_has_this_method()
|
||||||
|
@ -3824,17 +3827,14 @@ TEST_CASE_FIXTURE(Fixture, "a_free_shape_cannot_turn_into_a_scalar_if_it_is_not_
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
{
|
{
|
||||||
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
LUAU_REQUIRE_ERROR_COUNT(3, result);
|
||||||
|
|
||||||
CHECK(toString(result.errors[0]) == "Parameter 's' has been reduced to never. This function is not callable with any possible value.");
|
CHECK(toString(result.errors[0]) == "Parameter 's' has been reduced to never. This function is not callable with any possible value.");
|
||||||
// FIXME: These free types should have been generalized by now.
|
|
||||||
CHECK(
|
CHECK(
|
||||||
toString(result.errors[1]) ==
|
toString(result.errors[1]) ==
|
||||||
"Parameter 's' is required to be a subtype of '{- read absolutely_no_scalar_has_this_method: ('a <: (never) -> ('b, c...)) -}' here."
|
"Parameter 's' is required to be a subtype of '{- read absolutely_no_scalar_has_this_method: (never) -> (unknown, ...unknown) -}' here."
|
||||||
);
|
);
|
||||||
CHECK(toString(result.errors[2]) == "Parameter 's' is required to be a subtype of 'string' here.");
|
CHECK(toString(result.errors[2]) == "Parameter 's' is required to be a subtype of 'string' here.");
|
||||||
CHECK(get<CannotCallNonFunction>(result.errors[3]));
|
|
||||||
|
|
||||||
CHECK_EQ("(never) -> string", toString(requireType("f")));
|
CHECK_EQ("(never) -> string", toString(requireType("f")));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -5002,7 +5002,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "metatable_union_type")
|
||||||
)");
|
)");
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
CHECK_EQ(
|
CHECK_EQ(
|
||||||
"Cannot add indexer to table '{ @metatable t1, (nil & ~(false?)) | { } } where t1 = { new: <a>(a) -> { @metatable t1, (a & ~(false?)) | { } } }'",
|
"Cannot add indexer to table '{ @metatable t1, (nil & ~(false?)) | { } } where t1 = { new: <a>(a) -> { @metatable t1, (a & ~(false?)) | { "
|
||||||
|
"} } }'",
|
||||||
toString(result.errors[0])
|
toString(result.errors[0])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1709,10 +1709,7 @@ TEST_CASE_FIXTURE(Fixture, "react_lua_follow_free_type_ub")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "visit_error_nodes_in_lvalue")
|
TEST_CASE_FIXTURE(Fixture, "visit_error_nodes_in_lvalue")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauNewSolverVisitErrorExprLvalues, true}};
|
||||||
{FFlag::LuauSolverV2, true},
|
|
||||||
{FFlag::LuauNewSolverVisitErrorExprLvalues, true}
|
|
||||||
};
|
|
||||||
|
|
||||||
// This should always fail to parse, but shouldn't assert. Previously this
|
// This should always fail to parse, but shouldn't assert. Previously this
|
||||||
// would assert as we end up _roughly_ parsing this (with a lot of error
|
// would assert as we end up _roughly_ parsing this (with a lot of error
|
||||||
|
@ -1734,10 +1731,7 @@ TEST_CASE_FIXTURE(Fixture, "visit_error_nodes_in_lvalue")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "avoid_blocking_type_function")
|
TEST_CASE_FIXTURE(Fixture, "avoid_blocking_type_function")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauDontRefCountTypesInTypeFunctions, true}};
|
||||||
{FFlag::LuauSolverV2, true},
|
|
||||||
{FFlag::LuauDontRefCountTypesInTypeFunctions, true}
|
|
||||||
};
|
|
||||||
|
|
||||||
LUAU_CHECK_NO_ERRORS(check(R"(
|
LUAU_CHECK_NO_ERRORS(check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
|
@ -1750,10 +1744,7 @@ TEST_CASE_FIXTURE(Fixture, "avoid_blocking_type_function")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "avoid_double_reference_to_free_type")
|
TEST_CASE_FIXTURE(Fixture, "avoid_double_reference_to_free_type")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauDontRefCountTypesInTypeFunctions, true}};
|
||||||
{FFlag::LuauSolverV2, true},
|
|
||||||
{FFlag::LuauDontRefCountTypesInTypeFunctions, true}
|
|
||||||
};
|
|
||||||
|
|
||||||
LUAU_CHECK_NO_ERRORS(check(R"(
|
LUAU_CHECK_NO_ERRORS(check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
|
|
|
@ -953,8 +953,7 @@ a = b
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
{
|
{
|
||||||
const std::string expected =
|
const std::string expected = "Type\n"
|
||||||
"Type\n"
|
|
||||||
" '() -> (number, ...boolean)'\n"
|
" '() -> (number, ...boolean)'\n"
|
||||||
"could not be converted into\n"
|
"could not be converted into\n"
|
||||||
" '() -> (number, ...string)'; at returns().tail().variadic(), boolean is not a subtype of string";
|
" '() -> (number, ...string)'; at returns().tail().variadic(), boolean is not a subtype of string";
|
||||||
|
|
|
@ -881,7 +881,9 @@ TEST_CASE_FIXTURE(Fixture, "less_greedy_unification_with_union_types")
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
CHECK_EQ("(({ read x: unknown } & { x: number }) | ({ read x: unknown } & { x: string })) -> { x: number } | { x: string }", toString(requireType("f")));
|
CHECK_EQ(
|
||||||
|
"(({ read x: unknown } & { x: number }) | ({ read x: unknown } & { x: string })) -> { x: number } | { x: string }", toString(requireType("f"))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "less_greedy_unification_with_union_types_2")
|
TEST_CASE_FIXTURE(Fixture, "less_greedy_unification_with_union_types_2")
|
||||||
|
|
|
@ -599,6 +599,90 @@ end
|
||||||
|
|
||||||
misc(table.create(16, 0))
|
misc(table.create(16, 0))
|
||||||
|
|
||||||
|
local function bitops(size, base)
|
||||||
|
local b = buffer.create(size)
|
||||||
|
|
||||||
|
buffer.writeu32(b, base / 8, 0x12345678)
|
||||||
|
|
||||||
|
assert(buffer.readbits(b, base, 8) == buffer.readu8(b, base / 8))
|
||||||
|
assert(buffer.readbits(b, base, 16) == buffer.readu16(b, base / 8))
|
||||||
|
assert(buffer.readbits(b, base, 32) == buffer.readu32(b, base / 8))
|
||||||
|
|
||||||
|
buffer.writebits(b, base, 32, 0)
|
||||||
|
|
||||||
|
buffer.writebits(b, base, 1, 1)
|
||||||
|
assert(buffer.readi8(b, base / 8) == 1)
|
||||||
|
|
||||||
|
buffer.writebits(b, base + 1, 1, 1)
|
||||||
|
assert(buffer.readi8(b, base / 8) == 3)
|
||||||
|
|
||||||
|
-- construct 00000010 00000000_01000000_00010000_00001000 00001000_00010000_01000010_00100101
|
||||||
|
buffer.writebits(b, base + 0, 1, 0b1)
|
||||||
|
buffer.writebits(b, base + 1, 2, 0b10)
|
||||||
|
buffer.writebits(b, base + 3, 3, 0b100)
|
||||||
|
buffer.writebits(b, base + 6, 4, 0b1000)
|
||||||
|
buffer.writebits(b, base + 10, 5, 0b10000)
|
||||||
|
buffer.writebits(b, base + 15, 6, 0b100000)
|
||||||
|
buffer.writebits(b, base + 21, 7, 0b1000000)
|
||||||
|
buffer.writebits(b, base + 28, 8, 0b10000000)
|
||||||
|
buffer.writebits(b, base + 36, 9, 0b100000000)
|
||||||
|
buffer.writebits(b, base + 45, 10, 0b1000000000)
|
||||||
|
buffer.writebits(b, base + 55, 11, 0b10000000000)
|
||||||
|
|
||||||
|
assert(buffer.readbits(b, base + 0, 32) == 0b00001000_00010000_01000010_00100101)
|
||||||
|
assert(buffer.readbits(b, base + 32, 32) == 0b00000000_01000000_00010000_00001000)
|
||||||
|
|
||||||
|
assert(buffer.readu32(b, base / 8 + 0) == 0b00001000_00010000_01000010_00100101)
|
||||||
|
assert(buffer.readu32(b, base / 8 + 4) == 0b00000000_01000000_00010000_00001000)
|
||||||
|
|
||||||
|
-- slide the window to touch 5 bytes
|
||||||
|
assert(buffer.readbits(b, base + 1, 32) == 0b00000100000010000010000100010010)
|
||||||
|
assert(buffer.readbits(b, base + 2, 32) == 0b00000010000001000001000010001001)
|
||||||
|
assert(buffer.readbits(b, base + 3, 32) == 0b00000001000000100000100001000100)
|
||||||
|
assert(buffer.readbits(b, base + 4, 32) == 0b10000000100000010000010000100010)
|
||||||
|
assert(buffer.readbits(b, base + 5, 32) == 0b01000000010000001000001000010001)
|
||||||
|
assert(buffer.readbits(b, base + 6, 32) == 0b00100000001000000100000100001000)
|
||||||
|
assert(buffer.readbits(b, base + 7, 32) == 0b00010000000100000010000010000100)
|
||||||
|
assert(buffer.readbits(b, base + 8, 32) == 0b00001000000010000001000001000010)
|
||||||
|
|
||||||
|
assert(buffer.readbits(b, base + 1, 15) == 0b010000100010010)
|
||||||
|
assert(buffer.readbits(b, base + 2, 15) == 0b001000010001001)
|
||||||
|
assert(buffer.readbits(b, base + 3, 15) == 0b000100001000100)
|
||||||
|
assert(buffer.readbits(b, base + 4, 15) == 0b000010000100010)
|
||||||
|
assert(buffer.readbits(b, base + 5, 15) == 0b000001000010001)
|
||||||
|
assert(buffer.readbits(b, base + 6, 15) == 0b100000100001000)
|
||||||
|
assert(buffer.readbits(b, base + 7, 15) == 0b010000010000100)
|
||||||
|
assert(buffer.readbits(b, base + 8, 15) == 0b001000001000010)
|
||||||
|
|
||||||
|
-- zero bit
|
||||||
|
buffer.writebits(b, base, 0, 0b1)
|
||||||
|
assert(buffer.readbits(b, base, 32) == 0b00001000_00010000_01000010_00100101)
|
||||||
|
assert(buffer.readbits(b, base, 0) == 0)
|
||||||
|
assert(buffer.readbits(b, size * 8, 0) == 0)
|
||||||
|
|
||||||
|
-- bounds
|
||||||
|
assert(ecall(function() buffer.readbits(b, -1, 0) end) == "buffer access out of bounds")
|
||||||
|
assert(ecall(function() buffer.readbits(b, size * 8, 1) end) == "buffer access out of bounds")
|
||||||
|
assert(ecall(function() buffer.readbits(b, size * 8 - 1, 2) end) == "buffer access out of bounds")
|
||||||
|
assert(ecall(function() buffer.readbits(b, 0, 64) end) == "bit count is out of range of [0; 32]")
|
||||||
|
|
||||||
|
assert(ecall(function() buffer.writebits(b, -1, 0, 1) end) == "buffer access out of bounds")
|
||||||
|
assert(ecall(function() buffer.writebits(b, size * 8, 1, 1) end) == "buffer access out of bounds")
|
||||||
|
assert(ecall(function() buffer.writebits(b, size * 8 - 1, 2, 1) end) == "buffer access out of bounds")
|
||||||
|
assert(ecall(function() buffer.writebits(b, 0, 64, 1) end) == "bit count is out of range of [0; 32]")
|
||||||
|
|
||||||
|
|
||||||
|
return b
|
||||||
|
end
|
||||||
|
|
||||||
|
do
|
||||||
|
bitops(16, 0)
|
||||||
|
bitops(17, 8)
|
||||||
|
|
||||||
|
-- a very large buffer and bit offsets can now be over 32 bits
|
||||||
|
bitops(1024 * 1024 * 1024, 6 * 1024 * 1024 * 1024)
|
||||||
|
end
|
||||||
|
|
||||||
local function testslowcalls()
|
local function testslowcalls()
|
||||||
getfenv()
|
getfenv()
|
||||||
|
|
||||||
|
@ -619,6 +703,7 @@ local function testslowcalls()
|
||||||
fromtostring()
|
fromtostring()
|
||||||
fill()
|
fill()
|
||||||
misc(table.create(16, 0))
|
misc(table.create(16, 0))
|
||||||
|
bitops(16, 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
testslowcalls()
|
testslowcalls()
|
||||||
|
|
|
@ -237,7 +237,7 @@ if not limitedstack then
|
||||||
end
|
end
|
||||||
|
|
||||||
-- testing deep nested calls with a large thread stack
|
-- testing deep nested calls with a large thread stack
|
||||||
do
|
if not limitedstack then
|
||||||
function recurse(n, ...) return n <= 1 and (1 + #{...}) or recurse(n-1, table.unpack(table.create(4000, 1))) + 1 end
|
function recurse(n, ...) return n <= 1 and (1 + #{...}) or recurse(n-1, table.unpack(table.create(4000, 1))) + 1 end
|
||||||
|
|
||||||
local ok, msg = pcall(recurse, 19000)
|
local ok, msg = pcall(recurse, 19000)
|
||||||
|
|
|
@ -402,6 +402,22 @@ assert(math.map(4, 4, 1, 2, 0) == 2)
|
||||||
assert(math.map(-8, 0, 4, 0, 2) == -4)
|
assert(math.map(-8, 0, 4, 0, 2) == -4)
|
||||||
assert(math.map(16, 0, 4, 0, 2) == 8)
|
assert(math.map(16, 0, 4, 0, 2) == 8)
|
||||||
|
|
||||||
|
-- lerp basics
|
||||||
|
assert(math.lerp(1, 5, 0) == 1)
|
||||||
|
assert(math.lerp(1, 5, 1) == 5)
|
||||||
|
assert(math.lerp(1, 5, 0.5) == 3)
|
||||||
|
assert(math.lerp(1, 5, 1.5) == 7)
|
||||||
|
assert(math.lerp(1, 5, -0.5) == -1)
|
||||||
|
|
||||||
|
-- lerp properties
|
||||||
|
local sq2, sq3 = math.sqrt(2), math.sqrt(3)
|
||||||
|
assert(math.lerp(sq2, sq3, 0) == sq2) -- exact at 0
|
||||||
|
assert(math.lerp(sq2, sq3, 1) == sq3) -- exact at 1
|
||||||
|
assert(math.lerp(-sq3, sq2, 1) == sq2) -- exact at 1 (fails for a + t*(b-a))
|
||||||
|
assert(math.lerp(sq2, sq2, sq2 / 2) <= math.lerp(sq2, sq2, 1)) -- monotonic (fails for a*t + b*(1-t))
|
||||||
|
assert(math.lerp(-sq3, sq2, 1) <= math.sqrt(2)) -- bounded (fails for a + t*(b-a))
|
||||||
|
assert(math.lerp(sq2, sq2, sq2 / 2) == sq2) -- consistent (fails for a*t + b*(1-t))
|
||||||
|
|
||||||
assert(tostring(math.pow(-2, 0.5)) == "nan")
|
assert(tostring(math.pow(-2, 0.5)) == "nan")
|
||||||
|
|
||||||
-- test that fastcalls return correct number of results
|
-- test that fastcalls return correct number of results
|
||||||
|
@ -464,5 +480,6 @@ assert(math.sign("2") == 1)
|
||||||
assert(math.sign("-2") == -1)
|
assert(math.sign("-2") == -1)
|
||||||
assert(math.sign("0") == 0)
|
assert(math.sign("0") == 0)
|
||||||
assert(math.round("1.8") == 2)
|
assert(math.round("1.8") == 2)
|
||||||
|
assert(math.lerp("1", "5", 0.5) == 3)
|
||||||
|
|
||||||
return('OK')
|
return('OK')
|
||||||
|
|
|
@ -513,4 +513,68 @@ end
|
||||||
assert(extramath3(2) == "number")
|
assert(extramath3(2) == "number")
|
||||||
assert(extramath3("2") == "number")
|
assert(extramath3("2") == "number")
|
||||||
|
|
||||||
|
local function slotcachelimit1()
|
||||||
|
local tbl = {
|
||||||
|
f1 = function() return 1 end,
|
||||||
|
f2 = function() return 2 end,
|
||||||
|
f3 = function() return 3 end,
|
||||||
|
f4 = function() return 4 end,
|
||||||
|
f5 = function() return 5 end,
|
||||||
|
f6 = function() return 6 end,
|
||||||
|
f7 = function() return 7 end,
|
||||||
|
f8 = function() return 8 end,
|
||||||
|
f9 = function() return 9 end,
|
||||||
|
f10 = function() return 10 end,
|
||||||
|
f11 = function() return 11 end,
|
||||||
|
f12 = function() return 12 end,
|
||||||
|
f13 = function() return 13 end,
|
||||||
|
f14 = function() return 14 end,
|
||||||
|
f15 = function() return 15 end,
|
||||||
|
f16 = function() return 16 end,
|
||||||
|
}
|
||||||
|
|
||||||
|
local lookup = {
|
||||||
|
[tbl.f1] = 1,
|
||||||
|
[tbl.f2] = 2,
|
||||||
|
[tbl.f3] = 3,
|
||||||
|
[tbl.f4] = 4,
|
||||||
|
[tbl.f5] = 5,
|
||||||
|
[tbl.f6] = 6,
|
||||||
|
[tbl.f7] = 7,
|
||||||
|
[tbl.f8] = 8,
|
||||||
|
[tbl.f9] = 9,
|
||||||
|
[tbl.f10] = 10,
|
||||||
|
[tbl.f11] = 11,
|
||||||
|
[tbl.f12] = 12,
|
||||||
|
[tbl.f13] = 13,
|
||||||
|
[tbl.f14] = 14,
|
||||||
|
[tbl.f15] = 15,
|
||||||
|
[tbl.f16] = 16,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(is_native())
|
||||||
|
|
||||||
|
return lookup
|
||||||
|
end
|
||||||
|
|
||||||
|
slotcachelimit1()
|
||||||
|
|
||||||
|
local function slotcachelimit2(foo, size)
|
||||||
|
local c1 = foo(vector.create(size.X, size.Y, size.Z))
|
||||||
|
local c2 = foo(vector.create(-size.X, size.Y, size.Z))
|
||||||
|
local c3 = foo(vector.create(-size.X, -size.Y, size.Z))
|
||||||
|
local c4 = foo(vector.create(-size.X, -size.Y, -size.Z))
|
||||||
|
local c5 = foo(vector.create(size.X, -size.Y, -size.Z))
|
||||||
|
local c6 = foo(vector.create(size.X, size.Y, -size.Z))
|
||||||
|
local c7 = foo(vector.create(size.X, -size.Y, size.Z))
|
||||||
|
local c8 = foo(vector.create(-size.X, size.Y, -size.Z))
|
||||||
|
local max = vector.create(math.max(c1.X, c2.X, c3.X, c4.X, c5.X, c6.X, c7.X, c8.X), math.max(c1.Y, c2.Y, c3.Y, c4.Y, c5.Y, c6.Y, c7.Y, c8.Y), math.max(c1.Z, c2.Z, c3.Z, c4.Z, c5.Z, c6.Z, c7.Z, c8.Z))
|
||||||
|
local min = vector.create(math.min(c1.X, c2.X, c3.X, c4.X, c5.X, c6.X, c7.X, c8.X), math.min(c1.Y, c2.Y, c3.Y, c4.Y, c5.Y, c6.Y, c7.Y, c8.Y), math.min(c1.Z, c2.Z, c3.Z, c4.Z, c5.Z, c6.Z, c7.Z, c8.Z))
|
||||||
|
|
||||||
|
assert(is_native())
|
||||||
|
return max - min
|
||||||
|
end
|
||||||
|
|
||||||
|
slotcachelimit2(function(a) return -a end, vector.create(1, 2, 3))
|
||||||
|
|
||||||
return('OK')
|
return('OK')
|
||||||
|
|
Loading…
Reference in a new issue