Sync to upstream/release/514 (#357)
This commit is contained in:
parent
aecd60371b
commit
63d5423bbb
|
@ -86,6 +86,8 @@ struct OwningAutocompleteResult
|
|||
};
|
||||
|
||||
AutocompleteResult autocomplete(Frontend& frontend, const ModuleName& moduleName, Position position, StringCompletionCallback callback);
|
||||
|
||||
// Deprecated, do not use in new work.
|
||||
OwningAutocompleteResult autocompleteSource(Frontend& frontend, std::string_view source, Position position, StringCompletionCallback callback);
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -49,6 +49,7 @@ struct LintWarning
|
|||
Code_DeprecatedApi = 22,
|
||||
Code_TableOperations = 23,
|
||||
Code_DuplicateCondition = 24,
|
||||
Code_MisleadingAndOr = 25,
|
||||
|
||||
Code__Count
|
||||
};
|
||||
|
|
|
@ -93,7 +93,7 @@ struct Tarjan
|
|||
|
||||
// This should never be null; ensure you initialize it before calling
|
||||
// substitution methods.
|
||||
const TxnLog* log;
|
||||
const TxnLog* log = nullptr;
|
||||
|
||||
std::vector<TypeId> edgesTy;
|
||||
std::vector<TypePackId> edgesTp;
|
||||
|
|
|
@ -307,8 +307,8 @@ private:
|
|||
//
|
||||
// We can't use a DenseHashMap here because we need a non-const iterator
|
||||
// over the map when we concatenate.
|
||||
std::unordered_map<TypeId, std::unique_ptr<PendingType>> typeVarChanges;
|
||||
std::unordered_map<TypePackId, std::unique_ptr<PendingTypePack>> typePackChanges;
|
||||
std::unordered_map<TypeId, std::unique_ptr<PendingType>, DenseHashPointer> typeVarChanges;
|
||||
std::unordered_map<TypePackId, std::unique_ptr<PendingTypePack>, DenseHashPointer> typePackChanges;
|
||||
|
||||
TxnLog* parent = nullptr;
|
||||
|
||||
|
|
|
@ -103,6 +103,11 @@ struct GenericTypeDefinitions
|
|||
std::vector<GenericTypePackDefinition> genericPacks;
|
||||
};
|
||||
|
||||
struct HashBoolNamePair
|
||||
{
|
||||
size_t operator()(const std::pair<bool, Name>& pair) const;
|
||||
};
|
||||
|
||||
// All TypeVars are retained via Environment::typeVars. All TypeIds
|
||||
// within a program are borrowed pointers into this set.
|
||||
struct TypeChecker
|
||||
|
@ -411,6 +416,12 @@ public:
|
|||
private:
|
||||
int checkRecursionCount = 0;
|
||||
int recursionCount = 0;
|
||||
|
||||
/**
|
||||
* We use this to avoid doing second-pass analysis of type aliases that are duplicates. We record a pair
|
||||
* (exported, name) to properly deal with the case where the two duplicates do not have the same export status.
|
||||
*/
|
||||
DenseHashSet<std::pair<bool, Name>, HashBoolNamePair> duplicateTypeAliases;
|
||||
};
|
||||
|
||||
// Unit test hook
|
||||
|
|
|
@ -54,9 +54,6 @@ struct TypePackVar
|
|||
bool persistent = false;
|
||||
|
||||
// Pointer to the type arena that allocated this type.
|
||||
// Do not depend on the value of this under any circumstances. This is for
|
||||
// debugging purposes only. This is only set in debug builds; it is nullptr
|
||||
// in all other environments.
|
||||
TypeArena* owningArena = nullptr;
|
||||
};
|
||||
|
||||
|
|
|
@ -449,9 +449,6 @@ struct TypeVar final
|
|||
std::optional<std::string> documentationSymbol;
|
||||
|
||||
// Pointer to the type arena that allocated this type.
|
||||
// Do not depend on the value of this under any circumstances. This is for
|
||||
// debugging purposes only. This is only set in debug builds; it is nullptr
|
||||
// in all other environments.
|
||||
TypeArena* owningArena = nullptr;
|
||||
|
||||
bool operator==(const TypeVar& rhs) const;
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#include "Luau/BuiltinDefinitions.h"
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauFixTonumberReturnType, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -115,6 +113,7 @@ declare function gcinfo(): number
|
|||
declare function error<T>(message: T, level: number?)
|
||||
|
||||
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
|
||||
|
@ -200,14 +199,7 @@ declare function gcinfo(): number
|
|||
|
||||
std::string getBuiltinDefinitionSource()
|
||||
{
|
||||
std::string result = kBuiltinDefinitionLuaSrc;
|
||||
|
||||
if (FFlag::LuauFixTonumberReturnType)
|
||||
result += "declare function tonumber<T>(value: T, radix: number?): number?\n";
|
||||
else
|
||||
result += "declare function tonumber<T>(value: T, radix: number?): number\n";
|
||||
|
||||
return result;
|
||||
return kBuiltinDefinitionLuaSrc;
|
||||
}
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -43,6 +43,7 @@ static const char* kWarningNames[] = {
|
|||
"DeprecatedApi",
|
||||
"TableOperations",
|
||||
"DuplicateCondition",
|
||||
"MisleadingAndOr",
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
@ -2040,18 +2041,28 @@ private:
|
|||
const Property* prop = lookupClassProp(cty, node->index.value);
|
||||
|
||||
if (prop && prop->deprecated)
|
||||
{
|
||||
if (!prop->deprecatedSuggestion.empty())
|
||||
emitWarning(*context, LintWarning::Code_DeprecatedApi, node->location, "Member '%s.%s' is deprecated, use '%s' instead",
|
||||
cty->name.c_str(), node->index.value, prop->deprecatedSuggestion.c_str());
|
||||
else
|
||||
emitWarning(*context, LintWarning::Code_DeprecatedApi, node->location, "Member '%s.%s' is deprecated", cty->name.c_str(),
|
||||
node->index.value);
|
||||
}
|
||||
report(node->location, *prop, cty->name.c_str(), node->index.value);
|
||||
}
|
||||
else if (const TableTypeVar* tty = get<TableTypeVar>(follow(*ty)))
|
||||
{
|
||||
auto prop = tty->props.find(node->index.value);
|
||||
|
||||
if (prop != tty->props.end() && prop->second.deprecated)
|
||||
report(node->location, prop->second, tty->name ? tty->name->c_str() : nullptr, node->index.value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void report(const Location& location, const Property& prop, const char* container, const char* field)
|
||||
{
|
||||
std::string suggestion = prop.deprecatedSuggestion.empty() ? "" : format(", use '%s' instead", prop.deprecatedSuggestion.c_str());
|
||||
|
||||
if (container)
|
||||
emitWarning(*context, LintWarning::Code_DeprecatedApi, location, "Member '%s.%s' is deprecated%s", container, field, suggestion.c_str());
|
||||
else
|
||||
emitWarning(*context, LintWarning::Code_DeprecatedApi, location, "Member '%s' is deprecated%s", field, suggestion.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
class LintTableOperations : AstVisitor
|
||||
|
@ -2257,6 +2268,39 @@ private:
|
|||
return false;
|
||||
}
|
||||
|
||||
bool visit(AstExprIfElse* expr) override
|
||||
{
|
||||
if (!expr->falseExpr->is<AstExprIfElse>())
|
||||
return true;
|
||||
|
||||
// if..elseif chain detected, we need to unroll it
|
||||
std::vector<AstExpr*> conditions;
|
||||
conditions.reserve(2);
|
||||
|
||||
AstExprIfElse* head = expr;
|
||||
while (head)
|
||||
{
|
||||
head->condition->visit(this);
|
||||
head->trueExpr->visit(this);
|
||||
|
||||
conditions.push_back(head->condition);
|
||||
|
||||
if (head->falseExpr->is<AstExprIfElse>())
|
||||
{
|
||||
head = head->falseExpr->as<AstExprIfElse>();
|
||||
continue;
|
||||
}
|
||||
|
||||
head->falseExpr->visit(this);
|
||||
break;
|
||||
}
|
||||
|
||||
detectDuplicates(conditions);
|
||||
|
||||
// block recursive visits so that we only analyze each chain once
|
||||
return false;
|
||||
}
|
||||
|
||||
bool visit(AstExprBinary* expr) override
|
||||
{
|
||||
if (expr->op != AstExprBinary::And && expr->op != AstExprBinary::Or)
|
||||
|
@ -2418,6 +2462,46 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
class LintMisleadingAndOr : AstVisitor
|
||||
{
|
||||
public:
|
||||
LUAU_NOINLINE static void process(LintContext& context)
|
||||
{
|
||||
LintMisleadingAndOr pass;
|
||||
pass.context = &context;
|
||||
|
||||
context.root->visit(&pass);
|
||||
}
|
||||
|
||||
private:
|
||||
LintContext* context;
|
||||
|
||||
bool visit(AstExprBinary* node) override
|
||||
{
|
||||
if (node->op != AstExprBinary::Or)
|
||||
return true;
|
||||
|
||||
AstExprBinary* and_ = node->left->as<AstExprBinary>();
|
||||
if (!and_ || and_->op != AstExprBinary::And)
|
||||
return true;
|
||||
|
||||
const char* alt = nullptr;
|
||||
|
||||
if (and_->right->is<AstExprConstantNil>())
|
||||
alt = "nil";
|
||||
else if (AstExprConstantBool* c = and_->right->as<AstExprConstantBool>(); c && c->value == false)
|
||||
alt = "false";
|
||||
|
||||
if (alt)
|
||||
emitWarning(*context, LintWarning::Code_MisleadingAndOr, node->location,
|
||||
"The and-or expression always evaluates to the second alternative because the first alternative is %s; consider using if-then-else "
|
||||
"expression instead",
|
||||
alt);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static void fillBuiltinGlobals(LintContext& context, const AstNameTable& names, const ScopePtr& env)
|
||||
{
|
||||
ScopePtr current = env;
|
||||
|
@ -2522,6 +2606,9 @@ std::vector<LintWarning> lint(AstStat* root, const AstNameTable& names, const Sc
|
|||
if (context.warningEnabled(LintWarning::Code_DuplicateLocal))
|
||||
LintDuplicateLocal::process(context);
|
||||
|
||||
if (context.warningEnabled(LintWarning::Code_MisleadingAndOr))
|
||||
LintMisleadingAndOr::process(context);
|
||||
|
||||
std::sort(context.result.begin(), context.result.end(), WarningComparator());
|
||||
|
||||
return context.result;
|
||||
|
|
|
@ -12,10 +12,10 @@
|
|||
#include <algorithm>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeArena, false)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauTrackOwningArena, false)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauTrackOwningArena, false) // Remove with FFlagLuauImmutableTypes
|
||||
LUAU_FASTINTVARIABLE(LuauTypeCloneRecursionLimit, 300)
|
||||
LUAU_FASTFLAG(LuauTypeAliasDefaults)
|
||||
|
||||
LUAU_FASTFLAG(LuauImmutableTypes)
|
||||
LUAU_FASTFLAGVARIABLE(LuauPrepopulateUnionOptionsBeforeAllocation, false)
|
||||
|
||||
namespace Luau
|
||||
|
@ -66,7 +66,7 @@ TypeId TypeArena::addTV(TypeVar&& tv)
|
|||
{
|
||||
TypeId allocated = typeVars.allocate(std::move(tv));
|
||||
|
||||
if (FFlag::DebugLuauTrackOwningArena)
|
||||
if (FFlag::DebugLuauTrackOwningArena || FFlag::LuauImmutableTypes)
|
||||
asMutable(allocated)->owningArena = this;
|
||||
|
||||
return allocated;
|
||||
|
@ -76,7 +76,7 @@ TypeId TypeArena::freshType(TypeLevel level)
|
|||
{
|
||||
TypeId allocated = typeVars.allocate(FreeTypeVar{level});
|
||||
|
||||
if (FFlag::DebugLuauTrackOwningArena)
|
||||
if (FFlag::DebugLuauTrackOwningArena || FFlag::LuauImmutableTypes)
|
||||
asMutable(allocated)->owningArena = this;
|
||||
|
||||
return allocated;
|
||||
|
@ -86,7 +86,7 @@ TypePackId TypeArena::addTypePack(std::initializer_list<TypeId> types)
|
|||
{
|
||||
TypePackId allocated = typePacks.allocate(TypePack{std::move(types)});
|
||||
|
||||
if (FFlag::DebugLuauTrackOwningArena)
|
||||
if (FFlag::DebugLuauTrackOwningArena || FFlag::LuauImmutableTypes)
|
||||
asMutable(allocated)->owningArena = this;
|
||||
|
||||
return allocated;
|
||||
|
@ -96,7 +96,7 @@ TypePackId TypeArena::addTypePack(std::vector<TypeId> types)
|
|||
{
|
||||
TypePackId allocated = typePacks.allocate(TypePack{std::move(types)});
|
||||
|
||||
if (FFlag::DebugLuauTrackOwningArena)
|
||||
if (FFlag::DebugLuauTrackOwningArena || FFlag::LuauImmutableTypes)
|
||||
asMutable(allocated)->owningArena = this;
|
||||
|
||||
return allocated;
|
||||
|
@ -106,7 +106,7 @@ TypePackId TypeArena::addTypePack(TypePack tp)
|
|||
{
|
||||
TypePackId allocated = typePacks.allocate(std::move(tp));
|
||||
|
||||
if (FFlag::DebugLuauTrackOwningArena)
|
||||
if (FFlag::DebugLuauTrackOwningArena || FFlag::LuauImmutableTypes)
|
||||
asMutable(allocated)->owningArena = this;
|
||||
|
||||
return allocated;
|
||||
|
@ -116,7 +116,7 @@ TypePackId TypeArena::addTypePack(TypePackVar tp)
|
|||
{
|
||||
TypePackId allocated = typePacks.allocate(std::move(tp));
|
||||
|
||||
if (FFlag::DebugLuauTrackOwningArena)
|
||||
if (FFlag::DebugLuauTrackOwningArena || FFlag::LuauImmutableTypes)
|
||||
asMutable(allocated)->owningArena = this;
|
||||
|
||||
return allocated;
|
||||
|
@ -454,8 +454,16 @@ TypeId clone(TypeId typeId, TypeArena& dest, SeenTypes& seenTypes, SeenTypePacks
|
|||
TypeCloner cloner{dest, typeId, seenTypes, seenTypePacks, cloneState};
|
||||
Luau::visit(cloner, typeId->ty); // Mutates the storage that 'res' points into.
|
||||
|
||||
// TODO: Make this work when the arena of 'res' might be frozen
|
||||
asMutable(res)->documentationSymbol = typeId->documentationSymbol;
|
||||
if (FFlag::LuauImmutableTypes)
|
||||
{
|
||||
// Persistent types are not being cloned and we get the original type back which might be read-only
|
||||
if (!res->persistent)
|
||||
asMutable(res)->documentationSymbol = typeId->documentationSymbol;
|
||||
}
|
||||
else
|
||||
{
|
||||
asMutable(res)->documentationSymbol = typeId->documentationSymbol;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include "Luau/Scope.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauTwoPassAliasDefinitionFix);
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -17,6 +19,8 @@ Scope::Scope(const ScopePtr& parent, int subLevel)
|
|||
, returnType(parent->returnType)
|
||||
, level(parent->level.incr())
|
||||
{
|
||||
if (FFlag::LuauTwoPassAliasDefinitionFix)
|
||||
level = level.incr();
|
||||
level.subLevel = subLevel;
|
||||
}
|
||||
|
||||
|
|
|
@ -250,6 +250,10 @@ PendingTypePack* TxnLog::queue(TypePackId tp)
|
|||
|
||||
PendingType* TxnLog::pending(TypeId ty) const
|
||||
{
|
||||
// This function will technically work if `this` is nullptr, but this
|
||||
// indicates a bug, so we explicitly assert.
|
||||
LUAU_ASSERT(static_cast<const void*>(this) != nullptr);
|
||||
|
||||
for (const TxnLog* current = this; current; current = current->parent)
|
||||
{
|
||||
if (auto it = current->typeVarChanges.find(ty); it != current->typeVarChanges.end())
|
||||
|
@ -261,6 +265,10 @@ PendingType* TxnLog::pending(TypeId ty) const
|
|||
|
||||
PendingTypePack* TxnLog::pending(TypePackId tp) const
|
||||
{
|
||||
// This function will technically work if `this` is nullptr, but this
|
||||
// indicates a bug, so we explicitly assert.
|
||||
LUAU_ASSERT(static_cast<const void*>(this) != nullptr);
|
||||
|
||||
for (const TxnLog* current = this; current; current = current->parent)
|
||||
{
|
||||
if (auto it = current->typePackChanges.find(tp); it != current->typePackChanges.end())
|
||||
|
|
|
@ -32,12 +32,13 @@ LUAU_FASTFLAGVARIABLE(LuauRecursiveTypeParameterRestriction, false)
|
|||
LUAU_FASTFLAGVARIABLE(LuauGenericFunctionsDontCacheTypeParams, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauIfElseBranchTypeUnion, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauIfElseExpectedType2, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauImmutableTypes, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauLengthOnCompositeType, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNoSealedTypeMod, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauQuantifyInPlace2, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSealExports, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSingletonTypes, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauDiscriminableUnions, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauDiscriminableUnions2, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTypeAliasDefaults, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauExpectedTypesOfProperties, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauErrorRecoveryType, false)
|
||||
|
@ -47,7 +48,10 @@ LUAU_FASTFLAGVARIABLE(LuauProperTypeLevels, false)
|
|||
LUAU_FASTFLAGVARIABLE(LuauAscribeCorrectLevelToInferredProperitesOfFreeTables, false)
|
||||
LUAU_FASTFLAG(LuauUnionTagMatchFix)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUnsealedTableLiteral, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTwoPassAliasDefinitionFix, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAssertStripsFalsyTypes, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauReturnAnyInsteadOfICE, false) // Eventually removed as false.
|
||||
LUAU_FASTFLAGVARIABLE(LuauAnotherTypeLevelFix, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -213,6 +217,11 @@ static bool isMetamethod(const Name& name)
|
|||
name == "__metatable" || name == "__eq" || name == "__lt" || name == "__le" || name == "__mode";
|
||||
}
|
||||
|
||||
size_t HashBoolNamePair::operator()(const std::pair<bool, Name>& pair) const
|
||||
{
|
||||
return std::hash<bool>()(pair.first) ^ std::hash<Name>()(pair.second);
|
||||
}
|
||||
|
||||
TypeChecker::TypeChecker(ModuleResolver* resolver, InternalErrorReporter* iceHandler)
|
||||
: resolver(resolver)
|
||||
, iceHandler(iceHandler)
|
||||
|
@ -225,6 +234,7 @@ TypeChecker::TypeChecker(ModuleResolver* resolver, InternalErrorReporter* iceHan
|
|||
, anyType(getSingletonTypes().anyType)
|
||||
, optionalNumberType(getSingletonTypes().optionalNumberType)
|
||||
, anyTypePack(getSingletonTypes().anyTypePack)
|
||||
, duplicateTypeAliases{{false, {}}}
|
||||
{
|
||||
globalScope = std::make_shared<Scope>(globalTypes.addTypePack(TypePackVar{FreeTypePack{TypeLevel{}}}));
|
||||
|
||||
|
@ -291,6 +301,9 @@ ModulePtr TypeChecker::check(const SourceModule& module, Mode mode, std::optiona
|
|||
unifierState.skipCacheForType.clear();
|
||||
}
|
||||
|
||||
if (FFlag::LuauTwoPassAliasDefinitionFix)
|
||||
duplicateTypeAliases.clear();
|
||||
|
||||
return std::move(currentModule);
|
||||
}
|
||||
|
||||
|
@ -496,6 +509,9 @@ LUAU_NOINLINE void TypeChecker::checkBlockTypeAliases(const ScopePtr& scope, std
|
|||
{
|
||||
if (const auto& typealias = stat->as<AstStatTypeAlias>())
|
||||
{
|
||||
if (FFlag::LuauTwoPassAliasDefinitionFix && typealias->name == Parser::errorName)
|
||||
continue;
|
||||
|
||||
auto& bindings = typealias->exported ? scope->exportedTypeBindings : scope->privateTypeBindings;
|
||||
|
||||
Name name = typealias->name.value;
|
||||
|
@ -1176,6 +1192,10 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatTypeAlias& typealias
|
|||
// Once with forwardDeclare, and once without.
|
||||
Name name = typealias.name.value;
|
||||
|
||||
// If the alias is missing a name, we can't do anything with it. Ignore it.
|
||||
if (FFlag::LuauTwoPassAliasDefinitionFix && name == Parser::errorName)
|
||||
return;
|
||||
|
||||
std::optional<TypeFun> binding;
|
||||
if (auto it = scope->exportedTypeBindings.find(name); it != scope->exportedTypeBindings.end())
|
||||
binding = it->second;
|
||||
|
@ -1192,6 +1212,8 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatTypeAlias& typealias
|
|||
reportError(TypeError{typealias.location, DuplicateTypeDefinition{name, location}});
|
||||
|
||||
bindingsMap[name] = TypeFun{binding->typeParams, binding->typePackParams, errorRecoveryType(anyType)};
|
||||
if (FFlag::LuauTwoPassAliasDefinitionFix)
|
||||
duplicateTypeAliases.insert({typealias.exported, name});
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1211,6 +1233,11 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatTypeAlias& typealias
|
|||
}
|
||||
else
|
||||
{
|
||||
// If the first pass failed (this should mean a duplicate definition), the second pass isn't going to be
|
||||
// interesting.
|
||||
if (FFlag::LuauTwoPassAliasDefinitionFix && duplicateTypeAliases.find({typealias.exported, name}))
|
||||
return;
|
||||
|
||||
if (!binding)
|
||||
ice("Not predeclared");
|
||||
|
||||
|
@ -1235,7 +1262,8 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatTypeAlias& typealias
|
|||
if (auto ttv = getMutable<TableTypeVar>(follow(ty)))
|
||||
{
|
||||
// If the table is already named and we want to rename the type function, we have to bind new alias to a copy
|
||||
if (ttv->name)
|
||||
// Additionally, we can't modify types that come from other modules
|
||||
if (ttv->name || (FFlag::LuauImmutableTypes && follow(ty)->owningArena != ¤tModule->internalTypes))
|
||||
{
|
||||
bool sameTys = std::equal(ttv->instantiatedTypeParams.begin(), ttv->instantiatedTypeParams.end(), binding->typeParams.begin(),
|
||||
binding->typeParams.end(), [](auto&& itp, auto&& tp) {
|
||||
|
@ -1247,7 +1275,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatTypeAlias& typealias
|
|||
});
|
||||
|
||||
// Copy can be skipped if this is an identical alias
|
||||
if (ttv->name != name || !sameTys || !sameTps)
|
||||
if ((FFlag::LuauImmutableTypes && !ttv->name) || ttv->name != name || !sameTys || !sameTps)
|
||||
{
|
||||
// This is a shallow clone, original recursive links to self are not updated
|
||||
TableTypeVar clone = TableTypeVar{ttv->props, ttv->indexer, ttv->level, ttv->state};
|
||||
|
@ -1279,9 +1307,17 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatTypeAlias& typealias
|
|||
}
|
||||
}
|
||||
else if (auto mtv = getMutable<MetatableTypeVar>(follow(ty)))
|
||||
mtv->syntheticName = name;
|
||||
{
|
||||
// We can't modify types that come from other modules
|
||||
if (!FFlag::LuauImmutableTypes || follow(ty)->owningArena == ¤tModule->internalTypes)
|
||||
mtv->syntheticName = name;
|
||||
}
|
||||
|
||||
unify(ty, bindingsMap[name].type, typealias.location);
|
||||
TypeId& bindingType = bindingsMap[name].type;
|
||||
bool ok = unify(ty, bindingType, typealias.location);
|
||||
|
||||
if (FFlag::LuauTwoPassAliasDefinitionFix && ok)
|
||||
bindingType = ty;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1564,7 +1600,12 @@ ExprResult<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExprCa
|
|||
else if (auto vtp = get<VariadicTypePack>(retPack))
|
||||
return {vtp->ty, std::move(result.predicates)};
|
||||
else if (get<Unifiable::Generic>(retPack))
|
||||
ice("Unexpected abstract type pack!", expr.location);
|
||||
{
|
||||
if (FFlag::LuauReturnAnyInsteadOfICE)
|
||||
return {anyType, std::move(result.predicates)};
|
||||
else
|
||||
ice("Unexpected abstract type pack!", expr.location);
|
||||
}
|
||||
else
|
||||
ice("Unknown TypePack type!", expr.location);
|
||||
}
|
||||
|
@ -1614,11 +1655,23 @@ std::optional<TypeId> TypeChecker::getIndexTypeFromType(
|
|||
|
||||
tablify(type);
|
||||
|
||||
const PrimitiveTypeVar* primitiveType = get<PrimitiveTypeVar>(type);
|
||||
if (primitiveType && primitiveType->type == PrimitiveTypeVar::String)
|
||||
if (FFlag::LuauDiscriminableUnions2)
|
||||
{
|
||||
if (std::optional<TypeId> mtIndex = findMetatableEntry(type, "__index", location))
|
||||
if (isString(type))
|
||||
{
|
||||
std::optional<TypeId> mtIndex = findMetatableEntry(stringType, "__index", location);
|
||||
LUAU_ASSERT(mtIndex);
|
||||
type = *mtIndex;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const PrimitiveTypeVar* primitiveType = get<PrimitiveTypeVar>(type);
|
||||
if (primitiveType && primitiveType->type == PrimitiveTypeVar::String)
|
||||
{
|
||||
if (std::optional<TypeId> mtIndex = findMetatableEntry(type, "__index", location))
|
||||
type = *mtIndex;
|
||||
}
|
||||
}
|
||||
|
||||
if (TableTypeVar* tableType = getMutableTableType(type))
|
||||
|
@ -2476,7 +2529,7 @@ ExprResult<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExprBi
|
|||
|
||||
auto [rhsTy, rhsPredicates] = checkExpr(innerScope, *expr.right);
|
||||
|
||||
return {checkBinaryOperation(FFlag::LuauDiscriminableUnions ? scope : innerScope, expr, lhsTy, rhsTy),
|
||||
return {checkBinaryOperation(FFlag::LuauDiscriminableUnions2 ? scope : innerScope, expr, lhsTy, rhsTy),
|
||||
{AndPredicate{std::move(lhsPredicates), std::move(rhsPredicates)}}};
|
||||
}
|
||||
else if (expr.op == AstExprBinary::Or)
|
||||
|
@ -2489,7 +2542,7 @@ ExprResult<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExprBi
|
|||
auto [rhsTy, rhsPredicates] = checkExpr(innerScope, *expr.right);
|
||||
|
||||
// Because of C++, I'm not sure if lhsPredicates was not moved out by the time we call checkBinaryOperation.
|
||||
TypeId result = checkBinaryOperation(FFlag::LuauDiscriminableUnions ? scope : innerScope, expr, lhsTy, rhsTy, lhsPredicates);
|
||||
TypeId result = checkBinaryOperation(FFlag::LuauDiscriminableUnions2 ? scope : innerScope, expr, lhsTy, rhsTy, lhsPredicates);
|
||||
return {result, {OrPredicate{std::move(lhsPredicates), std::move(rhsPredicates)}}};
|
||||
}
|
||||
else if (expr.op == AstExprBinary::CompareEq || expr.op == AstExprBinary::CompareNe)
|
||||
|
@ -2497,8 +2550,8 @@ ExprResult<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExprBi
|
|||
if (auto predicate = tryGetTypeGuardPredicate(expr))
|
||||
return {booleanType, {std::move(*predicate)}};
|
||||
|
||||
ExprResult<TypeId> lhs = checkExpr(scope, *expr.left, std::nullopt, /*forceSingleton=*/FFlag::LuauDiscriminableUnions);
|
||||
ExprResult<TypeId> rhs = checkExpr(scope, *expr.right, std::nullopt, /*forceSingleton=*/FFlag::LuauDiscriminableUnions);
|
||||
ExprResult<TypeId> lhs = checkExpr(scope, *expr.left, std::nullopt, /*forceSingleton=*/FFlag::LuauDiscriminableUnions2);
|
||||
ExprResult<TypeId> rhs = checkExpr(scope, *expr.right, std::nullopt, /*forceSingleton=*/FFlag::LuauDiscriminableUnions2);
|
||||
|
||||
PredicateVec predicates;
|
||||
|
||||
|
@ -2785,12 +2838,16 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex
|
|||
}
|
||||
else if (exprTable->state == TableState::Unsealed || exprTable->state == TableState::Free)
|
||||
{
|
||||
TypeId resultType = freshType(scope);
|
||||
TypeId resultType = freshType(FFlag::LuauAnotherTypeLevelFix ? exprTable->level : scope->level);
|
||||
exprTable->indexer = TableIndexer{anyIfNonstrict(indexType), anyIfNonstrict(resultType)};
|
||||
return resultType;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* If we use [] indexing to fetch a property from a sealed table that has no indexer, we have no idea if it will
|
||||
* work, so we just mint a fresh type, return that, and hope for the best.
|
||||
*/
|
||||
TypeId resultType = freshType(scope);
|
||||
return resultType;
|
||||
}
|
||||
|
@ -4195,6 +4252,9 @@ TypeId TypeChecker::checkRequire(const ScopePtr& scope, const ModuleInfo& module
|
|||
return errorRecoveryType(scope);
|
||||
}
|
||||
|
||||
if (FFlag::LuauImmutableTypes)
|
||||
return *moduleType;
|
||||
|
||||
SeenTypes seenTypes;
|
||||
SeenTypePacks seenTypePacks;
|
||||
CloneState cloneState;
|
||||
|
@ -5446,7 +5506,7 @@ GenericTypeDefinitions TypeChecker::createGenericTypes(const ScopePtr& scope, st
|
|||
|
||||
void TypeChecker::refineLValue(const LValue& lvalue, RefinementMap& refis, const ScopePtr& scope, TypeIdPredicate predicate)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauDiscriminableUnions);
|
||||
LUAU_ASSERT(FFlag::LuauDiscriminableUnions2);
|
||||
|
||||
const LValue* target = &lvalue;
|
||||
std::optional<LValue> key; // If set, we know we took the base of the lvalue path and should be walking down each option of the base's type.
|
||||
|
@ -5659,7 +5719,7 @@ void TypeChecker::resolve(const TruthyPredicate& truthyP, ErrorVec& errVec, Refi
|
|||
return std::nullopt;
|
||||
};
|
||||
|
||||
if (FFlag::LuauDiscriminableUnions)
|
||||
if (FFlag::LuauDiscriminableUnions2)
|
||||
{
|
||||
std::optional<TypeId> ty = resolveLValue(refis, scope, truthyP.lvalue);
|
||||
if (ty && fromOr)
|
||||
|
@ -5772,7 +5832,7 @@ void TypeChecker::resolve(const IsAPredicate& isaP, ErrorVec& errVec, Refinement
|
|||
return res;
|
||||
};
|
||||
|
||||
if (FFlag::LuauDiscriminableUnions)
|
||||
if (FFlag::LuauDiscriminableUnions2)
|
||||
{
|
||||
refineLValue(isaP.lvalue, refis, scope, predicate);
|
||||
}
|
||||
|
@ -5847,7 +5907,7 @@ void TypeChecker::resolve(const TypeGuardPredicate& typeguardP, ErrorVec& errVec
|
|||
|
||||
if (auto it = primitives.find(typeguardP.kind); it != primitives.end())
|
||||
{
|
||||
if (FFlag::LuauDiscriminableUnions)
|
||||
if (FFlag::LuauDiscriminableUnions2)
|
||||
{
|
||||
refineLValue(typeguardP.lvalue, refis, scope, it->second(sense));
|
||||
return;
|
||||
|
@ -5869,7 +5929,7 @@ void TypeChecker::resolve(const TypeGuardPredicate& typeguardP, ErrorVec& errVec
|
|||
}
|
||||
|
||||
auto fail = [&](const TypeErrorData& err) {
|
||||
if (!FFlag::LuauDiscriminableUnions)
|
||||
if (!FFlag::LuauDiscriminableUnions2)
|
||||
errVec.push_back(TypeError{typeguardP.location, err});
|
||||
addRefinement(refis, typeguardP.lvalue, errorRecoveryType(scope));
|
||||
};
|
||||
|
@ -5901,7 +5961,7 @@ void TypeChecker::resolve(const EqPredicate& eqP, ErrorVec& errVec, RefinementMa
|
|||
return {ty};
|
||||
};
|
||||
|
||||
if (FFlag::LuauDiscriminableUnions)
|
||||
if (FFlag::LuauDiscriminableUnions2)
|
||||
{
|
||||
std::vector<TypeId> rhs = options(eqP.type);
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ LUAU_FASTFLAGVARIABLE(LuauMetatableAreEqualRecursion, false)
|
|||
LUAU_FASTFLAGVARIABLE(LuauRefactorTypeVarQuestions, false)
|
||||
LUAU_FASTFLAG(LuauErrorRecoveryType)
|
||||
LUAU_FASTFLAG(LuauUnionTagMatchFix)
|
||||
LUAU_FASTFLAG(LuauDiscriminableUnions2)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -393,7 +394,8 @@ bool hasLength(TypeId ty, DenseHashSet<TypeId>& seen, int* recursionCount)
|
|||
if (seen.contains(ty))
|
||||
return true;
|
||||
|
||||
if (isPrim(ty, PrimitiveTypeVar::String) || get<AnyTypeVar>(ty) || get<TableTypeVar>(ty) || get<MetatableTypeVar>(ty))
|
||||
bool isStr = FFlag::LuauDiscriminableUnions2 ? isString(ty) : isPrim(ty, PrimitiveTypeVar::String);
|
||||
if (isStr || get<AnyTypeVar>(ty) || get<TableTypeVar>(ty) || get<MetatableTypeVar>(ty))
|
||||
return true;
|
||||
|
||||
if (auto uty = get<UnionTypeVar>(ty))
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
LUAU_FASTINT(LuauTypeInferRecursionLimit);
|
||||
LUAU_FASTINT(LuauTypeInferTypePackLoopLimit);
|
||||
LUAU_FASTFLAGVARIABLE(LuauCommittingTxnLogFreeTpPromote, false)
|
||||
LUAU_FASTFLAG(LuauImmutableTypes)
|
||||
LUAU_FASTFLAG(LuauUseCommittingTxnLog)
|
||||
LUAU_FASTINTVARIABLE(LuauTypeInferIterationLimit, 2000);
|
||||
LUAU_FASTFLAGVARIABLE(LuauTableSubtypingVariance2, false);
|
||||
|
@ -24,6 +25,7 @@ LUAU_FASTFLAG(LuauErrorRecoveryType);
|
|||
LUAU_FASTFLAG(LuauProperTypeLevels);
|
||||
LUAU_FASTFLAGVARIABLE(LuauUnifyPackTails, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUnionTagMatchFix, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFollowWithCommittingTxnLogInAnyUnification, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -32,11 +34,13 @@ struct PromoteTypeLevels
|
|||
{
|
||||
DEPRECATED_TxnLog& DEPRECATED_log;
|
||||
TxnLog& log;
|
||||
const TypeArena* typeArena = nullptr;
|
||||
TypeLevel minLevel;
|
||||
|
||||
explicit PromoteTypeLevels(DEPRECATED_TxnLog& DEPRECATED_log, TxnLog& log, TypeLevel minLevel)
|
||||
explicit PromoteTypeLevels(DEPRECATED_TxnLog& DEPRECATED_log, TxnLog& log, const TypeArena* typeArena, TypeLevel minLevel)
|
||||
: DEPRECATED_log(DEPRECATED_log)
|
||||
, log(log)
|
||||
, typeArena(typeArena)
|
||||
, minLevel(minLevel)
|
||||
{
|
||||
}
|
||||
|
@ -65,8 +69,12 @@ struct PromoteTypeLevels
|
|||
}
|
||||
|
||||
template<typename TID, typename T>
|
||||
bool operator()(TID, const T&)
|
||||
bool operator()(TID ty, const T&)
|
||||
{
|
||||
// Type levels of types from other modules are already global, so we don't need to promote anything inside
|
||||
if (FFlag::LuauImmutableTypes && ty->owningArena != typeArena)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -83,12 +91,20 @@ struct PromoteTypeLevels
|
|||
|
||||
bool operator()(TypeId ty, const FunctionTypeVar&)
|
||||
{
|
||||
// Type levels of types from other modules are already global, so we don't need to promote anything inside
|
||||
if (FFlag::LuauImmutableTypes && ty->owningArena != typeArena)
|
||||
return false;
|
||||
|
||||
promote(ty, FFlag::LuauUseCommittingTxnLog ? log.getMutable<FunctionTypeVar>(ty) : getMutable<FunctionTypeVar>(ty));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator()(TypeId ty, const TableTypeVar& ttv)
|
||||
{
|
||||
// Type levels of types from other modules are already global, so we don't need to promote anything inside
|
||||
if (FFlag::LuauImmutableTypes && ty->owningArena != typeArena)
|
||||
return false;
|
||||
|
||||
if (ttv.state != TableState::Free && ttv.state != TableState::Generic)
|
||||
return true;
|
||||
|
||||
|
@ -108,24 +124,33 @@ struct PromoteTypeLevels
|
|||
}
|
||||
};
|
||||
|
||||
void promoteTypeLevels(DEPRECATED_TxnLog& DEPRECATED_log, TxnLog& log, TypeLevel minLevel, TypeId ty)
|
||||
void promoteTypeLevels(DEPRECATED_TxnLog& DEPRECATED_log, TxnLog& log, const TypeArena* typeArena, TypeLevel minLevel, TypeId ty)
|
||||
{
|
||||
PromoteTypeLevels ptl{DEPRECATED_log, log, minLevel};
|
||||
// Type levels of types from other modules are already global, so we don't need to promote anything inside
|
||||
if (FFlag::LuauImmutableTypes && ty->owningArena != typeArena)
|
||||
return;
|
||||
|
||||
PromoteTypeLevels ptl{DEPRECATED_log, log, typeArena, minLevel};
|
||||
DenseHashSet<void*> seen{nullptr};
|
||||
visitTypeVarOnce(ty, ptl, seen);
|
||||
}
|
||||
|
||||
void promoteTypeLevels(DEPRECATED_TxnLog& DEPRECATED_log, TxnLog& log, TypeLevel minLevel, TypePackId tp)
|
||||
void promoteTypeLevels(DEPRECATED_TxnLog& DEPRECATED_log, TxnLog& log, const TypeArena* typeArena, TypeLevel minLevel, TypePackId tp)
|
||||
{
|
||||
PromoteTypeLevels ptl{DEPRECATED_log, log, minLevel};
|
||||
// Type levels of types from other modules are already global, so we don't need to promote anything inside
|
||||
if (FFlag::LuauImmutableTypes && tp->owningArena != typeArena)
|
||||
return;
|
||||
|
||||
PromoteTypeLevels ptl{DEPRECATED_log, log, typeArena, minLevel};
|
||||
DenseHashSet<void*> seen{nullptr};
|
||||
visitTypeVarOnce(tp, ptl, seen);
|
||||
}
|
||||
|
||||
struct SkipCacheForType
|
||||
{
|
||||
SkipCacheForType(const DenseHashMap<TypeId, bool>& skipCacheForType)
|
||||
SkipCacheForType(const DenseHashMap<TypeId, bool>& skipCacheForType, const TypeArena* typeArena)
|
||||
: skipCacheForType(skipCacheForType)
|
||||
, typeArena(typeArena)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -152,6 +177,10 @@ struct SkipCacheForType
|
|||
|
||||
bool operator()(TypeId ty, const TableTypeVar&)
|
||||
{
|
||||
// Types from other modules don't contain mutable elements and are ok to cache
|
||||
if (FFlag::LuauImmutableTypes && ty->owningArena != typeArena)
|
||||
return false;
|
||||
|
||||
TableTypeVar& ttv = *getMutable<TableTypeVar>(ty);
|
||||
|
||||
if (ttv.boundTo)
|
||||
|
@ -172,6 +201,10 @@ struct SkipCacheForType
|
|||
template<typename T>
|
||||
bool operator()(TypeId ty, const T& t)
|
||||
{
|
||||
// Types from other modules don't contain mutable elements and are ok to cache
|
||||
if (FFlag::LuauImmutableTypes && ty->owningArena != typeArena)
|
||||
return false;
|
||||
|
||||
const bool* prev = skipCacheForType.find(ty);
|
||||
|
||||
if (prev && *prev)
|
||||
|
@ -184,8 +217,12 @@ struct SkipCacheForType
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
bool operator()(TypePackId, const T&)
|
||||
bool operator()(TypePackId tp, const T&)
|
||||
{
|
||||
// Types from other modules don't contain mutable elements and are ok to cache
|
||||
if (FFlag::LuauImmutableTypes && tp->owningArena != typeArena)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -208,6 +245,7 @@ struct SkipCacheForType
|
|||
}
|
||||
|
||||
const DenseHashMap<TypeId, bool>& skipCacheForType;
|
||||
const TypeArena* typeArena = nullptr;
|
||||
bool result = false;
|
||||
};
|
||||
|
||||
|
@ -422,13 +460,13 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool
|
|||
{
|
||||
if (FFlag::LuauUseCommittingTxnLog)
|
||||
{
|
||||
promoteTypeLevels(DEPRECATED_log, log, superLevel, subTy);
|
||||
promoteTypeLevels(DEPRECATED_log, log, types, superLevel, subTy);
|
||||
log.replace(superTy, BoundTypeVar(subTy));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FFlag::LuauProperTypeLevels)
|
||||
promoteTypeLevels(DEPRECATED_log, log, superLevel, subTy);
|
||||
promoteTypeLevels(DEPRECATED_log, log, types, superLevel, subTy);
|
||||
else if (auto subLevel = getMutableLevel(subTy))
|
||||
{
|
||||
if (!subLevel->subsumes(superFree->level))
|
||||
|
@ -466,13 +504,13 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool
|
|||
{
|
||||
if (FFlag::LuauUseCommittingTxnLog)
|
||||
{
|
||||
promoteTypeLevels(DEPRECATED_log, log, subLevel, superTy);
|
||||
promoteTypeLevels(DEPRECATED_log, log, types, subLevel, superTy);
|
||||
log.replace(subTy, BoundTypeVar(superTy));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FFlag::LuauProperTypeLevels)
|
||||
promoteTypeLevels(DEPRECATED_log, log, subLevel, superTy);
|
||||
promoteTypeLevels(DEPRECATED_log, log, types, subLevel, superTy);
|
||||
else if (auto superLevel = getMutableLevel(superTy))
|
||||
{
|
||||
if (!superLevel->subsumes(subFree->level))
|
||||
|
@ -849,7 +887,7 @@ void Unifier::cacheResult(TypeId subTy, TypeId superTy)
|
|||
return;
|
||||
|
||||
auto skipCacheFor = [this](TypeId ty) {
|
||||
SkipCacheForType visitor{sharedState.skipCacheForType};
|
||||
SkipCacheForType visitor{sharedState.skipCacheForType, types};
|
||||
visitTypeVarOnce(ty, visitor, sharedState.seenAny);
|
||||
|
||||
sharedState.skipCacheForType[ty] = visitor.result;
|
||||
|
@ -1637,32 +1675,35 @@ void Unifier::tryUnifyFunctions(TypeId subTy, TypeId superTy, bool isFunctionCal
|
|||
tryUnify_(subFunction->retType, superFunction->retType);
|
||||
}
|
||||
|
||||
if (FFlag::LuauUseCommittingTxnLog)
|
||||
if (!FFlag::LuauImmutableTypes)
|
||||
{
|
||||
if (superFunction->definition && !subFunction->definition && !subTy->persistent)
|
||||
if (FFlag::LuauUseCommittingTxnLog)
|
||||
{
|
||||
PendingType* newSubTy = log.queue(subTy);
|
||||
FunctionTypeVar* newSubFtv = getMutable<FunctionTypeVar>(newSubTy);
|
||||
LUAU_ASSERT(newSubFtv);
|
||||
newSubFtv->definition = superFunction->definition;
|
||||
if (superFunction->definition && !subFunction->definition && !subTy->persistent)
|
||||
{
|
||||
PendingType* newSubTy = log.queue(subTy);
|
||||
FunctionTypeVar* newSubFtv = getMutable<FunctionTypeVar>(newSubTy);
|
||||
LUAU_ASSERT(newSubFtv);
|
||||
newSubFtv->definition = superFunction->definition;
|
||||
}
|
||||
else if (!superFunction->definition && subFunction->definition && !superTy->persistent)
|
||||
{
|
||||
PendingType* newSuperTy = log.queue(superTy);
|
||||
FunctionTypeVar* newSuperFtv = getMutable<FunctionTypeVar>(newSuperTy);
|
||||
LUAU_ASSERT(newSuperFtv);
|
||||
newSuperFtv->definition = subFunction->definition;
|
||||
}
|
||||
}
|
||||
else if (!superFunction->definition && subFunction->definition && !superTy->persistent)
|
||||
else
|
||||
{
|
||||
PendingType* newSuperTy = log.queue(superTy);
|
||||
FunctionTypeVar* newSuperFtv = getMutable<FunctionTypeVar>(newSuperTy);
|
||||
LUAU_ASSERT(newSuperFtv);
|
||||
newSuperFtv->definition = subFunction->definition;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (superFunction->definition && !subFunction->definition && !subTy->persistent)
|
||||
{
|
||||
subFunction->definition = superFunction->definition;
|
||||
}
|
||||
else if (!superFunction->definition && subFunction->definition && !superTy->persistent)
|
||||
{
|
||||
superFunction->definition = subFunction->definition;
|
||||
if (superFunction->definition && !subFunction->definition && !subTy->persistent)
|
||||
{
|
||||
subFunction->definition = superFunction->definition;
|
||||
}
|
||||
else if (!superFunction->definition && subFunction->definition && !superTy->persistent)
|
||||
{
|
||||
superFunction->definition = subFunction->definition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2631,7 +2672,7 @@ static void queueTypePack(std::vector<TypeId>& queue, DenseHashSet<TypePackId>&
|
|||
{
|
||||
while (true)
|
||||
{
|
||||
a = follow(a);
|
||||
a = FFlag::LuauFollowWithCommittingTxnLogInAnyUnification ? state.log.follow(a) : follow(a);
|
||||
|
||||
if (seenTypePacks.find(a))
|
||||
break;
|
||||
|
@ -2738,7 +2779,7 @@ void Unifier::tryUnifyVariadics(TypePackId subTp, TypePackId superTp, bool rever
|
|||
}
|
||||
|
||||
static void tryUnifyWithAny(std::vector<TypeId>& queue, Unifier& state, DenseHashSet<TypeId>& seen, DenseHashSet<TypePackId>& seenTypePacks,
|
||||
TypeId anyType, TypePackId anyTypePack)
|
||||
const TypeArena* typeArena, TypeId anyType, TypePackId anyTypePack)
|
||||
{
|
||||
while (!queue.empty())
|
||||
{
|
||||
|
@ -2746,8 +2787,14 @@ static void tryUnifyWithAny(std::vector<TypeId>& queue, Unifier& state, DenseHas
|
|||
{
|
||||
TypeId ty = state.log.follow(queue.back());
|
||||
queue.pop_back();
|
||||
|
||||
// Types from other modules don't have free types
|
||||
if (FFlag::LuauImmutableTypes && ty->owningArena != typeArena)
|
||||
continue;
|
||||
|
||||
if (seen.find(ty))
|
||||
continue;
|
||||
|
||||
seen.insert(ty);
|
||||
|
||||
if (state.log.getMutable<FreeTypeVar>(ty))
|
||||
|
@ -2853,7 +2900,7 @@ void Unifier::tryUnifyWithAny(TypeId subTy, TypeId anyTy)
|
|||
sharedState.tempSeenTy.clear();
|
||||
sharedState.tempSeenTp.clear();
|
||||
|
||||
Luau::tryUnifyWithAny(queue, *this, sharedState.tempSeenTy, sharedState.tempSeenTp, getSingletonTypes().anyType, anyTP);
|
||||
Luau::tryUnifyWithAny(queue, *this, sharedState.tempSeenTy, sharedState.tempSeenTp, types, getSingletonTypes().anyType, anyTP);
|
||||
}
|
||||
|
||||
void Unifier::tryUnifyWithAny(TypePackId subTy, TypePackId anyTp)
|
||||
|
@ -2869,7 +2916,7 @@ void Unifier::tryUnifyWithAny(TypePackId subTy, TypePackId anyTp)
|
|||
|
||||
queueTypePack(queue, sharedState.tempSeenTp, *this, subTy, anyTp);
|
||||
|
||||
Luau::tryUnifyWithAny(queue, *this, sharedState.tempSeenTy, sharedState.tempSeenTp, anyTy, anyTp);
|
||||
Luau::tryUnifyWithAny(queue, *this, sharedState.tempSeenTy, sharedState.tempSeenTp, types, anyTy, anyTp);
|
||||
}
|
||||
|
||||
std::optional<TypeId> Unifier::findTablePropertyRespectingMeta(TypeId lhsType, Name name)
|
||||
|
|
|
@ -1133,7 +1133,7 @@ AstTypePack* Parser::parseTypeList(TempVector<AstType*>& result, TempVector<std:
|
|||
resultNames.push_back({});
|
||||
|
||||
resultNames.push_back(AstArgumentName{AstName(lexer.current().name), lexer.current().location});
|
||||
lexer.next();
|
||||
nextLexeme();
|
||||
|
||||
expectAndConsume(':');
|
||||
}
|
||||
|
|
68
CLI/Repl.cpp
68
CLI/Repl.cpp
|
@ -1,4 +1,6 @@
|
|||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#include "Repl.h"
|
||||
|
||||
#include "lua.h"
|
||||
#include "lualib.h"
|
||||
|
||||
|
@ -38,13 +40,14 @@ enum class CompileFormat
|
|||
struct GlobalOptions
|
||||
{
|
||||
int optimizationLevel = 1;
|
||||
int debugLevel = 1;
|
||||
} globalOptions;
|
||||
|
||||
static Luau::CompileOptions copts()
|
||||
{
|
||||
Luau::CompileOptions result = {};
|
||||
result.optimizationLevel = globalOptions.optimizationLevel;
|
||||
result.debugLevel = 1;
|
||||
result.debugLevel = globalOptions.debugLevel;
|
||||
result.coverageLevel = coverageActive() ? 2 : 0;
|
||||
|
||||
return result;
|
||||
|
@ -240,9 +243,8 @@ std::string runCode(lua_State* L, const std::string& source)
|
|||
return std::string();
|
||||
}
|
||||
|
||||
static void completeIndexer(ic_completion_env_t* cenv, const char* editBuffer)
|
||||
static void completeIndexer(lua_State* L, const std::string& editBuffer, const AddCompletionCallback& addCompletionCallback)
|
||||
{
|
||||
auto* L = reinterpret_cast<lua_State*>(ic_completion_arg(cenv));
|
||||
std::string_view lookup = editBuffer;
|
||||
char lastSep = 0;
|
||||
|
||||
|
@ -276,7 +278,7 @@ static void completeIndexer(ic_completion_env_t* cenv, const char* editBuffer)
|
|||
// Add an opening paren for function calls by default.
|
||||
completion += "(";
|
||||
}
|
||||
ic_add_completion_ex(cenv, completion.data(), key.data(), nullptr);
|
||||
addCompletionCallback(completion, std::string(key));
|
||||
}
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
@ -295,10 +297,11 @@ static void completeIndexer(ic_completion_env_t* cenv, const char* editBuffer)
|
|||
{
|
||||
// Replace the string object with the string class to perform further lookups of string functions
|
||||
// Note: We retrieve the string class from _G to prevent issues if the user assigns to `string`.
|
||||
lua_pop(L, 1); // Pop the string instance
|
||||
lua_getglobal(L, "_G");
|
||||
lua_pushlstring(L, "string", 6);
|
||||
lua_rawget(L, -2);
|
||||
lua_remove(L, -2);
|
||||
lua_remove(L, -2); // Remove the global table
|
||||
LUAU_ASSERT(lua_istable(L, -1));
|
||||
}
|
||||
else if (!lua_istable(L, -1))
|
||||
|
@ -312,6 +315,26 @@ static void completeIndexer(ic_completion_env_t* cenv, const char* editBuffer)
|
|||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
void getCompletions(lua_State* L, const std::string& editBuffer, const AddCompletionCallback& addCompletionCallback)
|
||||
{
|
||||
// look the value up in current global table first
|
||||
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
||||
completeIndexer(L, editBuffer, addCompletionCallback);
|
||||
|
||||
// and in actual global table after that
|
||||
lua_getglobal(L, "_G");
|
||||
completeIndexer(L, editBuffer, addCompletionCallback);
|
||||
}
|
||||
|
||||
static void icGetCompletions(ic_completion_env_t* cenv, const char* editBuffer)
|
||||
{
|
||||
auto* L = reinterpret_cast<lua_State*>(ic_completion_arg(cenv));
|
||||
|
||||
getCompletions(L, std::string(editBuffer), [cenv](const std::string& completion, const std::string& display) {
|
||||
ic_add_completion_ex(cenv, completion.data(), display.data(), nullptr);
|
||||
});
|
||||
}
|
||||
|
||||
static bool isMethodOrFunctionChar(const char* s, long len)
|
||||
{
|
||||
char c = *s;
|
||||
|
@ -320,15 +343,7 @@ static bool isMethodOrFunctionChar(const char* s, long len)
|
|||
|
||||
static void completeRepl(ic_completion_env_t* cenv, const char* editBuffer)
|
||||
{
|
||||
auto* L = reinterpret_cast<lua_State*>(ic_completion_arg(cenv));
|
||||
|
||||
// look the value up in current global table first
|
||||
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
||||
ic_complete_word(cenv, editBuffer, completeIndexer, isMethodOrFunctionChar);
|
||||
|
||||
// and in actual global table after that
|
||||
lua_getglobal(L, "_G");
|
||||
ic_complete_word(cenv, editBuffer, completeIndexer, isMethodOrFunctionChar);
|
||||
ic_complete_word(cenv, editBuffer, icGetCompletions, isMethodOrFunctionChar);
|
||||
}
|
||||
|
||||
struct LinenoiseScopedHistory
|
||||
|
@ -372,19 +387,20 @@ static void runReplImpl(lua_State* L)
|
|||
|
||||
for (;;)
|
||||
{
|
||||
const char* line = ic_readline(buffer.empty() ? "" : ">");
|
||||
const char* prompt = buffer.empty() ? "" : ">";
|
||||
std::unique_ptr<char, void (*)(void*)> line(ic_readline(prompt), free);
|
||||
if (!line)
|
||||
break;
|
||||
|
||||
if (buffer.empty() && runCode(L, std::string("return ") + line) == std::string())
|
||||
if (buffer.empty() && runCode(L, std::string("return ") + line.get()) == std::string())
|
||||
{
|
||||
ic_history_add(line);
|
||||
ic_history_add(line.get());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!buffer.empty())
|
||||
buffer += "\n";
|
||||
buffer += line;
|
||||
buffer += line.get();
|
||||
|
||||
std::string error = runCode(L, buffer);
|
||||
|
||||
|
@ -400,7 +416,6 @@ static void runReplImpl(lua_State* L)
|
|||
|
||||
ic_history_add(buffer.c_str());
|
||||
buffer.clear();
|
||||
free((void*)line);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -504,7 +519,7 @@ static bool compileFile(const char* name, CompileFormat format)
|
|||
|
||||
if (format == CompileFormat::Text)
|
||||
{
|
||||
bcb.setDumpFlags(Luau::BytecodeBuilder::Dump_Code | Luau::BytecodeBuilder::Dump_Source);
|
||||
bcb.setDumpFlags(Luau::BytecodeBuilder::Dump_Code | Luau::BytecodeBuilder::Dump_Source | Luau::BytecodeBuilder::Dump_Locals);
|
||||
bcb.setDumpSource(*source);
|
||||
}
|
||||
|
||||
|
@ -549,7 +564,8 @@ static void displayHelp(const char* argv0)
|
|||
printf(" --coverage: collect code coverage while running the code and output results to coverage.out\n");
|
||||
printf(" -h, --help: Display this usage message.\n");
|
||||
printf(" -i, --interactive: Run an interactive REPL after executing the last script specified.\n");
|
||||
printf(" -O<n>: use compiler optimization level (n=0-2).\n");
|
||||
printf(" -O<n>: compile with optimization level n (default 1, n should be between 0 and 2).\n");
|
||||
printf(" -g<n>: compile with debug level n (default 1, n should be between 0 and 2).\n");
|
||||
printf(" --profile[=N]: profile the code using N Hz sampling (default 10000) and output results to profile.out\n");
|
||||
printf(" --timetrace: record compiler time tracing information into trace.json\n");
|
||||
}
|
||||
|
@ -620,6 +636,16 @@ int replMain(int argc, char** argv)
|
|||
}
|
||||
globalOptions.optimizationLevel = level |