mirror of
https://github.com/luau-lang/luau.git
synced 2024-11-15 06:15:44 +08:00
Sync to upstream/release/640 (#1374)
### What's new * Fixed many of the false positive errors in indexing of table unions and table intersections * It is now possible to run custom checks over Luau AST during typechecking by setting `customModuleCheck` in `FrontendOptions` * Fixed codegen issue on arm, where number->vector cast could corrupt that number value for the next time it's read ### New Solver * `error` type now behaves as the bottom type during subtyping checks * Fixed the scope that is used in subtyping with generic types * Fixed `astOriginalCallTypes` table often used by LSP to match the old solver --- ### Internal Contributors Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
This commit is contained in:
parent
1dde4de793
commit
d518d14b92
@ -108,6 +108,10 @@ struct FrontendOptions
|
||||
|
||||
// When true, some internal complexity limits will be scaled down for modules that miss the limit set by moduleTimeLimitSec
|
||||
bool applyInternalLimitScaling = false;
|
||||
|
||||
// An optional callback which is called for every *dirty* module was checked
|
||||
// Is multi-threaded typechecking is used, this callback might be called from multiple threads and has to be thread-safe
|
||||
std::function<void(const SourceModule& sourceModule, const Luau::Module& module)> customModuleCheck;
|
||||
};
|
||||
|
||||
struct CheckResult
|
||||
|
@ -125,7 +125,6 @@ struct Subtyping
|
||||
NotNull<Normalizer> normalizer;
|
||||
NotNull<InternalErrorReporter> iceReporter;
|
||||
|
||||
NotNull<Scope> scope;
|
||||
TypeCheckLimits limits;
|
||||
|
||||
enum class Variance
|
||||
@ -144,8 +143,7 @@ struct Subtyping
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<TypeArena> typeArena,
|
||||
NotNull<Normalizer> normalizer,
|
||||
NotNull<InternalErrorReporter> iceReporter,
|
||||
NotNull<Scope> scope
|
||||
NotNull<InternalErrorReporter> iceReporter
|
||||
);
|
||||
|
||||
Subtyping(const Subtyping&) = delete;
|
||||
@ -164,75 +162,125 @@ struct Subtyping
|
||||
// TODO cyclic types
|
||||
// TODO recursion limits
|
||||
|
||||
SubtypingResult isSubtype(TypeId subTy, TypeId superTy);
|
||||
SubtypingResult isSubtype(TypePackId subTy, TypePackId superTy);
|
||||
SubtypingResult isSubtype(TypeId subTy, TypeId superTy, NotNull<Scope> scope);
|
||||
SubtypingResult isSubtype(TypePackId subTy, TypePackId superTy, NotNull<Scope> scope);
|
||||
|
||||
private:
|
||||
DenseHashMap<std::pair<TypeId, TypeId>, SubtypingResult, TypePairHash> resultCache{{}};
|
||||
|
||||
SubtypingResult cache(SubtypingEnvironment& env, SubtypingResult res, TypeId subTy, TypeId superTy);
|
||||
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypeId subTy, TypeId superTy);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypePackId subTy, TypePackId superTy);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypeId subTy, TypeId superTy, NotNull<Scope> scope);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypePackId subTp, TypePackId superTp, NotNull<Scope> scope);
|
||||
|
||||
template<typename SubTy, typename SuperTy>
|
||||
SubtypingResult isContravariantWith(SubtypingEnvironment& env, SubTy&& subTy, SuperTy&& superTy);
|
||||
SubtypingResult isContravariantWith(SubtypingEnvironment& env, SubTy&& subTy, SuperTy&& superTy, NotNull<Scope> scope);
|
||||
|
||||
template<typename SubTy, typename SuperTy>
|
||||
SubtypingResult isInvariantWith(SubtypingEnvironment& env, SubTy&& subTy, SuperTy&& superTy);
|
||||
SubtypingResult isInvariantWith(SubtypingEnvironment& env, SubTy&& subTy, SuperTy&& superTy, NotNull<Scope> scope);
|
||||
|
||||
template<typename SubTy, typename SuperTy>
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TryPair<const SubTy*, const SuperTy*>& pair);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TryPair<const SubTy*, const SuperTy*>& pair, NotNull<Scope> scope);
|
||||
|
||||
template<typename SubTy, typename SuperTy>
|
||||
SubtypingResult isContravariantWith(SubtypingEnvironment& env, const TryPair<const SubTy*, const SuperTy*>& pair);
|
||||
SubtypingResult isContravariantWith(SubtypingEnvironment& env, const TryPair<const SubTy*, const SuperTy*>& pair, NotNull<Scope>);
|
||||
|
||||
template<typename SubTy, typename SuperTy>
|
||||
SubtypingResult isInvariantWith(SubtypingEnvironment& env, const TryPair<const SubTy*, const SuperTy*>& pair);
|
||||
SubtypingResult isInvariantWith(SubtypingEnvironment& env, const TryPair<const SubTy*, const SuperTy*>& pair, NotNull<Scope>);
|
||||
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const UnionType* superUnion);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const UnionType* subUnion, TypeId superTy);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const IntersectionType* superIntersection);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const IntersectionType* subIntersection, TypeId superTy);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const UnionType* superUnion, NotNull<Scope> scope);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const UnionType* subUnion, TypeId superTy, NotNull<Scope> scope);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const IntersectionType* superIntersection, NotNull<Scope> scope);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const IntersectionType* subIntersection, TypeId superTy, NotNull<Scope> scope);
|
||||
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const NegationType* subNegation, TypeId superTy);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeId subTy, const NegationType* superNegation);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const NegationType* subNegation, TypeId superTy, NotNull<Scope> scope);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeId subTy, const NegationType* superNegation, NotNull<Scope> scope);
|
||||
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const PrimitiveType* subPrim, const PrimitiveType* superPrim);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const SingletonType* subSingleton, const PrimitiveType* superPrim);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const SingletonType* subSingleton, const SingletonType* superSingleton);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TableType* subTable, const TableType* superTable);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const MetatableType* superMt);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const TableType* superTable);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const ClassType* subClass, const ClassType* superClass);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const ClassType* subClass, TypeId superTy, const TableType* superTable);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const FunctionType* subFunction, const FunctionType* superFunction);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TableType* subTable, const PrimitiveType* superPrim);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const PrimitiveType* subPrim, const TableType* superTable);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const SingletonType* subSingleton, const TableType* superTable);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const PrimitiveType* subPrim, const PrimitiveType* superPrim, NotNull<Scope> scope);
|
||||
SubtypingResult isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const SingletonType* subSingleton,
|
||||
const PrimitiveType* superPrim,
|
||||
NotNull<Scope> scope
|
||||
);
|
||||
SubtypingResult isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const SingletonType* subSingleton,
|
||||
const SingletonType* superSingleton,
|
||||
NotNull<Scope> scope
|
||||
);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TableType* subTable, const TableType* superTable, NotNull<Scope> scope);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const MetatableType* superMt, NotNull<Scope> scope);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const TableType* superTable, NotNull<Scope> scope);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const ClassType* subClass, const ClassType* superClass, NotNull<Scope> scope);
|
||||
SubtypingResult
|
||||
isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const ClassType* subClass, TypeId superTy, const TableType* superTable, NotNull<Scope>);
|
||||
SubtypingResult isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const FunctionType* subFunction,
|
||||
const FunctionType* superFunction,
|
||||
NotNull<Scope> scope
|
||||
);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TableType* subTable, const PrimitiveType* superPrim, NotNull<Scope> scope);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const PrimitiveType* subPrim, const TableType* superTable, NotNull<Scope> scope);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const SingletonType* subSingleton, const TableType* superTable, NotNull<Scope> scope);
|
||||
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TableIndexer& subIndexer, const TableIndexer& superIndexer);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const Property& subProperty, const Property& superProperty, const std::string& name);
|
||||
SubtypingResult isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const TableIndexer& subIndexer,
|
||||
const TableIndexer& superIndexer,
|
||||
NotNull<Scope> scope
|
||||
);
|
||||
SubtypingResult
|
||||
isCovariantWith(SubtypingEnvironment& env, const Property& subProperty, const Property& superProperty, const std::string& name, NotNull<Scope>);
|
||||
|
||||
SubtypingResult isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const std::shared_ptr<const NormalizedType>& subNorm,
|
||||
const std::shared_ptr<const NormalizedType>& superNorm
|
||||
const std::shared_ptr<const NormalizedType>& superNorm,
|
||||
NotNull<Scope> scope
|
||||
);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const NormalizedClassType& subClass, const NormalizedClassType& superClass);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const NormalizedClassType& subClass, const TypeIds& superTables);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const NormalizedStringType& subString, const NormalizedStringType& superString);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const NormalizedStringType& subString, const TypeIds& superTables);
|
||||
SubtypingResult isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const NormalizedFunctionType& subFunction,
|
||||
const NormalizedFunctionType& superFunction
|
||||
const NormalizedClassType& subClass,
|
||||
const NormalizedClassType& superClass,
|
||||
NotNull<Scope> scope
|
||||
);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeIds& subTypes, const TypeIds& superTypes);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const NormalizedClassType& subClass, const TypeIds& superTables, NotNull<Scope> scope);
|
||||
SubtypingResult isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const NormalizedStringType& subString,
|
||||
const NormalizedStringType& superString,
|
||||
NotNull<Scope> scope
|
||||
);
|
||||
SubtypingResult isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const NormalizedStringType& subString,
|
||||
const TypeIds& superTables,
|
||||
NotNull<Scope> scope
|
||||
);
|
||||
SubtypingResult
|
||||
isCovariantWith(SubtypingEnvironment& env, const NormalizedFunctionType& subFunction, const NormalizedFunctionType& superFunction, NotNull<Scope>);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeIds& subTypes, const TypeIds& superTypes, NotNull<Scope> scope);
|
||||
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const VariadicTypePack* subVariadic, const VariadicTypePack* superVariadic);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeFunctionInstanceType* subFunctionInstance, const TypeId superTy);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeId subTy, const TypeFunctionInstanceType* superFunctionInstance);
|
||||
SubtypingResult isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const VariadicTypePack* subVariadic,
|
||||
const VariadicTypePack* superVariadic,
|
||||
NotNull<Scope> scope
|
||||
);
|
||||
SubtypingResult isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const TypeFunctionInstanceType* subFunctionInstance,
|
||||
const TypeId superTy,
|
||||
NotNull<Scope> scope
|
||||
);
|
||||
SubtypingResult isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const TypeId subTy,
|
||||
const TypeFunctionInstanceType* superFunctionInstance,
|
||||
NotNull<Scope> scope
|
||||
);
|
||||
|
||||
bool bindGeneric(SubtypingEnvironment& env, TypeId subTp, TypeId superTp);
|
||||
bool bindGeneric(SubtypingEnvironment& env, TypePackId subTp, TypePackId superTp);
|
||||
@ -240,7 +288,7 @@ private:
|
||||
template<typename T, typename Container>
|
||||
TypeId makeAggregateType(const Container& container, TypeId orElse);
|
||||
|
||||
std::pair<TypeId, ErrorVec> handleTypeFunctionReductionResult(const TypeFunctionInstanceType* functionInstance);
|
||||
std::pair<TypeId, ErrorVec> handleTypeFunctionReductionResult(const TypeFunctionInstanceType* functionInstance, NotNull<Scope> scope);
|
||||
|
||||
[[noreturn]] void unexpected(TypeId ty);
|
||||
[[noreturn]] void unexpected(TypePackId tp);
|
||||
|
@ -38,7 +38,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(StudioReportLuauAny, false);
|
||||
LUAU_FASTFLAGVARIABLE(StudioReportLuauAny2, false);
|
||||
LUAU_FASTINTVARIABLE(LuauAnySummaryRecursionLimit, 300);
|
||||
|
||||
LUAU_FASTFLAG(DebugLuauMagicTypes);
|
||||
@ -211,14 +211,17 @@ void AnyTypeSummary::visit(const Scope* scope, AstStatLocal* local, const Module
|
||||
if (!maybeRequire)
|
||||
continue;
|
||||
|
||||
if (isAnyCast(scope, local->values.data[posn], module, builtinTypes))
|
||||
if (std::min(local->values.size - 1, posn) < head.size())
|
||||
{
|
||||
TelemetryTypePair types;
|
||||
if (isAnyCast(scope, local->values.data[posn], module, builtinTypes))
|
||||
{
|
||||
TelemetryTypePair types;
|
||||
|
||||
types.inferredType = toString(head[std::min(local->values.size - 1, posn)]);
|
||||
types.inferredType = toString(head[std::min(local->values.size - 1, posn)]);
|
||||
|
||||
TypeInfo ti{Pattern::Casts, toString(ctxNode), types};
|
||||
typeInfo.push_back(ti);
|
||||
TypeInfo ti{Pattern::Casts, toString(ctxNode), types};
|
||||
typeInfo.push_back(ti);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -292,7 +295,7 @@ void AnyTypeSummary::visit(const Scope* scope, AstStatAssign* assign, const Modu
|
||||
types.annotatedType = toString(tp);
|
||||
|
||||
auto loc = std::min(assign->vars.size - 1, posn);
|
||||
if (head.size() >= assign->vars.size)
|
||||
if (head.size() >= assign->vars.size && posn < head.size())
|
||||
{
|
||||
types.inferredType = toString(head[posn]);
|
||||
}
|
||||
|
@ -144,9 +144,9 @@ static bool checkTypeMatch(TypeId subTy, TypeId superTy, NotNull<Scope> scope, T
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
Subtyping subtyping{builtinTypes, NotNull{typeArena}, NotNull{&normalizer}, NotNull{&iceReporter}, scope};
|
||||
Subtyping subtyping{builtinTypes, NotNull{typeArena}, NotNull{&normalizer}, NotNull{&iceReporter}};
|
||||
|
||||
return subtyping.isSubtype(subTy, superTy).isSubtype;
|
||||
return subtyping.isSubtype(subTy, superTy, scope).isSubtype;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1793,7 +1793,7 @@ InferencePack ConstraintGenerator::checkPack(const ScopePtr& scope, AstExprCall*
|
||||
|
||||
std::vector<std::optional<TypeId>> expectedTypesForCall = getExpectedCallTypesForFunctionOverloads(fnType);
|
||||
|
||||
module->astOriginalCallTypes[call] = fnType;
|
||||
module->astOriginalCallTypes[call->func] = fnType;
|
||||
|
||||
Checkpoint argBeginCheckpoint = checkpoint(this);
|
||||
|
||||
|
@ -45,8 +45,9 @@ LUAU_FASTFLAGVARIABLE(DebugLuauForbidInternalTypes, false)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauForceStrictMode, false)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauForceNonStrictMode, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSourceModuleUpdatedWithSelectedMode, false)
|
||||
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauRunCustomModuleChecks, false)
|
||||
|
||||
LUAU_FASTFLAG(StudioReportLuauAny)
|
||||
LUAU_FASTFLAG(StudioReportLuauAny2)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
@ -511,7 +512,7 @@ CheckResult Frontend::check(const ModuleName& name, std::optional<FrontendOption
|
||||
if (item.name == name)
|
||||
checkResult.lintResult = item.module->lintResult;
|
||||
|
||||
if (FFlag::StudioReportLuauAny && item.options.retainFullTypeGraphs)
|
||||
if (FFlag::StudioReportLuauAny2 && item.options.retainFullTypeGraphs)
|
||||
{
|
||||
if (item.module)
|
||||
{
|
||||
@ -1040,6 +1041,9 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item)
|
||||
item.stats.timeCheck += duration;
|
||||
item.stats.filesStrict += 1;
|
||||
|
||||
if (DFFlag::LuauRunCustomModuleChecks && item.options.customModuleCheck)
|
||||
item.options.customModuleCheck(sourceModule, *moduleForAutocomplete);
|
||||
|
||||
item.module = moduleForAutocomplete;
|
||||
return;
|
||||
}
|
||||
@ -1057,8 +1061,8 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item)
|
||||
item.stats.filesStrict += mode == Mode::Strict;
|
||||
item.stats.filesNonstrict += mode == Mode::Nonstrict;
|
||||
|
||||
if (module == nullptr)
|
||||
throw InternalCompilerError("Frontend::check produced a nullptr module for " + item.name, item.name);
|
||||
if (DFFlag::LuauRunCustomModuleChecks && item.options.customModuleCheck)
|
||||
item.options.customModuleCheck(sourceModule, *module);
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution && mode == Mode::NoCheck)
|
||||
module->errors.clear();
|
||||
|
@ -182,7 +182,7 @@ struct NonStrictTypeChecker
|
||||
, arena(arena)
|
||||
, module(module)
|
||||
, normalizer{arena, builtinTypes, unifierState, /* cache inhabitance */ true}
|
||||
, subtyping{builtinTypes, arena, NotNull(&normalizer), ice, NotNull{module->getModuleScope().get()}}
|
||||
, subtyping{builtinTypes, arena, NotNull(&normalizer), ice}
|
||||
, dfg(dfg)
|
||||
, limits(limits)
|
||||
{
|
||||
@ -531,7 +531,7 @@ struct NonStrictTypeChecker
|
||||
NonStrictContext visit(AstExprCall* call)
|
||||
{
|
||||
NonStrictContext fresh{};
|
||||
TypeId* originalCallTy = module->astOriginalCallTypes.find(call);
|
||||
TypeId* originalCallTy = module->astOriginalCallTypes.find(call->func);
|
||||
if (!originalCallTy)
|
||||
return fresh;
|
||||
|
||||
@ -699,6 +699,7 @@ struct NonStrictTypeChecker
|
||||
// If this fragment of the ast will run time error, return the type that causes this
|
||||
std::optional<TypeId> willRunTimeError(AstExpr* fragment, const NonStrictContext& context)
|
||||
{
|
||||
NotNull<Scope> scope{Luau::findScopeAtPosition(*module, fragment->location.end).get()};
|
||||
DefId def = dfg->getDef(fragment);
|
||||
std::vector<DefId> defs;
|
||||
collectOperands(def, &defs);
|
||||
@ -708,7 +709,7 @@ struct NonStrictTypeChecker
|
||||
{
|
||||
|
||||
TypeId actualType = lookupType(fragment);
|
||||
SubtypingResult r = subtyping.isSubtype(actualType, *contextTy);
|
||||
SubtypingResult r = subtyping.isSubtype(actualType, *contextTy, scope);
|
||||
if (r.normalizationTooComplex)
|
||||
reportError(NormalizationTooComplex{}, fragment->location);
|
||||
if (r.isSubtype)
|
||||
@ -721,6 +722,7 @@ struct NonStrictTypeChecker
|
||||
|
||||
std::optional<TypeId> willRunTimeErrorFunctionDefinition(AstLocal* fragment, const NonStrictContext& context)
|
||||
{
|
||||
NotNull<Scope> scope{Luau::findScopeAtPosition(*module, fragment->location.end).get()};
|
||||
DefId def = dfg->getDef(fragment);
|
||||
std::vector<DefId> defs;
|
||||
collectOperands(def, &defs);
|
||||
@ -728,8 +730,8 @@ struct NonStrictTypeChecker
|
||||
{
|
||||
if (std::optional<TypeId> contextTy = context.find(def))
|
||||
{
|
||||
SubtypingResult r1 = subtyping.isSubtype(builtinTypes->unknownType, *contextTy);
|
||||
SubtypingResult r2 = subtyping.isSubtype(*contextTy, builtinTypes->unknownType);
|
||||
SubtypingResult r1 = subtyping.isSubtype(builtinTypes->unknownType, *contextTy, scope);
|
||||
SubtypingResult r2 = subtyping.isSubtype(*contextTy, builtinTypes->unknownType, scope);
|
||||
if (r1.normalizationTooComplex || r2.normalizationTooComplex)
|
||||
reportError(NormalizationTooComplex{}, fragment->location);
|
||||
bool isUnknown = r1.isSubtype && r2.isSubtype;
|
||||
@ -747,7 +749,7 @@ private:
|
||||
if (!cachedResult)
|
||||
cachedResult = arena->addType(NegationType{baseType});
|
||||
return cachedResult;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
void checkNonStrict(
|
||||
|
@ -3429,9 +3429,9 @@ bool isSubtype(TypeId subTy, TypeId superTy, NotNull<Scope> scope, NotNull<Built
|
||||
// Subtyping under DCR is not implemented using unification!
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
Subtyping subtyping{builtinTypes, NotNull{&arena}, NotNull{&normalizer}, NotNull{&ice}, scope};
|
||||
Subtyping subtyping{builtinTypes, NotNull{&arena}, NotNull{&normalizer}, NotNull{&ice}};
|
||||
|
||||
return subtyping.isSubtype(subTy, superTy).isSubtype;
|
||||
return subtyping.isSubtype(subTy, superTy, scope).isSubtype;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -3451,9 +3451,9 @@ bool isSubtype(TypePackId subPack, TypePackId superPack, NotNull<Scope> scope, N
|
||||
// Subtyping under DCR is not implemented using unification!
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
Subtyping subtyping{builtinTypes, NotNull{&arena}, NotNull{&normalizer}, NotNull{&ice}, scope};
|
||||
Subtyping subtyping{builtinTypes, NotNull{&arena}, NotNull{&normalizer}, NotNull{&ice}};
|
||||
|
||||
return subtyping.isSubtype(subPack, superPack).isSubtype;
|
||||
return subtyping.isSubtype(subPack, superPack, scope).isSubtype;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -28,7 +28,7 @@ OverloadResolver::OverloadResolver(
|
||||
, scope(scope)
|
||||
, ice(reporter)
|
||||
, limits(limits)
|
||||
, subtyping({builtinTypes, arena, normalizer, ice, scope})
|
||||
, subtyping({builtinTypes, arena, normalizer, ice})
|
||||
, callLoc(callLocation)
|
||||
{
|
||||
}
|
||||
@ -41,7 +41,7 @@ std::pair<OverloadResolver::Analysis, TypeId> OverloadResolver::selectOverload(T
|
||||
{
|
||||
Subtyping::Variance variance = subtyping.variance;
|
||||
subtyping.variance = Subtyping::Variance::Contravariant;
|
||||
SubtypingResult r = subtyping.isSubtype(argsPack, ftv->argTypes);
|
||||
SubtypingResult r = subtyping.isSubtype(argsPack, ftv->argTypes, scope);
|
||||
subtyping.variance = variance;
|
||||
|
||||
if (r.isSubtype)
|
||||
@ -92,7 +92,7 @@ void OverloadResolver::resolve(TypeId fnTy, const TypePack* args, AstExpr* selfE
|
||||
|
||||
std::optional<ErrorVec> OverloadResolver::testIsSubtype(const Location& location, TypeId subTy, TypeId superTy)
|
||||
{
|
||||
auto r = subtyping.isSubtype(subTy, superTy);
|
||||
auto r = subtyping.isSubtype(subTy, superTy, scope);
|
||||
ErrorVec errors;
|
||||
|
||||
if (r.normalizationTooComplex)
|
||||
@ -121,7 +121,7 @@ std::optional<ErrorVec> OverloadResolver::testIsSubtype(const Location& location
|
||||
|
||||
std::optional<ErrorVec> OverloadResolver::testIsSubtype(const Location& location, TypePackId subTy, TypePackId superTy)
|
||||
{
|
||||
auto r = subtyping.isSubtype(subTy, superTy);
|
||||
auto r = subtyping.isSubtype(subTy, superTy, scope);
|
||||
ErrorVec errors;
|
||||
|
||||
if (r.normalizationTooComplex)
|
||||
@ -206,7 +206,7 @@ std::pair<OverloadResolver::Analysis, ErrorVec> OverloadResolver::checkOverload_
|
||||
TypePackId typ = arena->addTypePack(*args);
|
||||
|
||||
TypeId prospectiveFunction = arena->addType(FunctionType{typ, builtinTypes->anyTypePack});
|
||||
SubtypingResult sr = subtyping.isSubtype(fnTy, prospectiveFunction);
|
||||
SubtypingResult sr = subtyping.isSubtype(fnTy, prospectiveFunction, scope);
|
||||
|
||||
if (sr.isSubtype)
|
||||
return {Analysis::Ok, {}};
|
||||
@ -250,7 +250,7 @@ std::pair<OverloadResolver::Analysis, ErrorVec> OverloadResolver::checkOverload_
|
||||
// nil, then this overload does not match.
|
||||
for (size_t i = firstUnsatisfiedArgument; i < requiredHead.size(); ++i)
|
||||
{
|
||||
if (!subtyping.isSubtype(builtinTypes->nilType, requiredHead[i]).isSubtype)
|
||||
if (!subtyping.isSubtype(builtinTypes->nilType, requiredHead[i], scope).isSubtype)
|
||||
{
|
||||
auto [minParams, optMaxParams] = getParameterExtents(TxnLog::empty(), fn->argTypes);
|
||||
TypeError error{fnExpr->location, CountMismatch{minParams, optMaxParams, args->head.size(), CountMismatch::Arg, isVariadic}};
|
||||
|
@ -333,28 +333,25 @@ Subtyping::Subtyping(
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<TypeArena> typeArena,
|
||||
NotNull<Normalizer> normalizer,
|
||||
NotNull<InternalErrorReporter> iceReporter,
|
||||
NotNull<Scope> scope
|
||||
NotNull<InternalErrorReporter> iceReporter
|
||||
)
|
||||
: builtinTypes(builtinTypes)
|
||||
, arena(typeArena)
|
||||
, normalizer(normalizer)
|
||||
, iceReporter(iceReporter)
|
||||
, scope(scope)
|
||||
{
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isSubtype(TypeId subTy, TypeId superTy)
|
||||
SubtypingResult Subtyping::isSubtype(TypeId subTy, TypeId superTy, NotNull<Scope> scope)
|
||||
{
|
||||
SubtypingEnvironment env;
|
||||
|
||||
SubtypingResult result = isCovariantWith(env, subTy, superTy);
|
||||
SubtypingResult result = isCovariantWith(env, subTy, superTy, scope);
|
||||
|
||||
for (const auto& [subTy, bounds] : env.mappedGenerics)
|
||||
{
|
||||
const auto& lb = bounds.lowerBound;
|
||||
const auto& ub = bounds.upperBound;
|
||||
|
||||
TypeId lowerBound = makeAggregateType<UnionType>(lb, builtinTypes->neverType);
|
||||
TypeId upperBound = makeAggregateType<IntersectionType>(ub, builtinTypes->unknownType);
|
||||
|
||||
@ -382,7 +379,7 @@ SubtypingResult Subtyping::isSubtype(TypeId subTy, TypeId superTy)
|
||||
result.isSubtype = false;
|
||||
}
|
||||
|
||||
SubtypingResult boundsResult = isCovariantWith(env, lowerBound, upperBound);
|
||||
SubtypingResult boundsResult = isCovariantWith(env, lowerBound, upperBound, scope);
|
||||
boundsResult.reasoning.clear();
|
||||
|
||||
result.andAlso(boundsResult);
|
||||
@ -406,10 +403,10 @@ SubtypingResult Subtyping::isSubtype(TypeId subTy, TypeId superTy)
|
||||
return result;
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isSubtype(TypePackId subTp, TypePackId superTp)
|
||||
SubtypingResult Subtyping::isSubtype(TypePackId subTp, TypePackId superTp, NotNull<Scope> scope)
|
||||
{
|
||||
SubtypingEnvironment env;
|
||||
return isCovariantWith(env, subTp, superTp);
|
||||
return isCovariantWith(env, subTp, superTp, scope);
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::cache(SubtypingEnvironment& env, SubtypingResult result, TypeId subTy, TypeId superTy)
|
||||
@ -443,7 +440,7 @@ struct SeenSetPopper
|
||||
};
|
||||
} // namespace
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId subTy, TypeId superTy)
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId subTy, TypeId superTy, NotNull<Scope> scope)
|
||||
{
|
||||
subTy = follow(subTy);
|
||||
superTy = follow(superTy);
|
||||
@ -501,20 +498,20 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
||||
// tested as though it were its upper bounds. We do not yet support bounded
|
||||
// generics, so the upper bound is always unknown.
|
||||
if (auto subGeneric = get<GenericType>(subTy); subGeneric && subsumes(subGeneric->scope, scope))
|
||||
return isCovariantWith(env, builtinTypes->neverType, superTy);
|
||||
return isCovariantWith(env, builtinTypes->neverType, superTy, scope);
|
||||
if (auto superGeneric = get<GenericType>(superTy); superGeneric && subsumes(superGeneric->scope, scope))
|
||||
return isCovariantWith(env, subTy, builtinTypes->unknownType);
|
||||
return isCovariantWith(env, subTy, builtinTypes->unknownType, scope);
|
||||
|
||||
SubtypingResult result;
|
||||
|
||||
if (auto subUnion = get<UnionType>(subTy))
|
||||
result = isCovariantWith(env, subUnion, superTy);
|
||||
result = isCovariantWith(env, subUnion, superTy, scope);
|
||||
else if (auto superUnion = get<UnionType>(superTy))
|
||||
{
|
||||
result = isCovariantWith(env, subTy, superUnion);
|
||||
result = isCovariantWith(env, subTy, superUnion, scope);
|
||||
if (!result.isSubtype && !result.normalizationTooComplex)
|
||||
{
|
||||
SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy));
|
||||
SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy), scope);
|
||||
if (semantic.isSubtype)
|
||||
{
|
||||
semantic.reasoning.clear();
|
||||
@ -523,13 +520,13 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
||||
}
|
||||
}
|
||||
else if (auto superIntersection = get<IntersectionType>(superTy))
|
||||
result = isCovariantWith(env, subTy, superIntersection);
|
||||
result = isCovariantWith(env, subTy, superIntersection, scope);
|
||||
else if (auto subIntersection = get<IntersectionType>(subTy))
|
||||
{
|
||||
result = isCovariantWith(env, subIntersection, superTy);
|
||||
result = isCovariantWith(env, subIntersection, superTy, scope);
|
||||
if (!result.isSubtype && !result.normalizationTooComplex)
|
||||
{
|
||||
SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy));
|
||||
SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy), scope);
|
||||
if (semantic.isSubtype)
|
||||
{
|
||||
// Clear the semantic reasoning, as any reasonings within
|
||||
@ -550,7 +547,8 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
||||
{
|
||||
// any = unknown | error, so we rewrite this to match.
|
||||
// As per TAPL: A | B <: T iff A <: T && B <: T
|
||||
result = isCovariantWith(env, builtinTypes->unknownType, superTy).andAlso(isCovariantWith(env, builtinTypes->errorType, superTy));
|
||||
result =
|
||||
isCovariantWith(env, builtinTypes->unknownType, superTy, scope).andAlso(isCovariantWith(env, builtinTypes->errorType, superTy, scope));
|
||||
}
|
||||
else if (get<UnknownType>(superTy))
|
||||
{
|
||||
@ -566,15 +564,15 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
||||
else if (get<ErrorType>(superTy))
|
||||
result = {false};
|
||||
else if (get<ErrorType>(subTy))
|
||||
result = {false};
|
||||
result = {true};
|
||||
else if (auto p = get2<NegationType, NegationType>(subTy, superTy))
|
||||
result = isCovariantWith(env, p.first->ty, p.second->ty).withBothComponent(TypePath::TypeField::Negated);
|
||||
result = isCovariantWith(env, p.first->ty, p.second->ty, scope).withBothComponent(TypePath::TypeField::Negated);
|
||||
else if (auto subNegation = get<NegationType>(subTy))
|
||||
{
|
||||
result = isCovariantWith(env, subNegation, superTy);
|
||||
result = isCovariantWith(env, subNegation, superTy, scope);
|
||||
if (!result.isSubtype && !result.normalizationTooComplex)
|
||||
{
|
||||
SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy));
|
||||
SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy), scope);
|
||||
if (semantic.isSubtype)
|
||||
{
|
||||
semantic.reasoning.clear();
|
||||
@ -584,10 +582,10 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
||||
}
|
||||
else if (auto superNegation = get<NegationType>(superTy))
|
||||
{
|
||||
result = isCovariantWith(env, subTy, superNegation);
|
||||
result = isCovariantWith(env, subTy, superNegation, scope);
|
||||
if (!result.isSubtype && !result.normalizationTooComplex)
|
||||
{
|
||||
SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy));
|
||||
SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy), scope);
|
||||
if (semantic.isSubtype)
|
||||
{
|
||||
semantic.reasoning.clear();
|
||||
@ -600,14 +598,14 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
||||
if (auto substSubTy = env.applyMappedGenerics(builtinTypes, arena, subTy))
|
||||
subTypeFunctionInstance = get<TypeFunctionInstanceType>(*substSubTy);
|
||||
|
||||
result = isCovariantWith(env, subTypeFunctionInstance, superTy);
|
||||
result = isCovariantWith(env, subTypeFunctionInstance, superTy, scope);
|
||||
}
|
||||
else if (auto superTypeFunctionInstance = get<TypeFunctionInstanceType>(superTy))
|
||||
{
|
||||
if (auto substSuperTy = env.applyMappedGenerics(builtinTypes, arena, superTy))
|
||||
superTypeFunctionInstance = get<TypeFunctionInstanceType>(*substSuperTy);
|
||||
|
||||
result = isCovariantWith(env, subTy, superTypeFunctionInstance);
|
||||
result = isCovariantWith(env, subTy, superTypeFunctionInstance, scope);
|
||||
}
|
||||
else if (auto subGeneric = get<GenericType>(subTy); subGeneric && variance == Variance::Covariant)
|
||||
{
|
||||
@ -622,41 +620,41 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
||||
result.isCacheable = false;
|
||||
}
|
||||
else if (auto p = get2<PrimitiveType, PrimitiveType>(subTy, superTy))
|
||||
result = isCovariantWith(env, p);
|
||||
result = isCovariantWith(env, p, scope);
|
||||
else if (auto p = get2<SingletonType, PrimitiveType>(subTy, superTy))
|
||||
result = isCovariantWith(env, p);
|
||||
result = isCovariantWith(env, p, scope);
|
||||
else if (auto p = get2<SingletonType, SingletonType>(subTy, superTy))
|
||||
result = isCovariantWith(env, p);
|
||||
result = isCovariantWith(env, p, scope);
|
||||
else if (auto p = get2<FunctionType, PrimitiveType>(subTy, superTy))
|
||||
{
|
||||
auto [subFunction, superPrimitive] = p;
|
||||
result.isSubtype = superPrimitive->type == PrimitiveType::Function;
|
||||
}
|
||||
else if (auto p = get2<FunctionType, FunctionType>(subTy, superTy))
|
||||
result = isCovariantWith(env, p);
|
||||
result = isCovariantWith(env, p, scope);
|
||||
else if (auto p = get2<TableType, TableType>(subTy, superTy))
|
||||
result = isCovariantWith(env, p);
|
||||
result = isCovariantWith(env, p, scope);
|
||||
else if (auto p = get2<MetatableType, MetatableType>(subTy, superTy))
|
||||
result = isCovariantWith(env, p);
|
||||
result = isCovariantWith(env, p, scope);
|
||||
else if (auto p = get2<MetatableType, TableType>(subTy, superTy))
|
||||
result = isCovariantWith(env, p);
|
||||
result = isCovariantWith(env, p, scope);
|
||||
else if (auto p = get2<ClassType, ClassType>(subTy, superTy))
|
||||
result = isCovariantWith(env, p);
|
||||
result = isCovariantWith(env, p, scope);
|
||||
else if (auto p = get2<ClassType, TableType>(subTy, superTy))
|
||||
result = isCovariantWith(env, subTy, p.first, superTy, p.second);
|
||||
result = isCovariantWith(env, subTy, p.first, superTy, p.second, scope);
|
||||
else if (auto p = get2<TableType, PrimitiveType>(subTy, superTy))
|
||||
result = isCovariantWith(env, p);
|
||||
result = isCovariantWith(env, p, scope);
|
||||
else if (auto p = get2<PrimitiveType, TableType>(subTy, superTy))
|
||||
result = isCovariantWith(env, p);
|
||||
result = isCovariantWith(env, p, scope);
|
||||
else if (auto p = get2<SingletonType, TableType>(subTy, superTy))
|
||||
result = isCovariantWith(env, p);
|
||||
result = isCovariantWith(env, p, scope);
|
||||
|
||||
assertReasoningValid(subTy, superTy, result, builtinTypes);
|
||||
|
||||
return cache(env, result, subTy, superTy);
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId subTp, TypePackId superTp)
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId subTp, TypePackId superTp, NotNull<Scope> scope)
|
||||
{
|
||||
subTp = follow(subTp);
|
||||
superTp = follow(superTp);
|
||||
@ -675,7 +673,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId
|
||||
// Match head types pairwise
|
||||
|
||||
for (size_t i = 0; i < headSize; ++i)
|
||||
results.push_back(isCovariantWith(env, subHead[i], superHead[i]).withBothComponent(TypePath::Index{i}));
|
||||
results.push_back(isCovariantWith(env, subHead[i], superHead[i], scope).withBothComponent(TypePath::Index{i}));
|
||||
|
||||
// Handle mismatched head sizes
|
||||
|
||||
@ -686,7 +684,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId
|
||||
if (auto vt = get<VariadicTypePack>(*subTail))
|
||||
{
|
||||
for (size_t i = headSize; i < superHead.size(); ++i)
|
||||
results.push_back(isCovariantWith(env, vt->ty, superHead[i])
|
||||
results.push_back(isCovariantWith(env, vt->ty, superHead[i], scope)
|
||||
.withSubPath(TypePath::PathBuilder().tail().variadic().build())
|
||||
.withSuperComponent(TypePath::Index{i}));
|
||||
}
|
||||
@ -704,7 +702,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId
|
||||
|
||||
if (TypePackId* other = env.mappedGenericPacks.find(*subTail))
|
||||
// TODO: TypePath can't express "slice of a pack + its tail".
|
||||
results.push_back(isCovariantWith(env, *other, superTailPack).withSubComponent(TypePath::PackField::Tail));
|
||||
results.push_back(isCovariantWith(env, *other, superTailPack, scope).withSubComponent(TypePath::PackField::Tail));
|
||||
else
|
||||
env.mappedGenericPacks.try_insert(*subTail, superTailPack);
|
||||
|
||||
@ -741,7 +739,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId
|
||||
if (auto vt = get<VariadicTypePack>(*superTail))
|
||||
{
|
||||
for (size_t i = headSize; i < subHead.size(); ++i)
|
||||
results.push_back(isCovariantWith(env, subHead[i], vt->ty)
|
||||
results.push_back(isCovariantWith(env, subHead[i], vt->ty, scope)
|
||||
.withSubComponent(TypePath::Index{i})
|
||||
.withSuperPath(TypePath::PathBuilder().tail().variadic().build()));
|
||||
}
|
||||
@ -759,7 +757,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId
|
||||
|
||||
if (TypePackId* other = env.mappedGenericPacks.find(*superTail))
|
||||
// TODO: TypePath can't express "slice of a pack + its tail".
|
||||
results.push_back(isContravariantWith(env, subTailPack, *other).withSuperComponent(TypePath::PackField::Tail));
|
||||
results.push_back(isContravariantWith(env, subTailPack, *other, scope).withSuperComponent(TypePath::PackField::Tail));
|
||||
else
|
||||
env.mappedGenericPacks.try_insert(*superTail, subTailPack);
|
||||
|
||||
@ -794,7 +792,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId
|
||||
{
|
||||
// Variadic component is added by the isCovariantWith
|
||||
// implementation; no need to add it here.
|
||||
results.push_back(isCovariantWith(env, p).withBothComponent(TypePath::PackField::Tail));
|
||||
results.push_back(isCovariantWith(env, p, scope).withBothComponent(TypePath::PackField::Tail));
|
||||
}
|
||||
else if (auto p = get2<GenericTypePack, GenericTypePack>(*subTail, *superTail))
|
||||
{
|
||||
@ -899,11 +897,11 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId
|
||||
}
|
||||
|
||||
template<typename SubTy, typename SuperTy>
|
||||
SubtypingResult Subtyping::isContravariantWith(SubtypingEnvironment& env, SubTy&& subTy, SuperTy&& superTy)
|
||||
SubtypingResult Subtyping::isContravariantWith(SubtypingEnvironment& env, SubTy&& subTy, SuperTy&& superTy, NotNull<Scope> scope)
|
||||
{
|
||||
VarianceFlipper vf{&variance};
|
||||
|
||||
SubtypingResult result = isCovariantWith(env, superTy, subTy);
|
||||
SubtypingResult result = isCovariantWith(env, superTy, subTy, scope);
|
||||
if (result.reasoning.empty())
|
||||
result.reasoning.insert(SubtypingReasoning{TypePath::kEmpty, TypePath::kEmpty, SubtypingVariance::Contravariant});
|
||||
else
|
||||
@ -931,9 +929,9 @@ SubtypingResult Subtyping::isContravariantWith(SubtypingEnvironment& env, SubTy&
|
||||
}
|
||||
|
||||
template<typename SubTy, typename SuperTy>
|
||||
SubtypingResult Subtyping::isInvariantWith(SubtypingEnvironment& env, SubTy&& subTy, SuperTy&& superTy)
|
||||
SubtypingResult Subtyping::isInvariantWith(SubtypingEnvironment& env, SubTy&& subTy, SuperTy&& superTy, NotNull<Scope> scope)
|
||||
{
|
||||
SubtypingResult result = isCovariantWith(env, subTy, superTy).andAlso(isContravariantWith(env, subTy, superTy));
|
||||
SubtypingResult result = isCovariantWith(env, subTy, superTy, scope).andAlso(isContravariantWith(env, subTy, superTy, scope));
|
||||
|
||||
if (result.reasoning.empty())
|
||||
result.reasoning.insert(SubtypingReasoning{TypePath::kEmpty, TypePath::kEmpty, SubtypingVariance::Invariant});
|
||||
@ -948,19 +946,19 @@ SubtypingResult Subtyping::isInvariantWith(SubtypingEnvironment& env, SubTy&& su
|
||||
}
|
||||
|
||||
template<typename SubTy, typename SuperTy>
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TryPair<const SubTy*, const SuperTy*>& pair)
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TryPair<const SubTy*, const SuperTy*>& pair, NotNull<Scope> scope)
|
||||
{
|
||||
return isCovariantWith(env, pair.first, pair.second);
|
||||
return isCovariantWith(env, pair.first, pair.second, scope);
|
||||
}
|
||||
|
||||
template<typename SubTy, typename SuperTy>
|
||||
SubtypingResult Subtyping::isContravariantWith(SubtypingEnvironment& env, const TryPair<const SubTy*, const SuperTy*>& pair)
|
||||
SubtypingResult Subtyping::isContravariantWith(SubtypingEnvironment& env, const TryPair<const SubTy*, const SuperTy*>& pair, NotNull<Scope> scope)
|
||||
{
|
||||
return isContravariantWith(env, pair.first, pair.second);
|
||||
return isContravariantWith(env, pair.first, pair.second, scope);
|
||||
}
|
||||
|
||||
template<typename SubTy, typename SuperTy>
|
||||
SubtypingResult Subtyping::isInvariantWith(SubtypingEnvironment& env, const TryPair<const SubTy*, const SuperTy*>& pair)
|
||||
SubtypingResult Subtyping::isInvariantWith(SubtypingEnvironment& env, const TryPair<const SubTy*, const SuperTy*>& pair, NotNull<Scope> scope)
|
||||
{
|
||||
return isInvariantWith(env, pair.first, pair.second);
|
||||
}
|
||||
@ -996,13 +994,13 @@ SubtypingResult Subtyping::isInvariantWith(SubtypingEnvironment& env, const TryP
|
||||
* other just asks for boolean ~ 'b. We can dispatch this and only commit
|
||||
* boolean ~ 'b. This constraint does not teach us anything about 'a.
|
||||
*/
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const UnionType* superUnion)
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const UnionType* superUnion, NotNull<Scope> scope)
|
||||
{
|
||||
// As per TAPL: T <: A | B iff T <: A || T <: B
|
||||
|
||||
for (TypeId ty : superUnion)
|
||||
{
|
||||
SubtypingResult next = isCovariantWith(env, subTy, ty);
|
||||
SubtypingResult next = isCovariantWith(env, subTy, ty, scope);
|
||||
if (next.isSubtype)
|
||||
return SubtypingResult{true};
|
||||
}
|
||||
@ -1015,37 +1013,37 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
||||
return SubtypingResult{false};
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const UnionType* subUnion, TypeId superTy)
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const UnionType* subUnion, TypeId superTy, NotNull<Scope> scope)
|
||||
{
|
||||
// As per TAPL: A | B <: T iff A <: T && B <: T
|
||||
std::vector<SubtypingResult> subtypings;
|
||||
size_t i = 0;
|
||||
for (TypeId ty : subUnion)
|
||||
subtypings.push_back(isCovariantWith(env, ty, superTy).withSubComponent(TypePath::Index{i++}));
|
||||
subtypings.push_back(isCovariantWith(env, ty, superTy, scope).withSubComponent(TypePath::Index{i++}));
|
||||
return SubtypingResult::all(subtypings);
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const IntersectionType* superIntersection)
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const IntersectionType* superIntersection, NotNull<Scope> scope)
|
||||
{
|
||||
// As per TAPL: T <: A & B iff T <: A && T <: B
|
||||
std::vector<SubtypingResult> subtypings;
|
||||
size_t i = 0;
|
||||
for (TypeId ty : superIntersection)
|
||||
subtypings.push_back(isCovariantWith(env, subTy, ty).withSuperComponent(TypePath::Index{i++}));
|
||||
subtypings.push_back(isCovariantWith(env, subTy, ty, scope).withSuperComponent(TypePath::Index{i++}));
|
||||
return SubtypingResult::all(subtypings);
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const IntersectionType* subIntersection, TypeId superTy)
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const IntersectionType* subIntersection, TypeId superTy, NotNull<Scope> scope)
|
||||
{
|
||||
// As per TAPL: A & B <: T iff A <: T || B <: T
|
||||
std::vector<SubtypingResult> subtypings;
|
||||
size_t i = 0;
|
||||
for (TypeId ty : subIntersection)
|
||||
subtypings.push_back(isCovariantWith(env, ty, superTy).withSubComponent(TypePath::Index{i++}));
|
||||
subtypings.push_back(isCovariantWith(env, ty, superTy, scope).withSubComponent(TypePath::Index{i++}));
|
||||
return SubtypingResult::any(subtypings);
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const NegationType* subNegation, TypeId superTy)
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const NegationType* subNegation, TypeId superTy, NotNull<Scope> scope)
|
||||
{
|
||||
TypeId negatedTy = follow(subNegation->ty);
|
||||
|
||||
@ -1057,17 +1055,17 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Nega
|
||||
if (is<NeverType>(negatedTy))
|
||||
{
|
||||
// ¬never ~ unknown
|
||||
result = isCovariantWith(env, builtinTypes->unknownType, superTy).withSubComponent(TypePath::TypeField::Negated);
|
||||
result = isCovariantWith(env, builtinTypes->unknownType, superTy, scope).withSubComponent(TypePath::TypeField::Negated);
|
||||
}
|
||||
else if (is<UnknownType>(negatedTy))
|
||||
{
|
||||
// ¬unknown ~ never
|
||||
result = isCovariantWith(env, builtinTypes->neverType, superTy).withSubComponent(TypePath::TypeField::Negated);
|
||||
result = isCovariantWith(env, builtinTypes->neverType, superTy, scope).withSubComponent(TypePath::TypeField::Negated);
|
||||
}
|
||||
else if (is<AnyType>(negatedTy))
|
||||
{
|
||||
// ¬any ~ any
|
||||
result = isCovariantWith(env, negatedTy, superTy).withSubComponent(TypePath::TypeField::Negated);
|
||||
result = isCovariantWith(env, negatedTy, superTy, scope).withSubComponent(TypePath::TypeField::Negated);
|
||||
}
|
||||
else if (auto u = get<UnionType>(negatedTy))
|
||||
{
|
||||
@ -1078,11 +1076,11 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Nega
|
||||
for (TypeId ty : u)
|
||||
{
|
||||
if (auto negatedPart = get<NegationType>(follow(ty)))
|
||||
subtypings.push_back(isCovariantWith(env, negatedPart->ty, superTy).withSubComponent(TypePath::TypeField::Negated));
|
||||
subtypings.push_back(isCovariantWith(env, negatedPart->ty, superTy, scope).withSubComponent(TypePath::TypeField::Negated));
|
||||
else
|
||||
{
|
||||
NegationType negatedTmp{ty};
|
||||
subtypings.push_back(isCovariantWith(env, &negatedTmp, superTy));
|
||||
subtypings.push_back(isCovariantWith(env, &negatedTmp, superTy, scope));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1097,11 +1095,11 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Nega
|
||||
for (TypeId ty : i)
|
||||
{
|
||||
if (auto negatedPart = get<NegationType>(follow(ty)))
|
||||
subtypings.push_back(isCovariantWith(env, negatedPart->ty, superTy).withSubComponent(TypePath::TypeField::Negated));
|
||||
subtypings.push_back(isCovariantWith(env, negatedPart->ty, superTy, scope).withSubComponent(TypePath::TypeField::Negated));
|
||||
else
|
||||
{
|
||||
NegationType negatedTmp{ty};
|
||||
subtypings.push_back(isCovariantWith(env, &negatedTmp, superTy));
|
||||
subtypings.push_back(isCovariantWith(env, &negatedTmp, superTy, scope));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1121,7 +1119,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Nega
|
||||
return result;
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TypeId subTy, const NegationType* superNegation)
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TypeId subTy, const NegationType* superNegation, NotNull<Scope> scope)
|
||||
{
|
||||
TypeId negatedTy = follow(superNegation->ty);
|
||||
|
||||
@ -1130,17 +1128,17 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Type
|
||||
if (is<NeverType>(negatedTy))
|
||||
{
|
||||
// ¬never ~ unknown
|
||||
result = isCovariantWith(env, subTy, builtinTypes->unknownType);
|
||||
result = isCovariantWith(env, subTy, builtinTypes->unknownType, scope);
|
||||
}
|
||||
else if (is<UnknownType>(negatedTy))
|
||||
{
|
||||
// ¬unknown ~ never
|
||||
result = isCovariantWith(env, subTy, builtinTypes->neverType);
|
||||
result = isCovariantWith(env, subTy, builtinTypes->neverType, scope);
|
||||
}
|
||||
else if (is<AnyType>(negatedTy))
|
||||
{
|
||||
// ¬any ~ any
|
||||
result = isSubtype(subTy, negatedTy);
|
||||
result = isSubtype(subTy, negatedTy, scope);
|
||||
}
|
||||
else if (auto u = get<UnionType>(negatedTy))
|
||||
{
|
||||
@ -1151,11 +1149,11 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Type
|
||||
for (TypeId ty : u)
|
||||
{
|
||||
if (auto negatedPart = get<NegationType>(follow(ty)))
|
||||
subtypings.push_back(isCovariantWith(env, subTy, negatedPart->ty));
|
||||
subtypings.push_back(isCovariantWith(env, subTy, negatedPart->ty, scope));
|
||||
else
|
||||
{
|
||||
NegationType negatedTmp{ty};
|
||||
subtypings.push_back(isCovariantWith(env, subTy, &negatedTmp));
|
||||
subtypings.push_back(isCovariantWith(env, subTy, &negatedTmp, scope));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1170,11 +1168,11 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Type
|
||||
for (TypeId ty : i)
|
||||
{
|
||||
if (auto negatedPart = get<NegationType>(follow(ty)))
|
||||
subtypings.push_back(isCovariantWith(env, subTy, negatedPart->ty));
|
||||
subtypings.push_back(isCovariantWith(env, subTy, negatedPart->ty, scope));
|
||||
else
|
||||
{
|
||||
NegationType negatedTmp{ty};
|
||||
subtypings.push_back(isCovariantWith(env, subTy, &negatedTmp));
|
||||
subtypings.push_back(isCovariantWith(env, subTy, &negatedTmp, scope));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1218,7 +1216,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Type
|
||||
else if (auto p = get2<SingletonType, SingletonType>(subTy, negatedTy))
|
||||
result = {*p.first != *p.second};
|
||||
else if (auto p = get2<ClassType, ClassType>(subTy, negatedTy))
|
||||
result = SubtypingResult::negate(isCovariantWith(env, p.first, p.second));
|
||||
result = SubtypingResult::negate(isCovariantWith(env, p.first, p.second, scope));
|
||||
else if (get2<FunctionType, ClassType>(subTy, negatedTy))
|
||||
result = {true};
|
||||
else if (is<ErrorType, FunctionType, TableType, MetatableType>(negatedTy))
|
||||
@ -1229,12 +1227,22 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Type
|
||||
return result.withSuperComponent(TypePath::TypeField::Negated);
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const PrimitiveType* subPrim, const PrimitiveType* superPrim)
|
||||
SubtypingResult Subtyping::isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const PrimitiveType* subPrim,
|
||||
const PrimitiveType* superPrim,
|
||||
NotNull<Scope> scope
|
||||
)
|
||||
{
|
||||
return {subPrim->type == superPrim->type};
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const SingletonType* subSingleton, const PrimitiveType* superPrim)
|
||||
SubtypingResult Subtyping::isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const SingletonType* subSingleton,
|
||||
const PrimitiveType* superPrim,
|
||||
NotNull<Scope> scope
|
||||
)
|
||||
{
|
||||
if (get<StringSingleton>(subSingleton) && superPrim->type == PrimitiveType::String)
|
||||
return {true};
|
||||
@ -1244,12 +1252,17 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Sing
|
||||
return {false};
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const SingletonType* subSingleton, const SingletonType* superSingleton)
|
||||
SubtypingResult Subtyping::isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const SingletonType* subSingleton,
|
||||
const SingletonType* superSingleton,
|
||||
NotNull<Scope> scope
|
||||
)
|
||||
{
|
||||
return {*subSingleton == *superSingleton};
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TableType* subTable, const TableType* superTable)
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TableType* subTable, const TableType* superTable, NotNull<Scope> scope)
|
||||
{
|
||||
SubtypingResult result{true};
|
||||
|
||||
@ -1260,23 +1273,23 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Tabl
|
||||
{
|
||||
std::vector<SubtypingResult> results;
|
||||
if (auto subIter = subTable->props.find(name); subIter != subTable->props.end())
|
||||
results.push_back(isCovariantWith(env, subIter->second, superProp, name));
|
||||
results.push_back(isCovariantWith(env, subIter->second, superProp, name, scope));
|
||||
else if (subTable->indexer)
|
||||
{
|
||||
if (isCovariantWith(env, builtinTypes->stringType, subTable->indexer->indexType).isSubtype)
|
||||
if (isCovariantWith(env, builtinTypes->stringType, subTable->indexer->indexType, scope).isSubtype)
|
||||
{
|
||||
if (superProp.isShared())
|
||||
results.push_back(isInvariantWith(env, subTable->indexer->indexResultType, superProp.type())
|
||||
results.push_back(isInvariantWith(env, subTable->indexer->indexResultType, superProp.type(), scope)
|
||||
.withSubComponent(TypePath::TypeField::IndexResult)
|
||||
.withSuperComponent(TypePath::Property::read(name)));
|
||||
else
|
||||
{
|
||||
if (superProp.readTy)
|
||||
results.push_back(isCovariantWith(env, subTable->indexer->indexResultType, *superProp.readTy)
|
||||
results.push_back(isCovariantWith(env, subTable->indexer->indexResultType, *superProp.readTy, scope)
|
||||
.withSubComponent(TypePath::TypeField::IndexResult)
|
||||
.withSuperComponent(TypePath::Property::read(name)));
|
||||
if (superProp.writeTy)
|
||||
results.push_back(isContravariantWith(env, subTable->indexer->indexResultType, *superProp.writeTy)
|
||||
results.push_back(isContravariantWith(env, subTable->indexer->indexResultType, *superProp.writeTy, scope)
|
||||
.withSubComponent(TypePath::TypeField::IndexResult)
|
||||
.withSuperComponent(TypePath::Property::write(name)));
|
||||
}
|
||||
@ -1292,7 +1305,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Tabl
|
||||
if (superTable->indexer)
|
||||
{
|
||||
if (subTable->indexer)
|
||||
result.andAlso(isInvariantWith(env, *subTable->indexer, *superTable->indexer));
|
||||
result.andAlso(isInvariantWith(env, *subTable->indexer, *superTable->indexer, scope));
|
||||
else
|
||||
return {false};
|
||||
}
|
||||
@ -1300,13 +1313,13 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Tabl
|
||||
return result;
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const MetatableType* superMt)
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const MetatableType* superMt, NotNull<Scope> scope)
|
||||
{
|
||||
return isCovariantWith(env, subMt->table, superMt->table)
|
||||
.andAlso(isCovariantWith(env, subMt->metatable, superMt->metatable).withBothComponent(TypePath::TypeField::Metatable));
|
||||
return isCovariantWith(env, subMt->table, superMt->table, scope)
|
||||
.andAlso(isCovariantWith(env, subMt->metatable, superMt->metatable, scope).withBothComponent(TypePath::TypeField::Metatable));
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const TableType* superTable)
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const TableType* superTable, NotNull<Scope> scope)
|
||||
{
|
||||
if (auto subTable = get<TableType>(follow(subMt->table)))
|
||||
{
|
||||
@ -1319,7 +1332,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Meta
|
||||
// that the metatable isn't a subtype of the table, even though they have
|
||||
// compatible properties/shapes. We'll revisit this later when we have a
|
||||
// better understanding of how important this is.
|
||||
return isCovariantWith(env, subTable, superTable);
|
||||
return isCovariantWith(env, subTable, superTable, scope);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1328,7 +1341,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Meta
|
||||
}
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const ClassType* subClass, const ClassType* superClass)
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const ClassType* subClass, const ClassType* superClass, NotNull<Scope> scope)
|
||||
{
|
||||
return {isSubclass(subClass, superClass)};
|
||||
}
|
||||
@ -1338,7 +1351,8 @@ SubtypingResult Subtyping::isCovariantWith(
|
||||
TypeId subTy,
|
||||
const ClassType* subClass,
|
||||
TypeId superTy,
|
||||
const TableType* superTable
|
||||
const TableType* superTable,
|
||||
NotNull<Scope> scope
|
||||
)
|
||||
{
|
||||
SubtypingResult result{true};
|
||||
@ -1349,7 +1363,7 @@ SubtypingResult Subtyping::isCovariantWith(
|
||||
{
|
||||
if (auto classProp = lookupClassProp(subClass, name))
|
||||
{
|
||||
result.andAlso(isCovariantWith(env, *classProp, prop, name));
|
||||
result.andAlso(isCovariantWith(env, *classProp, prop, name, scope));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1363,19 +1377,26 @@ SubtypingResult Subtyping::isCovariantWith(
|
||||
return result;
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const FunctionType* subFunction, const FunctionType* superFunction)
|
||||
SubtypingResult Subtyping::isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const FunctionType* subFunction,
|
||||
const FunctionType* superFunction,
|
||||
NotNull<Scope> scope
|
||||
)
|
||||
{
|
||||
SubtypingResult result;
|
||||
{
|
||||
result.orElse(isContravariantWith(env, subFunction->argTypes, superFunction->argTypes).withBothComponent(TypePath::PackField::Arguments));
|
||||
result.orElse(
|
||||
isContravariantWith(env, subFunction->argTypes, superFunction->argTypes, scope).withBothComponent(TypePath::PackField::Arguments)
|
||||
);
|
||||
}
|
||||
|
||||
result.andAlso(isCovariantWith(env, subFunction->retTypes, superFunction->retTypes).withBothComponent(TypePath::PackField::Returns));
|
||||
result.andAlso(isCovariantWith(env, subFunction->retTypes, superFunction->retTypes, scope).withBothComponent(TypePath::PackField::Returns));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TableType* subTable, const PrimitiveType* superPrim)
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TableType* subTable, const PrimitiveType* superPrim, NotNull<Scope> scope)
|
||||
{
|
||||
SubtypingResult result{false};
|
||||
if (superPrim->type == PrimitiveType::Table)
|
||||
@ -1384,7 +1405,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Tabl
|
||||
return result;
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const PrimitiveType* subPrim, const TableType* superTable)
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const PrimitiveType* subPrim, const TableType* superTable, NotNull<Scope> scope)
|
||||
{
|
||||
SubtypingResult result{false};
|
||||
if (subPrim->type == PrimitiveType::String)
|
||||
@ -1397,7 +1418,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Prim
|
||||
{
|
||||
if (auto stringTable = get<TableType>(it->second.type()))
|
||||
result.orElse(
|
||||
isCovariantWith(env, stringTable, superTable).withSubPath(TypePath::PathBuilder().mt().readProp("__index").build())
|
||||
isCovariantWith(env, stringTable, superTable, scope).withSubPath(TypePath::PathBuilder().mt().readProp("__index").build())
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1412,7 +1433,12 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Prim
|
||||
return result;
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const SingletonType* subSingleton, const TableType* superTable)
|
||||
SubtypingResult Subtyping::isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const SingletonType* subSingleton,
|
||||
const TableType* superTable,
|
||||
NotNull<Scope> scope
|
||||
)
|
||||
{
|
||||
SubtypingResult result{false};
|
||||
if (auto stringleton = get<StringSingleton>(subSingleton))
|
||||
@ -1425,7 +1451,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Sing
|
||||
{
|
||||
if (auto stringTable = get<TableType>(it->second.type()))
|
||||
result.orElse(
|
||||
isCovariantWith(env, stringTable, superTable).withSubPath(TypePath::PathBuilder().mt().readProp("__index").build())
|
||||
isCovariantWith(env, stringTable, superTable, scope).withSubPath(TypePath::PathBuilder().mt().readProp("__index").build())
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1434,25 +1460,38 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Sing
|
||||
return result;
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TableIndexer& subIndexer, const TableIndexer& superIndexer)
|
||||
SubtypingResult Subtyping::isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const TableIndexer& subIndexer,
|
||||
const TableIndexer& superIndexer,
|
||||
NotNull<Scope> scope
|
||||
)
|
||||
{
|
||||
return isInvariantWith(env, subIndexer.indexType, superIndexer.indexType)
|
||||
return isInvariantWith(env, subIndexer.indexType, superIndexer.indexType, scope)
|
||||
.withBothComponent(TypePath::TypeField::IndexLookup)
|
||||
.andAlso(isInvariantWith(env, subIndexer.indexResultType, superIndexer.indexResultType).withBothComponent(TypePath::TypeField::IndexResult));
|
||||
.andAlso(
|
||||
isInvariantWith(env, subIndexer.indexResultType, superIndexer.indexResultType, scope).withBothComponent(TypePath::TypeField::IndexResult)
|
||||
);
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Property& subProp, const Property& superProp, const std::string& name)
|
||||
SubtypingResult Subtyping::isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const Property& subProp,
|
||||
const Property& superProp,
|
||||
const std::string& name,
|
||||
NotNull<Scope> scope
|
||||
)
|
||||
{
|
||||
SubtypingResult res{true};
|
||||
|
||||
if (superProp.isShared() && subProp.isShared())
|
||||
res.andAlso(isInvariantWith(env, subProp.type(), superProp.type()).withBothComponent(TypePath::Property::read(name)));
|
||||
res.andAlso(isInvariantWith(env, subProp.type(), superProp.type(), scope).withBothComponent(TypePath::Property::read(name)));
|
||||
else
|
||||
{
|
||||
if (superProp.readTy.has_value() && subProp.readTy.has_value())
|
||||
res.andAlso(isCovariantWith(env, *subProp.readTy, *superProp.readTy).withBothComponent(TypePath::Property::read(name)));
|
||||
res.andAlso(isCovariantWith(env, *subProp.readTy, *superProp.readTy, scope).withBothComponent(TypePath::Property::read(name)));
|
||||
if (superProp.writeTy.has_value() && subProp.writeTy.has_value())
|
||||
res.andAlso(isContravariantWith(env, *subProp.writeTy, *superProp.writeTy).withBothComponent(TypePath::Property::write(name)));
|
||||
res.andAlso(isContravariantWith(env, *subProp.writeTy, *superProp.writeTy, scope).withBothComponent(TypePath::Property::write(name)));
|
||||
|
||||
if (superProp.isReadWrite())
|
||||
{
|
||||
@ -1469,29 +1508,37 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Prop
|
||||
SubtypingResult Subtyping::isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const std::shared_ptr<const NormalizedType>& subNorm,
|
||||
const std::shared_ptr<const NormalizedType>& superNorm
|
||||
const std::shared_ptr<const NormalizedType>& superNorm,
|
||||
NotNull<Scope> scope
|
||||
)
|
||||
{
|
||||
if (!subNorm || !superNorm)
|
||||
return {false, true};
|
||||
|
||||
SubtypingResult result = isCovariantWith(env, subNorm->tops, superNorm->tops);
|
||||
result.andAlso(isCovariantWith(env, subNorm->booleans, superNorm->booleans));
|
||||
result.andAlso(isCovariantWith(env, subNorm->classes, superNorm->classes).orElse(isCovariantWith(env, subNorm->classes, superNorm->tables)));
|
||||
result.andAlso(isCovariantWith(env, subNorm->errors, superNorm->errors));
|
||||
result.andAlso(isCovariantWith(env, subNorm->nils, superNorm->nils));
|
||||
result.andAlso(isCovariantWith(env, subNorm->numbers, superNorm->numbers));
|
||||
result.andAlso(isCovariantWith(env, subNorm->strings, superNorm->strings));
|
||||
result.andAlso(isCovariantWith(env, subNorm->strings, superNorm->tables));
|
||||
result.andAlso(isCovariantWith(env, subNorm->threads, superNorm->threads));
|
||||
result.andAlso(isCovariantWith(env, subNorm->buffers, superNorm->buffers));
|
||||
result.andAlso(isCovariantWith(env, subNorm->tables, superNorm->tables));
|
||||
result.andAlso(isCovariantWith(env, subNorm->functions, superNorm->functions));
|
||||
SubtypingResult result = isCovariantWith(env, subNorm->tops, superNorm->tops, scope);
|
||||
result.andAlso(isCovariantWith(env, subNorm->booleans, superNorm->booleans, scope));
|
||||
result.andAlso(
|
||||
isCovariantWith(env, subNorm->classes, superNorm->classes, scope).orElse(isCovariantWith(env, subNorm->classes, superNorm->tables, scope))
|
||||
);
|
||||
result.andAlso(isCovariantWith(env, subNorm->errors, superNorm->errors, scope));
|
||||
result.andAlso(isCovariantWith(env, subNorm->nils, superNorm->nils, scope));
|
||||
result.andAlso(isCovariantWith(env, subNorm->numbers, superNorm->numbers, scope));
|
||||
result.andAlso(isCovariantWith(env, subNorm->strings, superNorm->strings, scope));
|
||||
result.andAlso(isCovariantWith(env, subNorm->strings, superNorm->tables, scope));
|
||||
result.andAlso(isCovariantWith(env, subNorm->threads, superNorm->threads, scope));
|
||||
result.andAlso(isCovariantWith(env, subNorm->buffers, superNorm->buffers, scope));
|
||||
result.andAlso(isCovariantWith(env, subNorm->tables, superNorm->tables, scope));
|
||||
result.andAlso(isCovariantWith(env, subNorm->functions, superNorm->functions, scope));
|
||||
// isCovariantWith(subNorm->tyvars, superNorm->tyvars);
|
||||
return result;
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const NormalizedClassType& subClass, const NormalizedClassType& superClass)
|
||||
SubtypingResult Subtyping::isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const NormalizedClassType& subClass,
|
||||
const NormalizedClassType& superClass,
|
||||
NotNull<Scope> scope
|
||||
)
|
||||
{
|
||||
for (const auto& [subClassTy, _] : subClass.classes)
|
||||
{
|
||||
@ -1499,13 +1546,13 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Norm
|
||||
|
||||
for (const auto& [superClassTy, superNegations] : superClass.classes)
|
||||
{
|
||||
result.orElse(isCovariantWith(env, subClassTy, superClassTy));
|
||||
result.orElse(isCovariantWith(env, subClassTy, superClassTy, scope));
|
||||
if (!result.isSubtype)
|
||||
continue;
|
||||
|
||||
for (TypeId negation : superNegations)
|
||||
{
|
||||
result.andAlso(SubtypingResult::negate(isCovariantWith(env, subClassTy, negation)));
|
||||
result.andAlso(SubtypingResult::negate(isCovariantWith(env, subClassTy, negation, scope)));
|
||||
if (result.isSubtype)
|
||||
break;
|
||||
}
|
||||
@ -1518,14 +1565,19 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Norm
|
||||
return {true};
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const NormalizedClassType& subClass, const TypeIds& superTables)
|
||||
SubtypingResult Subtyping::isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const NormalizedClassType& subClass,
|
||||
const TypeIds& superTables,
|
||||
NotNull<Scope> scope
|
||||
)
|
||||
{
|
||||
for (const auto& [subClassTy, _] : subClass.classes)
|
||||
{
|
||||
SubtypingResult result;
|
||||
|
||||
for (TypeId superTableTy : superTables)
|
||||
result.orElse(isCovariantWith(env, subClassTy, superTableTy));
|
||||
result.orElse(isCovariantWith(env, subClassTy, superTableTy, scope));
|
||||
|
||||
if (!result.isSubtype)
|
||||
return result;
|
||||
@ -1534,13 +1586,23 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Norm
|
||||
return {true};
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const NormalizedStringType& subString, const NormalizedStringType& superString)
|
||||
SubtypingResult Subtyping::isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const NormalizedStringType& subString,
|
||||
const NormalizedStringType& superString,
|
||||
NotNull<Scope> scope
|
||||
)
|
||||
{
|
||||
bool isSubtype = Luau::isSubtype(subString, superString);
|
||||
return {isSubtype};
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const NormalizedStringType& subString, const TypeIds& superTables)
|
||||
SubtypingResult Subtyping::isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const NormalizedStringType& subString,
|
||||
const TypeIds& superTables,
|
||||
NotNull<Scope> scope
|
||||
)
|
||||
{
|
||||
if (subString.isNever())
|
||||
return {true};
|
||||
@ -1550,7 +1612,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Norm
|
||||
SubtypingResult result;
|
||||
for (const auto& superTable : superTables)
|
||||
{
|
||||
result.orElse(isCovariantWith(env, builtinTypes->stringType, superTable));
|
||||
result.orElse(isCovariantWith(env, builtinTypes->stringType, superTable, scope));
|
||||
if (result.isSubtype)
|
||||
return result;
|
||||
}
|
||||
@ -1566,7 +1628,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Norm
|
||||
SubtypingResult result{true};
|
||||
for (const auto& [_, subString] : subString.singletons)
|
||||
{
|
||||
result.andAlso(isCovariantWith(env, subString, superTable));
|
||||
result.andAlso(isCovariantWith(env, subString, superTable, scope));
|
||||
if (!result.isSubtype)
|
||||
break;
|
||||
}
|
||||
@ -1583,7 +1645,8 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Norm
|
||||
SubtypingResult Subtyping::isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const NormalizedFunctionType& subFunction,
|
||||
const NormalizedFunctionType& superFunction
|
||||
const NormalizedFunctionType& superFunction,
|
||||
NotNull<Scope> scope
|
||||
)
|
||||
{
|
||||
if (subFunction.isNever())
|
||||
@ -1591,10 +1654,10 @@ SubtypingResult Subtyping::isCovariantWith(
|
||||
else if (superFunction.isTop)
|
||||
return {true};
|
||||
else
|
||||
return isCovariantWith(env, subFunction.parts, superFunction.parts);
|
||||
return isCovariantWith(env, subFunction.parts, superFunction.parts, scope);
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TypeIds& subTypes, const TypeIds& superTypes)
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TypeIds& subTypes, const TypeIds& superTypes, NotNull<Scope> scope)
|
||||
{
|
||||
std::vector<SubtypingResult> results;
|
||||
|
||||
@ -1602,15 +1665,20 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Type
|
||||
{
|
||||
results.emplace_back();
|
||||
for (TypeId superTy : superTypes)
|
||||
results.back().orElse(isCovariantWith(env, subTy, superTy));
|
||||
results.back().orElse(isCovariantWith(env, subTy, superTy, scope));
|
||||
}
|
||||
|
||||
return SubtypingResult::all(results);
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const VariadicTypePack* subVariadic, const VariadicTypePack* superVariadic)
|
||||
SubtypingResult Subtyping::isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const VariadicTypePack* subVariadic,
|
||||
const VariadicTypePack* superVariadic,
|
||||
NotNull<Scope> scope
|
||||
)
|
||||
{
|
||||
return isCovariantWith(env, subVariadic->ty, superVariadic->ty).withBothComponent(TypePath::TypeField::Variadic);
|
||||
return isCovariantWith(env, subVariadic->ty, superVariadic->ty, scope).withBothComponent(TypePath::TypeField::Variadic);
|
||||
}
|
||||
|
||||
bool Subtyping::bindGeneric(SubtypingEnvironment& env, TypeId subTy, TypeId superTy)
|
||||
@ -1633,20 +1701,30 @@ bool Subtyping::bindGeneric(SubtypingEnvironment& env, TypeId subTy, TypeId supe
|
||||
return true;
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TypeFunctionInstanceType* subFunctionInstance, const TypeId superTy)
|
||||
SubtypingResult Subtyping::isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const TypeFunctionInstanceType* subFunctionInstance,
|
||||
const TypeId superTy,
|
||||
NotNull<Scope> scope
|
||||
)
|
||||
{
|
||||
// Reduce the type function instance
|
||||
auto [ty, errors] = handleTypeFunctionReductionResult(subFunctionInstance);
|
||||
auto [ty, errors] = handleTypeFunctionReductionResult(subFunctionInstance, scope);
|
||||
|
||||
// If we return optional, that means the type function was irreducible - we can reduce that to never
|
||||
return isCovariantWith(env, ty, superTy).withErrors(errors).withSubComponent(TypePath::Reduction{ty});
|
||||
return isCovariantWith(env, ty, superTy, scope).withErrors(errors).withSubComponent(TypePath::Reduction{ty});
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TypeId subTy, const TypeFunctionInstanceType* superFunctionInstance)
|
||||
SubtypingResult Subtyping::isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const TypeId subTy,
|
||||
const TypeFunctionInstanceType* superFunctionInstance,
|
||||
NotNull<Scope> scope
|
||||
)
|
||||
{
|
||||
// Reduce the type function instance
|
||||
auto [ty, errors] = handleTypeFunctionReductionResult(superFunctionInstance);
|
||||
return isCovariantWith(env, subTy, ty).withErrors(errors).withSuperComponent(TypePath::Reduction{ty});
|
||||
auto [ty, errors] = handleTypeFunctionReductionResult(superFunctionInstance, scope);
|
||||
return isCovariantWith(env, subTy, ty, scope).withErrors(errors).withSuperComponent(TypePath::Reduction{ty});
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1681,7 +1759,7 @@ TypeId Subtyping::makeAggregateType(const Container& container, TypeId orElse)
|
||||
return arena->addType(T{std::vector<TypeId>(begin(container), end(container))});
|
||||
}
|
||||
|
||||
std::pair<TypeId, ErrorVec> Subtyping::handleTypeFunctionReductionResult(const TypeFunctionInstanceType* functionInstance)
|
||||
std::pair<TypeId, ErrorVec> Subtyping::handleTypeFunctionReductionResult(const TypeFunctionInstanceType* functionInstance, NotNull<Scope> scope)
|
||||
{
|
||||
TypeFunctionContext context{arena, builtinTypes, scope, normalizer, iceReporter, NotNull{&limits}};
|
||||
TypeId function = arena->addType(*functionInstance);
|
||||
|
@ -252,8 +252,14 @@ struct TypeChecker2
|
||||
Subtyping _subtyping;
|
||||
NotNull<Subtyping> subtyping;
|
||||
|
||||
TypeChecker2(NotNull<BuiltinTypes> builtinTypes, NotNull<UnifierSharedState> unifierState, NotNull<TypeCheckLimits> limits, DcrLogger* logger,
|
||||
const SourceModule* sourceModule, Module* module)
|
||||
TypeChecker2(
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<UnifierSharedState> unifierState,
|
||||
NotNull<TypeCheckLimits> limits,
|
||||
DcrLogger* logger,
|
||||
const SourceModule* sourceModule,
|
||||
Module* module
|
||||
)
|
||||
: builtinTypes(builtinTypes)
|
||||
, logger(logger)
|
||||
, limits(limits)
|
||||
@ -261,8 +267,7 @@ struct TypeChecker2
|
||||
, sourceModule(sourceModule)
|
||||
, module(module)
|
||||
, normalizer{&module->internalTypes, builtinTypes, unifierState, /* cacheInhabitance */ true}
|
||||
, _subtyping{builtinTypes, NotNull{&module->internalTypes}, NotNull{&normalizer}, NotNull{unifierState->iceHandler},
|
||||
NotNull{module->getModuleScope().get()}}
|
||||
, _subtyping{builtinTypes, NotNull{&module->internalTypes}, NotNull{&normalizer}, NotNull{unifierState->iceHandler}}
|
||||
, subtyping(&_subtyping)
|
||||
{
|
||||
}
|
||||
@ -1255,8 +1260,9 @@ struct TypeChecker2
|
||||
#if defined(LUAU_ENABLE_ASSERT)
|
||||
TypeId actualType = lookupType(expr);
|
||||
TypeId expectedType = builtinTypes->nilType;
|
||||
NotNull<Scope> scope{findInnermostScope(expr->location)};
|
||||
|
||||
SubtypingResult r = subtyping->isSubtype(actualType, expectedType);
|
||||
SubtypingResult r = subtyping->isSubtype(actualType, expectedType, scope);
|
||||
LUAU_ASSERT(r.isSubtype || isErrorSuppressing(expr->location, actualType));
|
||||
#endif
|
||||
}
|
||||
@ -1267,8 +1273,9 @@ struct TypeChecker2
|
||||
|
||||
const TypeId bestType = expr->value ? builtinTypes->trueType : builtinTypes->falseType;
|
||||
const TypeId inferredType = lookupType(expr);
|
||||
NotNull<Scope> scope{findInnermostScope(expr->location)};
|
||||
|
||||
const SubtypingResult r = subtyping->isSubtype(bestType, inferredType);
|
||||
const SubtypingResult r = subtyping->isSubtype(bestType, inferredType, scope);
|
||||
if (!r.isSubtype && !isErrorSuppressing(expr->location, inferredType))
|
||||
reportError(TypeMismatch{inferredType, bestType}, expr->location);
|
||||
}
|
||||
@ -1278,8 +1285,9 @@ struct TypeChecker2
|
||||
#if defined(LUAU_ENABLE_ASSERT)
|
||||
const TypeId bestType = builtinTypes->numberType;
|
||||
const TypeId inferredType = lookupType(expr);
|
||||
NotNull<Scope> scope{findInnermostScope(expr->location)};
|
||||
|
||||
const SubtypingResult r = subtyping->isSubtype(bestType, inferredType);
|
||||
const SubtypingResult r = subtyping->isSubtype(bestType, inferredType, scope);
|
||||
LUAU_ASSERT(r.isSubtype || isErrorSuppressing(expr->location, inferredType));
|
||||
#endif
|
||||
}
|
||||
@ -1290,8 +1298,9 @@ struct TypeChecker2
|
||||
|
||||
const TypeId bestType = module->internalTypes.addType(SingletonType{StringSingleton{std::string{expr->value.data, expr->value.size}}});
|
||||
const TypeId inferredType = lookupType(expr);
|
||||
NotNull<Scope> scope{findInnermostScope(expr->location)};
|
||||
|
||||
const SubtypingResult r = subtyping->isSubtype(bestType, inferredType);
|
||||
const SubtypingResult r = subtyping->isSubtype(bestType, inferredType, scope);
|
||||
if (!r.isSubtype && !isErrorSuppressing(expr->location, inferredType))
|
||||
reportError(TypeMismatch{inferredType, bestType}, expr->location);
|
||||
}
|
||||
@ -1320,7 +1329,7 @@ struct TypeChecker2
|
||||
std::vector<AstExpr*> argExprs;
|
||||
argExprs.reserve(call->args.size + 1);
|
||||
|
||||
TypeId* originalCallTy = module->astOriginalCallTypes.find(call);
|
||||
TypeId* originalCallTy = module->astOriginalCallTypes.find(call->func);
|
||||
TypeId* selectedOverloadTy = module->astOverloadResolvedTypes.find(call);
|
||||
if (!originalCallTy)
|
||||
return;
|
||||
@ -1347,7 +1356,8 @@ struct TypeChecker2
|
||||
|
||||
if (selectedOverloadTy)
|
||||
{
|
||||
SubtypingResult result = subtyping->isSubtype(*originalCallTy, *selectedOverloadTy);
|
||||
NotNull<Scope> scope{findInnermostScope(call->location)};
|
||||
SubtypingResult result = subtyping->isSubtype(*originalCallTy, *selectedOverloadTy, scope);
|
||||
if (result.isSubtype)
|
||||
fnTy = follow(*selectedOverloadTy);
|
||||
|
||||
@ -1622,12 +1632,17 @@ struct TypeChecker2
|
||||
reportError(OptionalValueAccess{exprType}, indexExpr->location);
|
||||
}
|
||||
}
|
||||
else if (auto exprIntersection = get<IntersectionType>(exprType))
|
||||
else if (auto ut = get<UnionType>(exprType))
|
||||
{
|
||||
for (TypeId part : exprIntersection)
|
||||
{
|
||||
(void)part;
|
||||
}
|
||||
// if all of the types are a table type, the union must be a table, and so we shouldn't error.
|
||||
if (!std::all_of(begin(ut), end(ut), getTableType))
|
||||
reportError(NotATable{exprType}, indexExpr->location);
|
||||
}
|
||||
else if (auto it = get<IntersectionType>(exprType))
|
||||
{
|
||||
// if any of the types are a table type, the intersection must be a table, and so we shouldn't error.
|
||||
if (!std::any_of(begin(it), end(it), getTableType))
|
||||
reportError(NotATable{exprType}, indexExpr->location);
|
||||
}
|
||||
else if (get<NeverType>(exprType) || isErrorSuppressing(indexExpr->location, exprType))
|
||||
{
|
||||
@ -2726,7 +2741,8 @@ struct TypeChecker2
|
||||
|
||||
bool testIsSubtype(TypeId subTy, TypeId superTy, Location location)
|
||||
{
|
||||
SubtypingResult r = subtyping->isSubtype(subTy, superTy);
|
||||
NotNull<Scope> scope{findInnermostScope(location)};
|
||||
SubtypingResult r = subtyping->isSubtype(subTy, superTy, scope);
|
||||
|
||||
if (r.normalizationTooComplex)
|
||||
reportError(NormalizationTooComplex{}, location);
|
||||
@ -2739,7 +2755,8 @@ struct TypeChecker2
|
||||
|
||||
bool testIsSubtype(TypePackId subTy, TypePackId superTy, Location location)
|
||||
{
|
||||
SubtypingResult r = subtyping->isSubtype(subTy, superTy);
|
||||
NotNull<Scope> scope{findInnermostScope(location)};
|
||||
SubtypingResult r = subtyping->isSubtype(subTy, superTy, scope);
|
||||
|
||||
if (r.normalizationTooComplex)
|
||||
reportError(NormalizationTooComplex{}, location);
|
||||
|
@ -701,8 +701,8 @@ TypeFunctionReductionResult<TypeId> lenTypeFunction(
|
||||
if (!u2.unify(inferredArgPack, instantiatedMmFtv->argTypes))
|
||||
return {std::nullopt, true, {}, {}}; // occurs check failed
|
||||
|
||||
Subtyping subtyping{ctx->builtins, ctx->arena, ctx->normalizer, ctx->ice, ctx->scope};
|
||||
if (!subtyping.isSubtype(inferredArgPack, instantiatedMmFtv->argTypes).isSubtype) // TODO: is this the right variance?
|
||||
Subtyping subtyping{ctx->builtins, ctx->arena, ctx->normalizer, ctx->ice};
|
||||
if (!subtyping.isSubtype(inferredArgPack, instantiatedMmFtv->argTypes, ctx->scope).isSubtype) // TODO: is this the right variance?
|
||||
return {std::nullopt, true, {}, {}};
|
||||
|
||||
// `len` must return a `number`.
|
||||
@ -790,8 +790,8 @@ TypeFunctionReductionResult<TypeId> unmTypeFunction(
|
||||
if (!u2.unify(inferredArgPack, instantiatedMmFtv->argTypes))
|
||||
return {std::nullopt, true, {}, {}}; // occurs check failed
|
||||
|
||||
Subtyping subtyping{ctx->builtins, ctx->arena, ctx->normalizer, ctx->ice, ctx->scope};
|
||||
if (!subtyping.isSubtype(inferredArgPack, instantiatedMmFtv->argTypes).isSubtype) // TODO: is this the right variance?
|
||||
Subtyping subtyping{ctx->builtins, ctx->arena, ctx->normalizer, ctx->ice};
|
||||
if (!subtyping.isSubtype(inferredArgPack, instantiatedMmFtv->argTypes, ctx->scope).isSubtype) // TODO: is this the right variance?
|
||||
return {std::nullopt, true, {}, {}};
|
||||
|
||||
if (std::optional<TypeId> ret = first(instantiatedMmFtv->retTypes))
|
||||
@ -1138,8 +1138,8 @@ TypeFunctionReductionResult<TypeId> concatTypeFunction(
|
||||
if (!u2.unify(inferredArgPack, instantiatedMmFtv->argTypes))
|
||||
return {std::nullopt, true, {}, {}}; // occurs check failed
|
||||
|
||||
Subtyping subtyping{ctx->builtins, ctx->arena, ctx->normalizer, ctx->ice, ctx->scope};
|
||||
if (!subtyping.isSubtype(inferredArgPack, instantiatedMmFtv->argTypes).isSubtype) // TODO: is this the right variance?
|
||||
Subtyping subtyping{ctx->builtins, ctx->arena, ctx->normalizer, ctx->ice};
|
||||
if (!subtyping.isSubtype(inferredArgPack, instantiatedMmFtv->argTypes, ctx->scope).isSubtype) // TODO: is this the right variance?
|
||||
return {std::nullopt, true, {}, {}};
|
||||
|
||||
return {ctx->builtins->stringType, false, {}, {}};
|
||||
@ -1392,8 +1392,8 @@ static TypeFunctionReductionResult<TypeId> comparisonTypeFunction(
|
||||
if (!u2.unify(inferredArgPack, instantiatedMmFtv->argTypes))
|
||||
return {std::nullopt, true, {}, {}}; // occurs check failed
|
||||
|
||||
Subtyping subtyping{ctx->builtins, ctx->arena, ctx->normalizer, ctx->ice, ctx->scope};
|
||||
if (!subtyping.isSubtype(inferredArgPack, instantiatedMmFtv->argTypes).isSubtype) // TODO: is this the right variance?
|
||||
Subtyping subtyping{ctx->builtins, ctx->arena, ctx->normalizer, ctx->ice};
|
||||
if (!subtyping.isSubtype(inferredArgPack, instantiatedMmFtv->argTypes, ctx->scope).isSubtype) // TODO: is this the right variance?
|
||||
return {std::nullopt, true, {}, {}};
|
||||
|
||||
return {ctx->builtins->booleanType, false, {}, {}};
|
||||
@ -1536,8 +1536,8 @@ TypeFunctionReductionResult<TypeId> eqTypeFunction(
|
||||
if (!u2.unify(inferredArgPack, instantiatedMmFtv->argTypes))
|
||||
return {std::nullopt, true, {}, {}}; // occurs check failed
|
||||
|
||||
Subtyping subtyping{ctx->builtins, ctx->arena, ctx->normalizer, ctx->ice, ctx->scope};
|
||||
if (!subtyping.isSubtype(inferredArgPack, instantiatedMmFtv->argTypes).isSubtype) // TODO: is this the right variance?
|
||||
Subtyping subtyping{ctx->builtins, ctx->arena, ctx->normalizer, ctx->ice};
|
||||
if (!subtyping.isSubtype(inferredArgPack, instantiatedMmFtv->argTypes, ctx->scope).isSubtype) // TODO: is this the right variance?
|
||||
return {std::nullopt, true, {}, {}};
|
||||
|
||||
return {ctx->builtins->booleanType, false, {}, {}};
|
||||
|
@ -34,6 +34,7 @@ LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false)
|
||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||
LUAU_FASTFLAGVARIABLE(LuauRemoveBadRelationalOperatorWarning, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauOkWithIteratingOverTableProperties, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAcceptIndexingTableUnionsIntersections, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
@ -3506,54 +3507,207 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex
|
||||
}
|
||||
}
|
||||
|
||||
TableType* exprTable = getMutableTableType(exprType);
|
||||
|
||||
if (!exprTable)
|
||||
if (FFlag::LuauAcceptIndexingTableUnionsIntersections)
|
||||
{
|
||||
reportError(TypeError{expr.expr->location, NotATable{exprType}});
|
||||
return errorRecoveryType(scope);
|
||||
}
|
||||
// We're going to have a whole vector.
|
||||
std::vector<TableType*> tableTypes{};
|
||||
bool isUnion = true;
|
||||
|
||||
if (value)
|
||||
{
|
||||
const auto& it = exprTable->props.find(value->value.data);
|
||||
if (it != exprTable->props.end())
|
||||
// We'd like for normalization eventually to deal with this sort of thing, but as a tactical affordance, we will
|
||||
// attempt to deal with _one_ level of unions or intersections.
|
||||
if (auto exprUnion = get<UnionType>(exprType))
|
||||
{
|
||||
return it->second.type();
|
||||
tableTypes.reserve(exprUnion->options.size());
|
||||
|
||||
for (auto option : exprUnion)
|
||||
{
|
||||
TableType* optionTable = getMutableTableType(option);
|
||||
|
||||
if (!optionTable)
|
||||
{
|
||||
// TODO: we could do better here and report `option` is not a table as reasoning for the error
|
||||
reportError(TypeError{expr.expr->location, NotATable{exprType}});
|
||||
return errorRecoveryType(scope);
|
||||
}
|
||||
|
||||
tableTypes.push_back(optionTable);
|
||||
}
|
||||
}
|
||||
else if ((ctx == ValueContext::LValue && exprTable->state == TableState::Unsealed) || exprTable->state == TableState::Free)
|
||||
else if (auto exprIntersection = get<IntersectionType>(exprType))
|
||||
{
|
||||
TypeId resultType = freshType(scope);
|
||||
Property& property = exprTable->props[value->value.data];
|
||||
property.setType(resultType);
|
||||
property.location = expr.index->location;
|
||||
return resultType;
|
||||
tableTypes.reserve(exprIntersection->parts.size());
|
||||
isUnion = false;
|
||||
|
||||
for (auto part : exprIntersection)
|
||||
{
|
||||
TableType* partTable = getMutableTableType(part);
|
||||
|
||||
if (!partTable)
|
||||
{
|
||||
// TODO: we could do better here and report `part` is not a table as reasoning for the error
|
||||
reportError(TypeError{expr.expr->location, NotATable{exprType}});
|
||||
return errorRecoveryType(scope);
|
||||
}
|
||||
|
||||
tableTypes.push_back(partTable);
|
||||
}
|
||||
}
|
||||
else if (auto exprTable = getMutableTableType(exprType))
|
||||
{
|
||||
tableTypes.push_back(exprTable);
|
||||
}
|
||||
else
|
||||
{
|
||||
reportError(TypeError{expr.expr->location, NotATable{exprType}});
|
||||
return errorRecoveryType(scope);
|
||||
}
|
||||
}
|
||||
|
||||
if (exprTable->indexer)
|
||||
{
|
||||
const TableIndexer& indexer = *exprTable->indexer;
|
||||
unify(indexType, indexer.indexType, scope, expr.index->location);
|
||||
return indexer.indexResultType;
|
||||
}
|
||||
else if ((ctx == ValueContext::LValue && exprTable->state == TableState::Unsealed) || exprTable->state == TableState::Free)
|
||||
{
|
||||
TypeId indexerType = freshType(exprTable->level);
|
||||
unify(indexType, indexerType, scope, expr.location);
|
||||
TypeId indexResultType = freshType(exprTable->level);
|
||||
if (value)
|
||||
{
|
||||
DenseHashSet<TypeId> propTypes{{}};
|
||||
|
||||
exprTable->indexer = TableIndexer{anyIfNonstrict(indexerType), anyIfNonstrict(indexResultType)};
|
||||
return indexResultType;
|
||||
for (auto table : tableTypes)
|
||||
{
|
||||
const auto& it = table->props.find(value->value.data);
|
||||
if (it != table->props.end())
|
||||
{
|
||||
propTypes.insert(it->second.type());
|
||||
}
|
||||
else if ((ctx == ValueContext::LValue && table->state == TableState::Unsealed) || table->state == TableState::Free)
|
||||
{
|
||||
TypeId resultType = freshType(scope);
|
||||
Property& property = table->props[value->value.data];
|
||||
property.setType(resultType);
|
||||
property.location = expr.index->location;
|
||||
propTypes.insert(resultType);
|
||||
}
|
||||
}
|
||||
|
||||
if (propTypes.size() == 1)
|
||||
return *propTypes.begin();
|
||||
|
||||
if (!propTypes.empty())
|
||||
{
|
||||
if (isUnion)
|
||||
{
|
||||
std::vector<TypeId> options = reduceUnion({propTypes.begin(), propTypes.end()});
|
||||
|
||||
if (options.empty())
|
||||
return neverType;
|
||||
|
||||
if (options.size() == 1)
|
||||
return options[0];
|
||||
|
||||
return addType(UnionType{options});
|
||||
}
|
||||
|
||||
return addType(IntersectionType{{propTypes.begin(), propTypes.end()}});
|
||||
}
|
||||
}
|
||||
|
||||
DenseHashSet<TypeId> resultTypes{{}};
|
||||
|
||||
for (auto table : tableTypes)
|
||||
{
|
||||
if (table->indexer)
|
||||
{
|
||||
const TableIndexer& indexer = *table->indexer;
|
||||
unify(indexType, indexer.indexType, scope, expr.index->location);
|
||||
resultTypes.insert(indexer.indexResultType);
|
||||
}
|
||||
else if ((ctx == ValueContext::LValue && table->state == TableState::Unsealed) || table->state == TableState::Free)
|
||||
{
|
||||
TypeId indexerType = freshType(table->level);
|
||||
unify(indexType, indexerType, scope, expr.location);
|
||||
TypeId indexResultType = freshType(table->level);
|
||||
|
||||
table->indexer = TableIndexer{anyIfNonstrict(indexerType), anyIfNonstrict(indexResultType)};
|
||||
resultTypes.insert(indexResultType);
|
||||
}
|
||||
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 return any
|
||||
* and hope for the best.
|
||||
*/
|
||||
|
||||
// if this is a union, it's going to be equivalent to `any` no matter what at this point, so we'll just call it done.
|
||||
if (isUnion)
|
||||
return anyType;
|
||||
|
||||
resultTypes.insert(anyType);
|
||||
}
|
||||
}
|
||||
|
||||
if (resultTypes.size() == 1)
|
||||
return *resultTypes.begin();
|
||||
|
||||
if (isUnion)
|
||||
{
|
||||
std::vector<TypeId> options = reduceUnion({resultTypes.begin(), resultTypes.end()});
|
||||
|
||||
if (options.empty())
|
||||
return neverType;
|
||||
|
||||
if (options.size() == 1)
|
||||
return options[0];
|
||||
|
||||
return addType(UnionType{options});
|
||||
}
|
||||
|
||||
return addType(IntersectionType{{resultTypes.begin(), resultTypes.end()}});
|
||||
}
|
||||
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 return any
|
||||
* and hope for the best.
|
||||
*/
|
||||
return anyType;
|
||||
TableType* exprTable = getMutableTableType(exprType);
|
||||
if (!exprTable)
|
||||
{
|
||||
reportError(TypeError{expr.expr->location, NotATable{exprType}});
|
||||
return errorRecoveryType(scope);
|
||||
}
|
||||
|
||||
if (value)
|
||||
{
|
||||
const auto& it = exprTable->props.find(value->value.data);
|
||||
if (it != exprTable->props.end())
|
||||
{
|
||||
return it->second.type();
|
||||
}
|
||||
else if ((ctx == ValueContext::LValue && exprTable->state == TableState::Unsealed) || exprTable->state == TableState::Free)
|
||||
{
|
||||
TypeId resultType = freshType(scope);
|
||||
Property& property = exprTable->props[value->value.data];
|
||||
property.setType(resultType);
|
||||
property.location = expr.index->location;
|
||||
return resultType;
|
||||
}
|
||||
}
|
||||
|
||||
if (exprTable->indexer)
|
||||
{
|
||||
const TableIndexer& indexer = *exprTable->indexer;
|
||||
unify(indexType, indexer.indexType, scope, expr.index->location);
|
||||
return indexer.indexResultType;
|
||||
}
|
||||
else if ((ctx == ValueContext::LValue && exprTable->state == TableState::Unsealed) || exprTable->state == TableState::Free)
|
||||
{
|
||||
TypeId indexerType = freshType(exprTable->level);
|
||||
unify(indexType, indexerType, scope, expr.location);
|
||||
TypeId indexResultType = freshType(exprTable->level);
|
||||
|
||||
exprTable->indexer = TableIndexer{anyIfNonstrict(indexerType), anyIfNonstrict(indexResultType)};
|
||||
return indexResultType;
|
||||
}
|
||||
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 return any
|
||||
* and hope for the best.
|
||||
*/
|
||||
return anyType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -301,6 +301,8 @@ TypePack extendTypePack(
|
||||
|
||||
TypePack newPack;
|
||||
newPack.tail = arena.freshTypePack(ftp->scope);
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
result.tail = newPack.tail;
|
||||
size_t overridesIndex = 0;
|
||||
while (result.head.size() < length)
|
||||
{
|
||||
|
@ -4,8 +4,6 @@
|
||||
#include "Luau/Common.h"
|
||||
#include "Luau/IrData.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauCodegenFastcall3)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
namespace CodeGen
|
||||
@ -113,47 +111,16 @@ static void visitVmRegDefsUses(T& visitor, IrFunction& function, const IrInst& i
|
||||
break;
|
||||
|
||||
case IrCmd::FASTCALL:
|
||||
if (FFlag::LuauCodegenFastcall3)
|
||||
{
|
||||
visitor.use(inst.c);
|
||||
visitor.use(inst.c);
|
||||
|
||||
if (int nresults = function.intOp(inst.d); nresults != -1)
|
||||
visitor.defRange(vmRegOp(inst.b), nresults);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (int count = function.intOp(inst.e); count != -1)
|
||||
{
|
||||
if (count >= 3)
|
||||
{
|
||||
CODEGEN_ASSERT(inst.d.kind == IrOpKind::VmReg && vmRegOp(inst.d) == vmRegOp(inst.c) + 1);
|
||||
|
||||
visitor.useRange(vmRegOp(inst.c), count);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (count >= 1)
|
||||
visitor.use(inst.c);
|
||||
|
||||
if (count >= 2)
|
||||
visitor.maybeUse(inst.d); // Argument can also be a VmConst
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
visitor.useVarargs(vmRegOp(inst.c));
|
||||
}
|
||||
|
||||
// Multiple return sequences (count == -1) are defined by ADJUST_STACK_TO_REG
|
||||
if (int count = function.intOp(inst.f); count != -1)
|
||||
visitor.defRange(vmRegOp(inst.b), count);
|
||||
}
|
||||
if (int nresults = function.intOp(inst.d); nresults != -1)
|
||||
visitor.defRange(vmRegOp(inst.b), nresults);
|
||||
break;
|
||||
case IrCmd::INVOKE_FASTCALL:
|
||||
if (int count = function.intOp(FFlag::LuauCodegenFastcall3 ? inst.f : inst.e); count != -1)
|
||||
if (int count = function.intOp(inst.f); count != -1)
|
||||
{
|
||||
// Only LOP_FASTCALL3 lowering is allowed to have third optional argument
|
||||
if (count >= 3 && (!FFlag::LuauCodegenFastcall3 || inst.e.kind == IrOpKind::Undef))
|
||||
if (count >= 3 && inst.e.kind == IrOpKind::Undef)
|
||||
{
|
||||
CODEGEN_ASSERT(inst.d.kind == IrOpKind::VmReg && vmRegOp(inst.d) == vmRegOp(inst.c) + 1);
|
||||
|
||||
@ -167,7 +134,7 @@ static void visitVmRegDefsUses(T& visitor, IrFunction& function, const IrInst& i
|
||||
if (count >= 2)
|
||||
visitor.maybeUse(inst.d); // Argument can also be a VmConst
|
||||
|
||||
if (FFlag::LuauCodegenFastcall3 && count >= 3)
|
||||
if (count >= 3)
|
||||
visitor.maybeUse(inst.e); // Argument can also be a VmConst
|
||||
}
|
||||
}
|
||||
@ -177,7 +144,7 @@ static void visitVmRegDefsUses(T& visitor, IrFunction& function, const IrInst& i
|
||||
}
|
||||
|
||||
// Multiple return sequences (count == -1) are defined by ADJUST_STACK_TO_REG
|
||||
if (int count = function.intOp(FFlag::LuauCodegenFastcall3 ? inst.g : inst.f); count != -1)
|
||||
if (int count = function.intOp(inst.g); count != -1)
|
||||
visitor.defRange(vmRegOp(inst.b), count);
|
||||
break;
|
||||
case IrCmd::FORGLOOP:
|
||||
|
@ -11,8 +11,6 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauCodegenFastcall3, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
namespace CodeGen
|
||||
@ -1101,8 +1099,6 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks)
|
||||
}
|
||||
case LOP_FASTCALL3:
|
||||
{
|
||||
CODEGEN_ASSERT(FFlag::LuauCodegenFastcall3);
|
||||
|
||||
int bfid = LUAU_INSN_A(*pc);
|
||||
int skip = LUAU_INSN_C(*pc);
|
||||
int aux = pc[1];
|
||||
|
@ -149,7 +149,10 @@ static std::string getAssemblyImpl(AssemblyBuilder& build, const TValue* func, A
|
||||
Proto* root = clvalue(func)->l.p;
|
||||
|
||||
if ((options.compilationOptions.flags & CodeGen_OnlyNativeModules) != 0 && (root->flags & LPF_NATIVE_MODULE) == 0)
|
||||
{
|
||||
build.finalize();
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::vector<Proto*> protos;
|
||||
if (FFlag::LuauNativeAttribute)
|
||||
@ -174,7 +177,7 @@ static std::string getAssemblyImpl(AssemblyBuilder& build, const TValue* func, A
|
||||
|
||||
if (protos.empty())
|
||||
{
|
||||
build.finalize(); // to avoid assertion in AssemblyBuilder dtor
|
||||
build.finalize();
|
||||
return std::string();
|
||||
}
|
||||
|
||||
|
@ -12,8 +12,6 @@
|
||||
|
||||
#include "lstate.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauCodegenMathSign)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
namespace CodeGen
|
||||
@ -57,36 +55,6 @@ static void emitBuiltinMathModf(IrRegAllocX64& regs, AssemblyBuilderX64& build,
|
||||
}
|
||||
}
|
||||
|
||||
static void emitBuiltinMathSign(IrRegAllocX64& regs, AssemblyBuilderX64& build, int ra, int arg)
|
||||
{
|
||||
CODEGEN_ASSERT(!FFlag::LuauCodegenMathSign);
|
||||
|
||||
ScopedRegX64 tmp0{regs, SizeX64::xmmword};
|
||||
ScopedRegX64 tmp1{regs, SizeX64::xmmword};
|
||||
ScopedRegX64 tmp2{regs, SizeX64::xmmword};
|
||||
ScopedRegX64 tmp3{regs, SizeX64::xmmword};
|
||||
|
||||
build.vmovsd(tmp0.reg, luauRegValue(arg));
|
||||
build.vxorpd(tmp1.reg, tmp1.reg, tmp1.reg);
|
||||
|
||||
// Set tmp2 to -1 if arg < 0, else 0
|
||||
build.vcmpltsd(tmp2.reg, tmp0.reg, tmp1.reg);
|
||||
build.vmovsd(tmp3.reg, build.f64(-1));
|
||||
build.vandpd(tmp2.reg, tmp2.reg, tmp3.reg);
|
||||
|
||||
// Set mask bit to 1 if 0 < arg, else 0
|
||||
build.vcmpltsd(tmp0.reg, tmp1.reg, tmp0.reg);
|
||||
|
||||
// Result = (mask-bit == 1) ? 1.0 : tmp2
|
||||
// If arg < 0 then tmp2 is -1 and mask-bit is 0, result is -1
|
||||
// If arg == 0 then tmp2 is 0 and mask-bit is 0, result is 0
|
||||
// If arg > 0 then tmp2 is 0 and mask-bit is 1, result is 1
|
||||
build.vblendvpd(tmp0.reg, tmp2.reg, build.f64x2(1, 1), tmp0.reg);
|
||||
|
||||
build.vmovsd(luauRegValue(ra), tmp0.reg);
|
||||
build.mov(luauRegTag(ra), LUA_TNUMBER);
|
||||
}
|
||||
|
||||
void emitBuiltin(IrRegAllocX64& regs, AssemblyBuilderX64& build, int bfid, int ra, int arg, int nresults)
|
||||
{
|
||||
switch (bfid)
|
||||
@ -97,10 +65,6 @@ void emitBuiltin(IrRegAllocX64& regs, AssemblyBuilderX64& build, int bfid, int r
|
||||
case LBF_MATH_MODF:
|
||||
CODEGEN_ASSERT(nresults == 1 || nresults == 2);
|
||||
return emitBuiltinMathModf(regs, build, ra, arg, nresults);
|
||||
case LBF_MATH_SIGN:
|
||||
CODEGEN_ASSERT(!FFlag::LuauCodegenMathSign);
|
||||
CODEGEN_ASSERT(nresults == 1);
|
||||
return emitBuiltinMathSign(regs, build, ra, arg);
|
||||
default:
|
||||
CODEGEN_ASSERT(!"Missing x64 lowering");
|
||||
}
|
||||
|
@ -13,8 +13,6 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
LUAU_FASTFLAG(LuauCodegenFastcall3)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
namespace CodeGen
|
||||
@ -458,8 +456,6 @@ void IrBuilder::translateInst(LuauOpcode op, const Instruction* pc, int i)
|
||||
handleFastcallFallback(translateFastCallN(*this, pc, i, true, 2, vmConst(pc[1]), undef()), pc, i);
|
||||
break;
|
||||
case LOP_FASTCALL3:
|
||||
CODEGEN_ASSERT(FFlag::LuauCodegenFastcall3);
|
||||
|
||||
handleFastcallFallback(translateFastCallN(*this, pc, i, true, 3, vmReg(pc[1] & 0xff), vmReg((pc[1] >> 8) & 0xff)), pc, i);
|
||||
break;
|
||||
case LOP_FORNPREP:
|
||||
|
@ -11,8 +11,7 @@
|
||||
#include "lstate.h"
|
||||
#include "lgc.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauCodegenFastcall3)
|
||||
LUAU_FASTFLAG(LuauCodegenMathSign)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCodegenArmNumToVecFix, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
@ -235,25 +234,6 @@ static bool emitBuiltin(AssemblyBuilderA64& build, IrFunction& function, IrRegAl
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case LBF_MATH_SIGN:
|
||||
{
|
||||
CODEGEN_ASSERT(!FFlag::LuauCodegenMathSign);
|
||||
CODEGEN_ASSERT(nresults == 1);
|
||||
build.ldr(d0, mem(rBase, arg * sizeof(TValue) + offsetof(TValue, value.n)));
|
||||
build.fcmpz(d0);
|
||||
build.fmov(d0, 0.0);
|
||||
build.fmov(d1, 1.0);
|
||||
build.fcsel(d0, d1, d0, getConditionFP(IrCondition::Greater));
|
||||
build.fmov(d1, -1.0);
|
||||
build.fcsel(d0, d1, d0, getConditionFP(IrCondition::Less));
|
||||
build.str(d0, mem(rBase, res * sizeof(TValue) + offsetof(TValue, value.n)));
|
||||
|
||||
RegisterA64 temp = regs.allocTemp(KindA64::w);
|
||||
build.mov(temp, LUA_TNUMBER);
|
||||
build.str(temp, mem(rBase, res * sizeof(TValue) + offsetof(TValue, tt)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
CODEGEN_ASSERT(!"Missing A64 lowering");
|
||||
@ -701,8 +681,6 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
||||
}
|
||||
case IrCmd::SIGN_NUM:
|
||||
{
|
||||
CODEGEN_ASSERT(FFlag::LuauCodegenMathSign);
|
||||
|
||||
inst.regA64 = regs.allocReuse(KindA64::d, index, {inst.a});
|
||||
|
||||
RegisterA64 temp = tempDouble(inst.a);
|
||||
@ -1143,7 +1121,7 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
||||
else
|
||||
{
|
||||
RegisterA64 tempd = tempDouble(inst.a);
|
||||
RegisterA64 temps = castReg(KindA64::s, tempd);
|
||||
RegisterA64 temps = FFlag::LuauCodegenArmNumToVecFix ? regs.allocTemp(KindA64::s) : castReg(KindA64::s, tempd);
|
||||
|
||||
build.fcvt(temps, tempd);
|
||||
build.dup_4s(inst.regA64, castReg(KindA64::q, temps), 0);
|
||||
@ -1194,88 +1172,54 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
||||
case IrCmd::FASTCALL:
|
||||
regs.spill(build, index);
|
||||
|
||||
if (FFlag::LuauCodegenFastcall3)
|
||||
error |= !emitBuiltin(build, function, regs, uintOp(inst.a), vmRegOp(inst.b), vmRegOp(inst.c), intOp(inst.d));
|
||||
else
|
||||
error |= !emitBuiltin(build, function, regs, uintOp(inst.a), vmRegOp(inst.b), vmRegOp(inst.c), intOp(inst.f));
|
||||
|
||||
error |= !emitBuiltin(build, function, regs, uintOp(inst.a), vmRegOp(inst.b), vmRegOp(inst.c), intOp(inst.d));
|
||||
break;
|
||||
case IrCmd::INVOKE_FASTCALL:
|
||||
{
|
||||
if (FFlag::LuauCodegenFastcall3)
|
||||
// We might need a temporary and we have to preserve it over the spill
|
||||
RegisterA64 temp = regs.allocTemp(KindA64::q);
|
||||
regs.spill(build, index, {temp});
|
||||
|
||||
build.mov(x0, rState);
|
||||
build.add(x1, rBase, uint16_t(vmRegOp(inst.b) * sizeof(TValue)));
|
||||
build.add(x2, rBase, uint16_t(vmRegOp(inst.c) * sizeof(TValue)));
|
||||
build.mov(w3, intOp(inst.g)); // nresults
|
||||
|
||||
// 'E' argument can only be produced by LOP_FASTCALL3 lowering
|
||||
if (inst.e.kind != IrOpKind::Undef)
|
||||
{
|
||||
// We might need a temporary and we have to preserve it over the spill
|
||||
RegisterA64 temp = regs.allocTemp(KindA64::q);
|
||||
regs.spill(build, index, {temp});
|
||||
CODEGEN_ASSERT(intOp(inst.f) == 3);
|
||||
|
||||
build.mov(x0, rState);
|
||||
build.add(x1, rBase, uint16_t(vmRegOp(inst.b) * sizeof(TValue)));
|
||||
build.add(x2, rBase, uint16_t(vmRegOp(inst.c) * sizeof(TValue)));
|
||||
build.mov(w3, intOp(inst.g)); // nresults
|
||||
build.ldr(x4, mem(rState, offsetof(lua_State, top)));
|
||||
|
||||
// 'E' argument can only be produced by LOP_FASTCALL3 lowering
|
||||
if (inst.e.kind != IrOpKind::Undef)
|
||||
{
|
||||
CODEGEN_ASSERT(intOp(inst.f) == 3);
|
||||
build.ldr(temp, mem(rBase, vmRegOp(inst.d) * sizeof(TValue)));
|
||||
build.str(temp, mem(x4, 0));
|
||||
|
||||
build.ldr(x4, mem(rState, offsetof(lua_State, top)));
|
||||
|
||||
build.ldr(temp, mem(rBase, vmRegOp(inst.d) * sizeof(TValue)));
|
||||
build.str(temp, mem(x4, 0));
|
||||
|
||||
build.ldr(temp, mem(rBase, vmRegOp(inst.e) * sizeof(TValue)));
|
||||
build.str(temp, mem(x4, sizeof(TValue)));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (inst.d.kind == IrOpKind::VmReg)
|
||||
build.add(x4, rBase, uint16_t(vmRegOp(inst.d) * sizeof(TValue)));
|
||||
else if (inst.d.kind == IrOpKind::VmConst)
|
||||
emitAddOffset(build, x4, rConstants, vmConstOp(inst.d) * sizeof(TValue));
|
||||
else
|
||||
CODEGEN_ASSERT(inst.d.kind == IrOpKind::Undef);
|
||||
}
|
||||
|
||||
// nparams
|
||||
if (intOp(inst.f) == LUA_MULTRET)
|
||||
{
|
||||
// L->top - (ra + 1)
|
||||
build.ldr(x5, mem(rState, offsetof(lua_State, top)));
|
||||
build.sub(x5, x5, rBase);
|
||||
build.sub(x5, x5, uint16_t((vmRegOp(inst.b) + 1) * sizeof(TValue)));
|
||||
build.lsr(x5, x5, kTValueSizeLog2);
|
||||
}
|
||||
else
|
||||
build.mov(w5, intOp(inst.f));
|
||||
build.ldr(temp, mem(rBase, vmRegOp(inst.e) * sizeof(TValue)));
|
||||
build.str(temp, mem(x4, sizeof(TValue)));
|
||||
}
|
||||
else
|
||||
{
|
||||
regs.spill(build, index);
|
||||
build.mov(x0, rState);
|
||||
build.add(x1, rBase, uint16_t(vmRegOp(inst.b) * sizeof(TValue)));
|
||||
build.add(x2, rBase, uint16_t(vmRegOp(inst.c) * sizeof(TValue)));
|
||||
build.mov(w3, intOp(inst.f)); // nresults
|
||||
|
||||
if (inst.d.kind == IrOpKind::VmReg)
|
||||
build.add(x4, rBase, uint16_t(vmRegOp(inst.d) * sizeof(TValue)));
|
||||
else if (inst.d.kind == IrOpKind::VmConst)
|
||||
emitAddOffset(build, x4, rConstants, vmConstOp(inst.d) * sizeof(TValue));
|
||||
else
|
||||
CODEGEN_ASSERT(inst.d.kind == IrOpKind::Undef);
|
||||
|
||||
// nparams
|
||||
if (intOp(inst.e) == LUA_MULTRET)
|
||||
{
|
||||
// L->top - (ra + 1)
|
||||
build.ldr(x5, mem(rState, offsetof(lua_State, top)));
|
||||
build.sub(x5, x5, rBase);
|
||||
build.sub(x5, x5, uint16_t((vmRegOp(inst.b) + 1) * sizeof(TValue)));
|
||||
build.lsr(x5, x5, kTValueSizeLog2);
|
||||
}
|
||||
else
|
||||
build.mov(w5, intOp(inst.e));
|
||||
}
|
||||
|
||||
// nparams
|
||||
if (intOp(inst.f) == LUA_MULTRET)
|
||||
{
|
||||
// L->top - (ra + 1)
|
||||
build.ldr(x5, mem(rState, offsetof(lua_State, top)));
|
||||
build.sub(x5, x5, rBase);
|
||||
build.sub(x5, x5, uint16_t((vmRegOp(inst.b) + 1) * sizeof(TValue)));
|
||||
build.lsr(x5, x5, kTValueSizeLog2);
|
||||
}
|
||||
else
|
||||
build.mov(w5, intOp(inst.f));
|
||||
|
||||
build.ldr(x6, mem(rNativeContext, offsetof(NativeContext, luauF_table) + uintOp(inst.a) * sizeof(luau_FastFunction)));
|
||||
build.blr(x6);
|
||||
|
||||
|
@ -15,9 +15,6 @@
|
||||
#include "lstate.h"
|
||||
#include "lgc.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauCodegenFastcall3)
|
||||
LUAU_FASTFLAG(LuauCodegenMathSign)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
namespace CodeGen
|
||||
@ -596,8 +593,6 @@ void IrLoweringX64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
||||
break;
|
||||
case IrCmd::SIGN_NUM:
|
||||
{
|
||||
CODEGEN_ASSERT(FFlag::LuauCodegenMathSign);
|
||||
|
||||
inst.regX64 = regs.allocRegOrReuse(SizeX64::xmmword, index, {inst.a});
|
||||
|
||||
ScopedRegX64 tmp0{regs, SizeX64::xmmword};
|
||||
@ -1038,10 +1033,7 @@ void IrLoweringX64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
||||
|
||||
case IrCmd::FASTCALL:
|
||||
{
|
||||
if (FFlag::LuauCodegenFastcall3)
|
||||
emitBuiltin(regs, build, uintOp(inst.a), vmRegOp(inst.b), vmRegOp(inst.c), intOp(inst.d));
|
||||
else
|
||||
emitBuiltin(regs, build, uintOp(inst.a), vmRegOp(inst.b), vmRegOp(inst.c), intOp(inst.f));
|
||||
emitBuiltin(regs, build, uintOp(inst.a), vmRegOp(inst.b), vmRegOp(inst.c), intOp(inst.d));
|
||||
break;
|
||||
}
|
||||
case IrCmd::INVOKE_FASTCALL:
|
||||
@ -1052,7 +1044,7 @@ void IrLoweringX64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
||||
ScopedRegX64 argsAlt{regs};
|
||||
|
||||
// 'E' argument can only be produced by LOP_FASTCALL3
|
||||
if (FFlag::LuauCodegenFastcall3 && inst.e.kind != IrOpKind::Undef)
|
||||
if (inst.e.kind != IrOpKind::Undef)
|
||||
{
|
||||
CODEGEN_ASSERT(intOp(inst.f) == 3);
|
||||
|
||||
@ -1079,8 +1071,8 @@ void IrLoweringX64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
||||
|
||||
int ra = vmRegOp(inst.b);
|
||||
int arg = vmRegOp(inst.c);
|
||||
int nparams = intOp(FFlag::LuauCodegenFastcall3 ? inst.f : inst.e);
|
||||
int nresults = intOp(FFlag::LuauCodegenFastcall3 ? inst.g : inst.f);
|
||||
int nparams = intOp(inst.f);
|
||||
int nresults = intOp(inst.g);
|
||||
|
||||
IrCallWrapperX64 callWrap(regs, build, index);
|
||||
callWrap.addArgument(SizeX64::qword, rState);
|
||||
@ -1088,7 +1080,7 @@ void IrLoweringX64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
||||
callWrap.addArgument(SizeX64::qword, luauRegAddress(arg));
|
||||
callWrap.addArgument(SizeX64::dword, nresults);
|
||||
|
||||
if (FFlag::LuauCodegenFastcall3 && inst.e.kind != IrOpKind::Undef)
|
||||
if (inst.e.kind != IrOpKind::Undef)
|
||||
callWrap.addArgument(SizeX64::qword, argsAlt);
|
||||
else
|
||||
callWrap.addArgument(SizeX64::qword, args);
|
||||
|
@ -8,9 +8,6 @@
|
||||
|
||||
#include <math.h>
|
||||
|
||||
LUAU_FASTFLAG(LuauCodegenFastcall3)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCodegenMathSign, false)
|
||||
|
||||
// TODO: when nresults is less than our actual result count, we can skip computing/writing unused results
|
||||
|
||||
static const int kMinMaxUnrolledParams = 5;
|
||||
@ -39,33 +36,6 @@ static IrOp builtinLoadDouble(IrBuilder& build, IrOp arg)
|
||||
|
||||
// Wrapper code for all builtins with a fixed signature and manual assembly lowering of the body
|
||||
|
||||
// (number, ...) -> number
|
||||
static BuiltinImplResult translateBuiltinNumberToNumber(
|
||||
IrBuilder& build,
|
||||
LuauBuiltinFunction bfid,
|
||||
int nparams,
|
||||
int ra,
|
||||
int arg,
|
||||
IrOp args,
|
||||
int nresults,
|
||||
int pcpos
|
||||
)
|
||||
{
|
||||
CODEGEN_ASSERT(!FFlag::LuauCodegenMathSign);
|
||||
|
||||
if (nparams < 1 || nresults > 1)
|
||||
return {BuiltinImplType::None, -1};
|
||||
|
||||
builtinCheckDouble(build, build.vmReg(arg), pcpos);
|
||||
|
||||
if (FFlag::LuauCodegenFastcall3)
|
||||
build.inst(IrCmd::FASTCALL, build.constUint(bfid), build.vmReg(ra), build.vmReg(arg), build.constInt(1));
|
||||
else
|
||||
build.inst(IrCmd::FASTCALL, build.constUint(bfid), build.vmReg(ra), build.vmReg(arg), args, build.constInt(1), build.constInt(1));
|
||||
|
||||
return {BuiltinImplType::Full, 1};
|
||||
}
|
||||
|
||||
static BuiltinImplResult translateBuiltinNumberToNumberLibm(
|
||||
IrBuilder& build,
|
||||
LuauBuiltinFunction bfid,
|
||||
@ -142,18 +112,7 @@ static BuiltinImplResult translateBuiltinNumberTo2Number(
|
||||
|
||||
builtinCheckDouble(build, build.vmReg(arg), pcpos);
|
||||
|
||||
if (FFlag::LuauCodegenFastcall3)
|
||||
build.inst(IrCmd::FASTCALL, build.constUint(bfid), build.vmReg(ra), build.vmReg(arg), build.constInt(nresults == 1 ? 1 : 2));
|
||||
else
|
||||
build.inst(
|
||||
IrCmd::FASTCALL,
|
||||
build.constUint(bfid),
|
||||
build.vmReg(ra),
|
||||
build.vmReg(arg),
|
||||
build.undef(),
|
||||
build.constInt(1),
|
||||
build.constInt(nresults == 1 ? 1 : 2)
|
||||
);
|
||||
build.inst(IrCmd::FASTCALL, build.constUint(bfid), build.vmReg(ra), build.vmReg(arg), build.constInt(nresults == 1 ? 1 : 2));
|
||||
|
||||
return {BuiltinImplType::Full, 2};
|
||||
}
|
||||
@ -250,10 +209,10 @@ static BuiltinImplResult translateBuiltinMathMinMax(
|
||||
builtinCheckDouble(build, build.vmReg(arg), pcpos);
|
||||
builtinCheckDouble(build, args, pcpos);
|
||||
|
||||
if (FFlag::LuauCodegenFastcall3 && nparams >= 3)
|
||||
if (nparams >= 3)
|
||||
builtinCheckDouble(build, arg3, pcpos);
|
||||
|
||||
for (int i = (FFlag::LuauCodegenFastcall3 ? 4 : 3); i <= nparams; ++i)
|
||||
for (int i = 4; i <= nparams; ++i)
|
||||
builtinCheckDouble(build, build.vmReg(vmRegOp(args) + (i - 2)), pcpos);
|
||||
|
||||
IrOp varg1 = builtinLoadDouble(build, build.vmReg(arg));
|
||||
@ -261,13 +220,13 @@ static BuiltinImplResult translateBuiltinMathMinMax(
|
||||
|
||||
IrOp res = build.inst(cmd, varg2, varg1); // Swapped arguments are required for consistency with VM builtins
|
||||
|
||||
if (FFlag::LuauCodegenFastcall3 && nparams >= 3)
|
||||
if (nparams >= 3)
|
||||
{
|
||||
IrOp arg = builtinLoadDouble(build, arg3);
|
||||
res = build.inst(cmd, arg, res);
|
||||
}
|
||||
|
||||
for (int i = (FFlag::LuauCodegenFastcall3 ? 4 : 3); i <= nparams; ++i)
|
||||
for (int i = 4; i <= nparams; ++i)
|
||||
{
|
||||
IrOp arg = builtinLoadDouble(build, build.vmReg(vmRegOp(args) + (i - 2)));
|
||||
res = build.inst(cmd, arg, res);
|
||||
@ -302,10 +261,10 @@ static BuiltinImplResult translateBuiltinMathClamp(
|
||||
|
||||
builtinCheckDouble(build, build.vmReg(arg), pcpos);
|
||||
builtinCheckDouble(build, args, pcpos);
|
||||
builtinCheckDouble(build, FFlag::LuauCodegenFastcall3 ? arg3 : build.vmReg(vmRegOp(args) + 1), pcpos);
|
||||
builtinCheckDouble(build, arg3, pcpos);
|
||||
|
||||
IrOp min = builtinLoadDouble(build, args);
|
||||
IrOp max = builtinLoadDouble(build, FFlag::LuauCodegenFastcall3 ? arg3 : build.vmReg(vmRegOp(args) + 1));
|
||||
IrOp max = builtinLoadDouble(build, arg3);
|
||||
|
||||
build.inst(IrCmd::JUMP_CMP_NUM, min, max, build.cond(IrCondition::NotLessEqual), fallback, block);
|
||||
build.beginBlock(block);
|
||||
@ -386,10 +345,10 @@ static BuiltinImplResult translateBuiltinBit32BinaryOp(
|
||||
builtinCheckDouble(build, build.vmReg(arg), pcpos);
|
||||
builtinCheckDouble(build, args, pcpos);
|
||||
|
||||
if (FFlag::LuauCodegenFastcall3 && nparams >= 3)
|
||||
if (nparams >= 3)
|
||||
builtinCheckDouble(build, arg3, pcpos);
|
||||
|
||||
for (int i = (FFlag::LuauCodegenFastcall3 ? 4 : 3); i <= nparams; ++i)
|
||||
for (int i = 4; i <= nparams; ++i)
|
||||
builtinCheckDouble(build, build.vmReg(vmRegOp(args) + (i - 2)), pcpos);
|
||||
|
||||
IrOp va = builtinLoadDouble(build, build.vmReg(arg));
|
||||
@ -400,7 +359,7 @@ static BuiltinImplResult translateBuiltinBit32BinaryOp(
|
||||
|
||||
IrOp res = build.inst(cmd, vaui, vbui);
|
||||
|
||||
if (FFlag::LuauCodegenFastcall3 && nparams >= 3)
|
||||
if (nparams >= 3)
|
||||
{
|
||||
IrOp vc = builtinLoadDouble(build, arg3);
|
||||
IrOp arg = build.inst(IrCmd::NUM_TO_UINT, vc);
|
||||
@ -408,7 +367,7 @@ static BuiltinImplResult translateBuiltinBit32BinaryOp(
|
||||
res = build.inst(cmd, res, arg);
|
||||
}
|
||||
|
||||
for (int i = (FFlag::LuauCodegenFastcall3 ? 4 : 3); i <= nparams; ++i)
|
||||
for (int i = 4; i <= nparams; ++i)
|
||||
{
|
||||
IrOp vc = builtinLoadDouble(build, build.vmReg(vmRegOp(args) + (i - 2)));
|
||||
IrOp arg = build.inst(IrCmd::NUM_TO_UINT, vc);
|
||||
@ -599,8 +558,8 @@ static BuiltinImplResult translateBuiltinBit32Extract(
|
||||
{
|
||||
IrOp f = build.inst(IrCmd::NUM_TO_INT, vb);
|
||||
|
||||
builtinCheckDouble(build, FFlag::LuauCodegenFastcall3 ? arg3 : build.vmReg(args.index + 1), pcpos);
|
||||
IrOp vc = builtinLoadDouble(build, FFlag::LuauCodegenFastcall3 ? arg3 : build.vmReg(args.index + 1));
|
||||
builtinCheckDouble(build, arg3, pcpos);
|
||||
IrOp vc = builtinLoadDouble(build, arg3);
|
||||
IrOp w = build.inst(IrCmd::NUM_TO_INT, vc);
|
||||
|
||||
IrOp block1 = build.block(IrBlockKind::Internal);
|
||||
@ -705,11 +664,11 @@ static BuiltinImplResult translateBuiltinBit32Replace(
|
||||
|
||||
builtinCheckDouble(build, build.vmReg(arg), pcpos);
|
||||
builtinCheckDouble(build, args, pcpos);
|
||||
builtinCheckDouble(build, FFlag::LuauCodegenFastcall3 ? arg3 : build.vmReg(args.index + 1), pcpos);
|
||||
builtinCheckDouble(build, arg3, pcpos);
|
||||
|
||||
IrOp va = builtinLoadDouble(build, build.vmReg(arg));
|
||||
IrOp vb = builtinLoadDouble(build, args);
|
||||
IrOp vc = builtinLoadDouble(build, FFlag::LuauCodegenFastcall3 ? arg3 : build.vmReg(args.index + 1));
|
||||
IrOp vc = builtinLoadDouble(build, arg3);
|
||||
|
||||
IrOp n = build.inst(IrCmd::NUM_TO_UINT, va);
|
||||
IrOp v = build.inst(IrCmd::NUM_TO_UINT, vb);
|
||||
@ -734,8 +693,8 @@ static BuiltinImplResult translateBuiltinBit32Replace(
|
||||
}
|
||||
else
|
||||
{
|
||||
builtinCheckDouble(build, FFlag::LuauCodegenFastcall3 ? build.vmReg(vmRegOp(args) + 2) : build.vmReg(args.index + 2), pcpos);
|
||||
IrOp vd = builtinLoadDouble(build, FFlag::LuauCodegenFastcall3 ? build.vmReg(vmRegOp(args) + 2) : build.vmReg(args.index + 2));
|
||||
builtinCheckDouble(build, build.vmReg(vmRegOp(args) + 2), pcpos);
|
||||
IrOp vd = builtinLoadDouble(build, build.vmReg(vmRegOp(args) + 2));
|
||||
IrOp w = build.inst(IrCmd::NUM_TO_INT, vd);
|
||||
|
||||
IrOp block1 = build.block(IrBlockKind::Internal);
|
||||
@ -781,11 +740,11 @@ static BuiltinImplResult translateBuiltinVector(IrBuilder& build, int nparams, i
|
||||
|
||||
builtinCheckDouble(build, build.vmReg(arg), pcpos);
|
||||
builtinCheckDouble(build, args, pcpos);
|
||||
builtinCheckDouble(build, FFlag::LuauCodegenFastcall3 ? arg3 : build.vmReg(vmRegOp(args) + 1), pcpos);
|
||||
builtinCheckDouble(build, arg3, pcpos);
|
||||
|
||||
IrOp x = builtinLoadDouble(build, build.vmReg(arg));
|
||||
IrOp y = builtinLoadDouble(build, args);
|
||||
IrOp z = builtinLoadDouble(build, FFlag::LuauCodegenFastcall3 ? arg3 : build.vmReg(vmRegOp(args) + 1));
|
||||
IrOp z = builtinLoadDouble(build, arg3);
|
||||
|
||||
build.inst(IrCmd::STORE_VECTOR, build.vmReg(ra), x, y, z);
|
||||
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TVECTOR));
|
||||
@ -863,7 +822,7 @@ static void translateBufferArgsAndCheckBounds(
|
||||
builtinCheckDouble(build, args, pcpos);
|
||||
|
||||
if (nparams == 3)
|
||||
builtinCheckDouble(build, FFlag::LuauCodegenFastcall3 ? arg3 : build.vmReg(vmRegOp(args) + 1), pcpos);
|
||||
builtinCheckDouble(build, arg3, pcpos);
|
||||
|
||||
buf = build.inst(IrCmd::LOAD_POINTER, build.vmReg(arg));
|
||||
|
||||
@ -920,7 +879,7 @@ static BuiltinImplResult translateBuiltinBufferWrite(
|
||||
IrOp buf, intIndex;
|
||||
translateBufferArgsAndCheckBounds(build, nparams, arg, args, arg3, size, pcpos, buf, intIndex);
|
||||
|
||||
IrOp numValue = builtinLoadDouble(build, FFlag::LuauCodegenFastcall3 ? arg3 : build.vmReg(vmRegOp(args) + 1));
|
||||
IrOp numValue = builtinLoadDouble(build, arg3);
|
||||
build.inst(writeCmd, buf, intIndex, convCmd == IrCmd::NOP ? numValue : build.inst(convCmd, numValue));
|
||||
|
||||
return {BuiltinImplType::Full, 0};
|
||||
@ -982,10 +941,7 @@ BuiltinImplResult translateBuiltin(
|
||||
case LBF_MATH_LOG10:
|
||||
return translateBuiltinNumberToNumberLibm(build, LuauBuiltinFunction(bfid), nparams, ra, arg, nresults, pcpos);
|
||||
case LBF_MATH_SIGN:
|
||||
if (FFlag::LuauCodegenMathSign)
|
||||
return translateBuiltinMathUnary(build, IrCmd::SIGN_NUM, nparams, ra, arg, nresults, pcpos);
|
||||
else
|
||||
return translateBuiltinNumberToNumber(build, LuauBuiltinFunction(bfid), nparams, ra, arg, args, nresults, pcpos);
|
||||
return translateBuiltinMathUnary(build, IrCmd::SIGN_NUM, nparams, ra, arg, nresults, pcpos);
|
||||
case LBF_MATH_POW:
|
||||
case LBF_MATH_FMOD:
|
||||
case LBF_MATH_ATAN2:
|
||||
|
@ -13,8 +13,6 @@
|
||||
#include "lstate.h"
|
||||
#include "ltm.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauCodegenFastcall3)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
namespace CodeGen
|
||||
@ -767,7 +765,7 @@ IrOp translateFastCallN(IrBuilder& build, const Instruction* pc, int pcpos, bool
|
||||
builtinArgs = build.constDouble(protok.value.n);
|
||||
}
|
||||
|
||||
IrOp builtinArg3 = FFlag::LuauCodegenFastcall3 ? (customParams ? customArg3 : build.vmReg(ra + 3)) : IrOp{};
|
||||
IrOp builtinArg3 = customParams ? customArg3 : build.vmReg(ra + 3);
|
||||
|
||||
IrOp fallback = build.block(IrBlockKind::Fallback);
|
||||
|
||||
@ -793,7 +791,7 @@ IrOp translateFastCallN(IrBuilder& build, const Instruction* pc, int pcpos, bool
|
||||
return build.undef();
|
||||
}
|
||||
}
|
||||
else if (FFlag::LuauCodegenFastcall3)
|
||||
else
|
||||
{
|
||||
IrOp arg3 = customParams ? customArg3 : build.undef();
|
||||
|
||||
@ -817,21 +815,6 @@ IrOp translateFastCallN(IrBuilder& build, const Instruction* pc, int pcpos, bool
|
||||
else if (nparams == LUA_MULTRET)
|
||||
build.inst(IrCmd::ADJUST_STACK_TO_TOP);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: we can skip saving pc for some well-behaved builtins which we didn't inline
|
||||
build.inst(IrCmd::SET_SAVEDPC, build.constUint(pcpos + getOpLength(opcode)));
|
||||
|
||||
IrOp res = build.inst(
|
||||
IrCmd::INVOKE_FASTCALL, build.constUint(bfid), build.vmReg(ra), build.vmReg(arg), args, build.constInt(nparams), build.constInt(nresults)
|
||||
);
|
||||
build.inst(IrCmd::CHECK_FASTCALL_RES, res, fallback);
|
||||
|
||||
if (nresults == LUA_MULTRET)
|
||||
build.inst(IrCmd::ADJUST_STACK_TO_REG, build.vmReg(ra), res);
|
||||
else if (nparams == LUA_MULTRET)
|
||||
build.inst(IrCmd::ADJUST_STACK_TO_TOP);
|
||||
}
|
||||
|
||||
return fallback;
|
||||
}
|
||||
|
@ -3,8 +3,6 @@
|
||||
|
||||
#include "Luau/IrUtils.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauCodegenFastcall3)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
namespace CodeGen
|
||||
@ -46,11 +44,11 @@ void IrValueLocationTracking::beforeInstLowering(IrInst& inst)
|
||||
invalidateRestoreVmRegs(vmRegOp(inst.a), -1);
|
||||
break;
|
||||
case IrCmd::FASTCALL:
|
||||
invalidateRestoreVmRegs(vmRegOp(inst.b), function.intOp(FFlag::LuauCodegenFastcall3 ? inst.d : inst.f));
|
||||
invalidateRestoreVmRegs(vmRegOp(inst.b), function.intOp(inst.d));
|
||||
break;
|
||||
case IrCmd::INVOKE_FASTCALL:
|
||||
// Multiple return sequences (count == -1) are defined by ADJUST_STACK_TO_REG
|
||||
if (int count = function.intOp(FFlag::LuauCodegenFastcall3 ? inst.g : inst.f); count != -1)
|
||||
if (int count = function.intOp(inst.g); count != -1)
|
||||
invalidateRestoreVmRegs(vmRegOp(inst.b), count);
|
||||
break;
|
||||
case IrCmd::DO_ARITH:
|
||||
|
@ -18,8 +18,6 @@ LUAU_FASTINTVARIABLE(LuauCodeGenMinLinearBlockPath, 3)
|
||||
LUAU_FASTINTVARIABLE(LuauCodeGenReuseSlotLimit, 64)
|
||||
LUAU_FASTINTVARIABLE(LuauCodeGenReuseUdataTagLimit, 64)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauAbortingChecks, false)
|
||||
LUAU_FASTFLAG(LuauCodegenFastcall3)
|
||||
LUAU_FASTFLAG(LuauCodegenMathSign)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
@ -1119,7 +1117,7 @@ static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction&
|
||||
{
|
||||
LuauBuiltinFunction bfid = LuauBuiltinFunction(function.uintOp(inst.a));
|
||||
int firstReturnReg = vmRegOp(inst.b);
|
||||
int nresults = function.intOp(FFlag::LuauCodegenFastcall3 ? inst.d : inst.f);
|
||||
int nresults = function.intOp(inst.d);
|
||||
|
||||
// TODO: FASTCALL is more restrictive than INVOKE_FASTCALL; we should either determine the exact semantics, or rework it
|
||||
handleBuiltinEffects(state, bfid, firstReturnReg, nresults);
|
||||
@ -1133,19 +1131,13 @@ static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction&
|
||||
if (nresults > 1)
|
||||
state.updateTag(IrOp{IrOpKind::VmReg, uint8_t(firstReturnReg + 1)}, LUA_TNUMBER);
|
||||
break;
|
||||
case LBF_MATH_SIGN:
|
||||
CODEGEN_ASSERT(!FFlag::LuauCodegenMathSign);
|
||||
state.updateTag(IrOp{IrOpKind::VmReg, uint8_t(firstReturnReg)}, LUA_TNUMBER);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IrCmd::INVOKE_FASTCALL:
|
||||
handleBuiltinEffects(
|
||||
state, LuauBuiltinFunction(function.uintOp(inst.a)), vmRegOp(inst.b), function.intOp(FFlag::LuauCodegenFastcall3 ? inst.g : inst.f)
|
||||
);
|
||||
handleBuiltinEffects(state, LuauBuiltinFunction(function.uintOp(inst.a)), vmRegOp(inst.b), function.intOp(inst.g));
|
||||
break;
|
||||
|
||||
// These instructions don't have an effect on register/memory state we are tracking
|
||||
|
@ -13,7 +13,7 @@ inline bool isFlagExperimental(const char* flag)
|
||||
static const char* const kList[] = {
|
||||
"LuauInstantiateInSubtyping", // requires some fixes to lua-apps code
|
||||
"LuauFixIndexerSubtypingOrdering", // requires some small fixes to lua-apps code since this fixes a false negative
|
||||
"StudioReportLuauAny", // takes telemetry data for usage of any types
|
||||
"StudioReportLuauAny2", // takes telemetry data for usage of any types
|
||||
// makes sure we always have at least one entry
|
||||
nullptr,
|
||||
};
|
||||
|
@ -5,7 +5,6 @@
|
||||
#include "Luau/Id.h"
|
||||
#include "Luau/Language.h"
|
||||
#include "Luau/UnionFind.h"
|
||||
#include "Luau/VecDeque.h"
|
||||
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
@ -145,7 +144,7 @@ private:
|
||||
/// The hashcons 𝐻 is a map from e-nodes to e-class ids.
|
||||
std::unordered_map<L, Id, typename L::Hash> hashcons;
|
||||
|
||||
VecDeque<std::pair<L, Id>> worklist;
|
||||
std::vector<std::pair<L, Id>> worklist;
|
||||
|
||||
private:
|
||||
void canonicalize(L& enode)
|
||||
@ -183,7 +182,7 @@ private:
|
||||
for (Id operand : enode.operands())
|
||||
get(operand).parents.push_back({enode, id});
|
||||
|
||||
worklist.push_back({enode, id});
|
||||
worklist.emplace_back(enode, id);
|
||||
hashcons.insert_or_assign(enode, id);
|
||||
|
||||
return id;
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include "Luau/Variant.h"
|
||||
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
@ -233,12 +232,6 @@ struct Language final
|
||||
{
|
||||
}
|
||||
|
||||
Language(const Language&) noexcept = default;
|
||||
Language& operator=(const Language&) noexcept = default;
|
||||
|
||||
Language(Language&&) noexcept = default;
|
||||
Language& operator=(Language&&) noexcept = default;
|
||||
|
||||
int index() const noexcept
|
||||
{
|
||||
return v.index();
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace Luau::EqSat
|
||||
{
|
||||
|
@ -11,6 +11,7 @@ Id UnionFind::makeSet()
|
||||
Id id{parents.size()};
|
||||
parents.push_back(id);
|
||||
ranks.push_back(0);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
@ -32,6 +33,7 @@ Id UnionFind::find(Id id)
|
||||
parents[size_t(id)] = set;
|
||||
id = parent;
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
@ -47,6 +49,7 @@ void UnionFind::merge(Id a, Id b)
|
||||
std::swap(aSet, bSet);
|
||||
|
||||
parents[size_t(bSet)] = aSet;
|
||||
|
||||
if (ranks[size_t(aSet)] == ranks[size_t(bSet)])
|
||||
ranks[size_t(aSet)]++;
|
||||
}
|
||||
|
3
Makefile
3
Makefile
@ -181,8 +181,7 @@ coverage: $(TESTS_TARGET) $(COMPILE_CLI_TARGET)
|
||||
mv default.profraw tests.profraw
|
||||
$(TESTS_TARGET) --fflags=true
|
||||
mv default.profraw tests-flags.profraw
|
||||
# new solver is expected to fail tests at the moment, remove '!' once tests are fixed and this starts to fail
|
||||
! $(TESTS_TARGET) --fflags=true,DebugLuauDeferredConstraintResolution=true
|
||||
$(TESTS_TARGET) --fflags=true,DebugLuauDeferredConstraintResolution=true
|
||||
mv default.profraw tests-dcr.profraw
|
||||
$(TESTS_TARGET) -ts=Conformance --codegen
|
||||
mv default.profraw codegen.profraw
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include "Fixture.h"
|
||||
|
||||
#include "ScopedFlags.h"
|
||||
#include "doctest.h"
|
||||
|
||||
#include <algorithm>
|
||||
@ -14,14 +15,13 @@ using namespace Luau;
|
||||
using Pattern = AnyTypeSummary::Pattern;
|
||||
|
||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
|
||||
LUAU_FASTFLAG(DebugLuauFreezeArena);
|
||||
LUAU_FASTFLAG(DebugLuauMagicTypes);
|
||||
LUAU_FASTFLAG(DebugLuauFreezeArena)
|
||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||
LUAU_FASTFLAG(StudioReportLuauAny2)
|
||||
|
||||
LUAU_FASTFLAG(StudioReportLuauAny);
|
||||
|
||||
struct ATSFixture : BuiltinsFixture
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true};
|
||||
|
||||
ATSFixture()
|
||||
{
|
||||
@ -34,6 +34,11 @@ TEST_SUITE_BEGIN("AnyTypeSummaryTest");
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "var_typepack_any")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
type A = (number, string) -> ...any
|
||||
)";
|
||||
@ -43,16 +48,18 @@ type A = (number, string) -> ...any
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 1);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "type A = (number, string)->( ...any)");
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 1);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "type A = (number, string)->( ...any)");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "export_alias")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
export type t8<t8> = t0 &(<t0 ...>(true | any)->(''))
|
||||
)";
|
||||
@ -62,16 +69,18 @@ export type t8<t8> = t0 &(<t0 ...>(true | any)->(''))
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 1);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "export type t8<t8> = t0 &(<t0 ...>(true | any)->(''))");
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 1);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "export type t8<t8> = t0 &(<t0 ...>(true | any)->(''))");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "typepacks")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
local function fallible(t: number): ...any
|
||||
if t > 0 then
|
||||
@ -86,16 +95,18 @@ end
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 3);
|
||||
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.size() == 3);
|
||||
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");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "typepacks_no_ret")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
-- TODO: if partially typed, we'd want to know too
|
||||
local function fallible(t: number)
|
||||
@ -111,14 +122,16 @@ end
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 0);
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "var_typepack_any_gen_table")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
type Pair<T> = {first: T, second: any}
|
||||
)";
|
||||
@ -128,16 +141,18 @@ type Pair<T> = {first: T, second: any}
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 1);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "type Pair<T> = {first: T, second: any}");
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 1);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "type Pair<T> = {first: T, second: any}");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "assign_uneq")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/B"] = R"(
|
||||
local function greetings(name: string)
|
||||
return "Hello, " .. name, nil
|
||||
@ -152,14 +167,16 @@ local x, y, z = greetings("Dibri") -- mismatch
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result1);
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/B");
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 0);
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "var_typepack_any_gen")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
-- type Pair<T> = (boolean, string, ...any) -> {T} -- type aliases with generics/pack do not seem to be processed?
|
||||
type Pair<T> = (boolean, T) -> ...any
|
||||
@ -170,16 +187,18 @@ type Pair<T> = (boolean, T) -> ...any
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 1);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "type Pair<T> = (boolean, T)->( ...any)");
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 1);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "type Pair<T> = (boolean, T)->( ...any)");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "typeof_any_in_func")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
local function f()
|
||||
local a: any = 1
|
||||
@ -192,16 +211,18 @@ TEST_CASE_FIXTURE(ATSFixture, "typeof_any_in_func")
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 2);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::VarAnnot);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "local function f()\n local a: any = 1\n local b: typeof(a) = 1\n end");
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 2);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::VarAnnot);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "local function f()\n local a: any = 1\n local b: typeof(a) = 1\n end");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "generic_types")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
local function foo<A>(a: (...A) -> any, ...: A)
|
||||
return a(...)
|
||||
@ -220,16 +241,18 @@ foo(addNumbers)
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 3);
|
||||
LUAU_ASSERT(module->ats.typeInfo[1].code == Pattern::FuncApp);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "local function foo<A>(a: (...A)->( any),...: A)\n return a(...)\nend");
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 3);
|
||||
LUAU_ASSERT(module->ats.typeInfo[1].code == Pattern::FuncApp);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "local function foo<A>(a: (...A)->( any),...: A)\n return a(...)\nend");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "no_annot")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
local character = script.Parent
|
||||
)";
|
||||
@ -239,14 +262,16 @@ local character = script.Parent
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 0);
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "if_any")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
function f(x: any)
|
||||
if not x then
|
||||
@ -266,22 +291,24 @@ end
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 1);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg);
|
||||
LUAU_ASSERT(
|
||||
module->ats.typeInfo[0].node == "function f(x: any)\nif not x then\nx = {\n y = math.random(0, 2^31-1),\n left = nil,\n right = "
|
||||
"nil\n}\nelse\n local expected = x * 5\nend\nend"
|
||||
);
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 1);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg);
|
||||
LUAU_ASSERT(
|
||||
module->ats.typeInfo[0].node == "function f(x: any)\nif not x then\nx = {\n y = math.random(0, 2^31-1),\n left = nil,\n right = "
|
||||
"nil\n}\nelse\n local expected = x * 5\nend\nend"
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "variadic_any")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
local function f(): (number, ...any)
|
||||
return 1, 5
|
||||
return 1, 5 --catching this
|
||||
end
|
||||
|
||||
local x, y, z = f() -- not catching this any because no annot
|
||||
@ -292,16 +319,18 @@ TEST_CASE_FIXTURE(ATSFixture, "variadic_any")
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 1);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncRet);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "local function f(): (number, ...any)\n return 1, 5\n end");
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 2);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncRet);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "local function f(): (number, ...any)\n return 1, 5\n end");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "type_alias_intersection")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
type XCoord = {x: number}
|
||||
type YCoord = {y: any}
|
||||
@ -314,16 +343,18 @@ TEST_CASE_FIXTURE(ATSFixture, "type_alias_intersection")
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 3);
|
||||
LUAU_ASSERT(module->ats.typeInfo[2].code == Pattern::VarAnnot);
|
||||
LUAU_ASSERT(module->ats.typeInfo[2].node == "local vec2: Vector2 = {x = 1, y = 2}");
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 3);
|
||||
LUAU_ASSERT(module->ats.typeInfo[2].code == Pattern::VarAnnot);
|
||||
LUAU_ASSERT(module->ats.typeInfo[2].node == "local vec2: Vector2 = {x = 1, y = 2}");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "var_func_arg")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
local function f(...: any)
|
||||
end
|
||||
@ -340,16 +371,18 @@ TEST_CASE_FIXTURE(ATSFixture, "var_func_arg")
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 4);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::VarAny);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "local function f(...: any)\n end");
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 4);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::VarAny);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "local function f(...: any)\n end");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "var_func_apps")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
local function f(...: any)
|
||||
end
|
||||
@ -362,17 +395,19 @@ TEST_CASE_FIXTURE(ATSFixture, "var_func_apps")
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 3);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::VarAny);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "local function f(...: any)\n end");
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 3);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::VarAny);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "local function f(...: any)\n end");
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "CannotExtendTable")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
local CAR_COLLISION_GROUP = "Car"
|
||||
|
||||
@ -390,14 +425,16 @@ end
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 0);
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "unknown_symbol")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
local function manageRace(raceContainer: Model)
|
||||
RaceManager.new(raceContainer)
|
||||
@ -410,16 +447,18 @@ end
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 2);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "local function manageRace(raceContainer: Model)\n RaceManager.new(raceContainer)\nend");
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 2);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "local function manageRace(raceContainer: Model)\n RaceManager.new(raceContainer)\nend");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "racing_3_short")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
|
||||
local CollectionService = game:GetService("CollectionService")
|
||||
@ -449,16 +488,18 @@ initialize()
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 5);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "local function manageRace(raceContainer: Model)\n RaceManager.new(raceContainer)\nend");
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 5);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "local function manageRace(raceContainer: Model)\n RaceManager.new(raceContainer)\nend");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "racing_collision_2")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
local PhysicsService = game:GetService("PhysicsService")
|
||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
@ -524,22 +565,24 @@ initialize()
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 11);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg);
|
||||
LUAU_ASSERT(
|
||||
module->ats.typeInfo[0].node ==
|
||||
"local function onCharacterAdded(character: Model)\n\n character.DescendantAdded:Connect(function(descendant)\n if "
|
||||
"descendant:IsA('BasePart')then\n descendant.CollisionGroup = CHARACTER_COLLISION_GROUP\n end\n end)\n\n\n for _, descendant in "
|
||||
"character:GetDescendants()do\n if descendant:IsA('BasePart')then\n descendant.CollisionGroup = CHARACTER_COLLISION_GROUP\n end\n "
|
||||
"end\nend"
|
||||
);
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 11);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg);
|
||||
LUAU_ASSERT(
|
||||
module->ats.typeInfo[0].node ==
|
||||
"local function onCharacterAdded(character: Model)\n\n character.DescendantAdded:Connect(function(descendant)\n if "
|
||||
"descendant:IsA('BasePart')then\n descendant.CollisionGroup = CHARACTER_COLLISION_GROUP\n end\n end)\n\n\n for _, descendant in "
|
||||
"character:GetDescendants()do\n if descendant:IsA('BasePart')then\n descendant.CollisionGroup = CHARACTER_COLLISION_GROUP\n end\n "
|
||||
"end\nend"
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "racing_spawning_1")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
local CollectionService = game:GetService("CollectionService")
|
||||
local Players = game:GetService("Players")
|
||||
@ -593,23 +636,25 @@ initialize()
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 7);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg);
|
||||
LUAU_ASSERT(
|
||||
module->ats.typeInfo[0].node ==
|
||||
"local function setupKiosk(kiosk: Model)\n local spawnLocation = kiosk:FindFirstChild('SpawnLocation')\n assert(spawnLocation, "
|
||||
"`{kiosk:GetFullName()} has no SpawnLocation part`)\n local promptPart = kiosk:FindFirstChild('Prompt')\n assert(promptPart, "
|
||||
"`{kiosk:GetFullName()} has no Prompt part`)\n\n\n spawnLocation.Transparency = 1\n\n\n local spawnPrompt = "
|
||||
"spawnPromptTemplate:Clone()\n spawnPrompt.Parent = promptPart\n\n spawnPrompt.Triggered:Connect(function(player: Player)\n\n "
|
||||
"destroyPlayerCars(player)\n\n spawnCar(spawnLocation.CFrame, player)\n end)\nend"
|
||||
);
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 7);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg);
|
||||
LUAU_ASSERT(
|
||||
module->ats.typeInfo[0].node ==
|
||||
"local function setupKiosk(kiosk: Model)\n local spawnLocation = kiosk:FindFirstChild('SpawnLocation')\n assert(spawnLocation, "
|
||||
"`{kiosk:GetFullName()} has no SpawnLocation part`)\n local promptPart = kiosk:FindFirstChild('Prompt')\n assert(promptPart, "
|
||||
"`{kiosk:GetFullName()} has no Prompt part`)\n\n\n spawnLocation.Transparency = 1\n\n\n local spawnPrompt = "
|
||||
"spawnPromptTemplate:Clone()\n spawnPrompt.Parent = promptPart\n\n spawnPrompt.Triggered:Connect(function(player: Player)\n\n "
|
||||
"destroyPlayerCars(player)\n\n spawnCar(spawnLocation.CFrame, player)\n end)\nend"
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "mutually_recursive_generic")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
--!strict
|
||||
type T<a> = { f: a, g: U<a> }
|
||||
@ -625,14 +670,16 @@ TEST_CASE_FIXTURE(ATSFixture, "mutually_recursive_generic")
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 0);
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "explicit_pack")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
type Foo<T...> = (T...) -> () -- also want to see how these are used.
|
||||
type Bar = Foo<(number, any)>
|
||||
@ -643,16 +690,39 @@ type Bar = Foo<(number, any)>
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 1);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "type Bar = Foo<(number, any)>");
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 1);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "type Bar = Foo<(number, any)>");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "local_val")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
local a, b, c = 1 :: any
|
||||
)";
|
||||
|
||||
CheckResult result1 = frontend.check("game/Gui/Modules/A");
|
||||
LUAU_REQUIRE_NO_ERRORS(result1);
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 1);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Casts);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "local a, b, c = 1 :: any");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "var_any_local")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
local x = 2
|
||||
local x: any = 2, 3
|
||||
@ -665,16 +735,18 @@ local x: number, y: any, z, h: nil = 1, nil
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 3);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::VarAnnot);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "local x: any = 2, 3");
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 3);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::VarAnnot);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "local x: any = 2, 3");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "table_uses_any")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
local x: any = 0
|
||||
local y: number
|
||||
@ -686,16 +758,18 @@ TEST_CASE_FIXTURE(ATSFixture, "table_uses_any")
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 1);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::VarAnnot);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "local x: any = 0");
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 1);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::VarAnnot);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "local x: any = 0");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "typeof_any")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
local x: any = 0
|
||||
function some1(x: typeof(x))
|
||||
@ -707,16 +781,18 @@ TEST_CASE_FIXTURE(ATSFixture, "typeof_any")
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 2);
|
||||
LUAU_ASSERT(module->ats.typeInfo[1].code == Pattern::FuncArg);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "function some1(x: typeof(x))\n end");
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 2);
|
||||
LUAU_ASSERT(module->ats.typeInfo[1].code == Pattern::FuncArg);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "function some1(x: typeof(x))\n end");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "table_type_assigned")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
local x: { x: any?} = {x = 1}
|
||||
local z: { x : any, y : number? } -- not catching this
|
||||
@ -729,16 +805,18 @@ TEST_CASE_FIXTURE(ATSFixture, "table_type_assigned")
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 2);
|
||||
LUAU_ASSERT(module->ats.typeInfo[1].code == Pattern::Assign);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "local x: { x: any?} = {x = 1}");
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 2);
|
||||
LUAU_ASSERT(module->ats.typeInfo[1].code == Pattern::Assign);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "local x: { x: any?} = {x = 1}");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "simple_func_wo_ret")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
function some(x: any)
|
||||
end
|
||||
@ -749,16 +827,18 @@ TEST_CASE_FIXTURE(ATSFixture, "simple_func_wo_ret")
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 1);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "function some(x: any)\n end");
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 1);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "function some(x: any)\n end");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "simple_func_w_ret")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
function other(y: number): any
|
||||
return "gotcha!"
|
||||
@ -770,16 +850,18 @@ TEST_CASE_FIXTURE(ATSFixture, "simple_func_w_ret")
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 1);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncRet);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "function other(y: number): any\n return 'gotcha!'\n end");
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 1);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncRet);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "function other(y: number): any\n return 'gotcha!'\n end");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "nested_local")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
function cool(y: number): number
|
||||
local g: any = "gratatataaa"
|
||||
@ -792,16 +874,18 @@ TEST_CASE_FIXTURE(ATSFixture, "nested_local")
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 1);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::VarAnnot);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "function cool(y: number): number\n local g: any = 'gratatataaa'\n return y\n end");
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 1);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::VarAnnot);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "function cool(y: number): number\n local g: any = 'gratatataaa'\n return y\n end");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "generic_func")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
function reverse<T>(a: {T}, b: any): {T}
|
||||
return a
|
||||
@ -813,16 +897,18 @@ TEST_CASE_FIXTURE(ATSFixture, "generic_func")
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 1);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "function reverse<T>(a: {T}, b: any): {T}\n return a\n end");
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 1);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "function reverse<T>(a: {T}, b: any): {T}\n return a\n end");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "type_alias_any")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||
type Clear = any
|
||||
local z: Clear = "zip"
|
||||
@ -833,16 +919,18 @@ TEST_CASE_FIXTURE(ATSFixture, "type_alias_any")
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 2);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "type Clear = any");
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 2);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "type Clear = any");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "multi_module_any")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/A"] = R"(
|
||||
export type MyFunction = (number, string) -> (any)
|
||||
)";
|
||||
@ -864,16 +952,18 @@ TEST_CASE_FIXTURE(ATSFixture, "multi_module_any")
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/B");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 2);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "type Clear = any");
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 2);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias);
|
||||
LUAU_ASSERT(module->ats.typeInfo[0].node == "type Clear = any");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ATSFixture, "cast_on_cyclic_req")
|
||||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, true},
|
||||
{FFlag::StudioReportLuauAny2, true},
|
||||
};
|
||||
|
||||
fileResolver.source["game/A"] = R"(
|
||||
local a = require(script.Parent.B) -- not resolving this module
|
||||
export type MyFunction = (number, string) -> (any)
|
||||
@ -890,12 +980,9 @@ TEST_CASE_FIXTURE(ATSFixture, "cast_on_cyclic_req")
|
||||
|
||||
ModulePtr module = frontend.moduleResolver.getModule("game/B");
|
||||
|
||||
if (FFlag::StudioReportLuauAny)
|
||||
{
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 3);
|
||||
LUAU_ASSERT(module->ats.typeInfo[1].code == Pattern::Alias);
|
||||
LUAU_ASSERT(module->ats.typeInfo[1].node == "type Clear = any");
|
||||
}
|
||||
LUAU_ASSERT(module->ats.typeInfo.size() == 3);
|
||||
LUAU_ASSERT(module->ats.typeInfo[1].code == Pattern::Alias);
|
||||
LUAU_ASSERT(module->ats.typeInfo[1].node == "type Clear = any");
|
||||
}
|
||||
|
||||
|
||||
|
@ -1607,6 +1607,9 @@ return target(a.@1
|
||||
|
||||
TEST_CASE_FIXTURE(ACFixture, "type_correct_suggestion_in_table")
|
||||
{
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution) // CLI-116815 Autocomplete cannot suggest keys while autocompleting inside of a table
|
||||
return;
|
||||
|
||||
check(R"(
|
||||
type Foo = { a: number, b: string }
|
||||
local a = { one = 4, two = "hello" }
|
||||
@ -2261,6 +2264,9 @@ local ec = e(f@5)
|
||||
|
||||
TEST_CASE_FIXTURE(ACFixture, "type_correct_suggestion_for_overloads")
|
||||
{
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution) // CLI-116814 Autocomplete needs to populate expected types for function arguments correctly
|
||||
// (overloads and singletons)
|
||||
return;
|
||||
check(R"(
|
||||
local target: ((number) -> string) & ((string) -> number))
|
||||
|
||||
@ -2608,6 +2614,10 @@ end
|
||||
|
||||
TEST_CASE_FIXTURE(ACFixture, "suggest_table_keys")
|
||||
{
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution) // CLI-116812 AutocompleteTest.suggest_table_keys needs to populate expected types for nested
|
||||
// tables without an annotation
|
||||
return;
|
||||
|
||||
check(R"(
|
||||
type Test = { first: number, second: number }
|
||||
local t: Test = { f@1 }
|
||||
@ -3091,6 +3101,10 @@ TEST_CASE_FIXTURE(ACBuiltinsFixture, "autocomplete_on_string_singletons")
|
||||
|
||||
TEST_CASE_FIXTURE(ACFixture, "autocomplete_string_singletons")
|
||||
{
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution) // CLI-116814 Autocomplete needs to populate expected types for function arguments correctly
|
||||
// (overloads and singletons)
|
||||
return;
|
||||
|
||||
check(R"(
|
||||
type tag = "cat" | "dog"
|
||||
local function f(a: tag) end
|
||||
@ -4247,6 +4261,9 @@ foo(@1)
|
||||
|
||||
TEST_CASE_FIXTURE(ACFixture, "anonymous_autofilled_generic_type_pack_vararg")
|
||||
{
|
||||
// CLI-116932 - Autocomplete on a anonymous function in a function argument should not recommend a function with a generic parameter.
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
check(R"(
|
||||
local function foo<A>(a: (...A) -> number, ...: A)
|
||||
return a(...)
|
||||
@ -4276,7 +4293,8 @@ end
|
||||
foo(@1)
|
||||
)");
|
||||
|
||||
const std::optional<std::string> EXPECTED_INSERT = "function(...): number end";
|
||||
const std::optional<std::string> EXPECTED_INSERT =
|
||||
FFlag::DebugLuauDeferredConstraintResolution ? "function(...: number): number end" : "function(...): number end";
|
||||
|
||||
auto ac = autocomplete('1');
|
||||
|
||||
|
@ -35,6 +35,7 @@ LUAU_FASTFLAG(DebugLuauAbortingChecks)
|
||||
LUAU_FASTINT(CodegenHeuristicsInstructionLimit)
|
||||
LUAU_FASTFLAG(LuauNativeAttribute)
|
||||
LUAU_FASTFLAG(LuauPreserveLudataRenaming)
|
||||
LUAU_FASTFLAG(LuauCodegenArmNumToVecFix)
|
||||
|
||||
static lua_CompileOptions defaultOptions()
|
||||
{
|
||||
@ -809,6 +810,8 @@ TEST_CASE("Pack")
|
||||
|
||||
TEST_CASE("Vector")
|
||||
{
|
||||
ScopedFastFlag luauCodegenArmNumToVecFix{FFlag::LuauCodegenArmNumToVecFix, true};
|
||||
|
||||
lua_CompileOptions copts = defaultOptions();
|
||||
Luau::CodeGen::CompilationOptions nativeOpts = defaultCodegenOptions();
|
||||
|
||||
|
@ -13,8 +13,6 @@
|
||||
#include <limits.h>
|
||||
|
||||
LUAU_FASTFLAG(DebugLuauAbortingChecks)
|
||||
LUAU_FASTFLAG(LuauCodegenFastcall3)
|
||||
LUAU_FASTFLAG(LuauCodegenMathSign)
|
||||
|
||||
using namespace Luau::CodeGen;
|
||||
|
||||
@ -334,8 +332,6 @@ TEST_SUITE_BEGIN("ConstantFolding");
|
||||
|
||||
TEST_CASE_FIXTURE(IrBuilderFixture, "Numeric")
|
||||
{
|
||||
ScopedFastFlag luauCodegenMathSign{FFlag::LuauCodegenMathSign, true};
|
||||
|
||||
IrOp block = build.block(IrBlockKind::Internal);
|
||||
|
||||
build.beginBlock(block);
|
||||
@ -2631,8 +2627,6 @@ bb_1:
|
||||
|
||||
TEST_CASE_FIXTURE(IrBuilderFixture, "FastCallEffects1")
|
||||
{
|
||||
ScopedFastFlag luauCodegenFastcall3{FFlag::LuauCodegenFastcall3, true};
|
||||
|
||||
IrOp entry = build.block(IrBlockKind::Internal);
|
||||
|
||||
build.beginBlock(entry);
|
||||
@ -2656,8 +2650,6 @@ bb_0:
|
||||
|
||||
TEST_CASE_FIXTURE(IrBuilderFixture, "FastCallEffects2")
|
||||
{
|
||||
ScopedFastFlag luauCodegenFastcall3{FFlag::LuauCodegenFastcall3, true};
|
||||
|
||||
IrOp entry = build.block(IrBlockKind::Internal);
|
||||
|
||||
build.beginBlock(entry);
|
||||
@ -2852,8 +2844,6 @@ bb_1:
|
||||
|
||||
TEST_CASE_FIXTURE(IrBuilderFixture, "ExplicitUseOfRegisterInVarargSequence")
|
||||
{
|
||||
ScopedFastFlag luauCodegenFastcall3{FFlag::LuauCodegenFastcall3, true};
|
||||
|
||||
IrOp entry = build.block(IrBlockKind::Internal);
|
||||
IrOp exit = build.block(IrBlockKind::Internal);
|
||||
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
LUAU_FASTFLAG(LuauCompileUserdataInfo)
|
||||
LUAU_FASTFLAG(LuauCompileFastcall3)
|
||||
LUAU_FASTFLAG(LuauCodegenFastcall3)
|
||||
|
||||
static std::string getCodegenAssembly(const char* source, bool includeIrTypes = false, int debugLevel = 1)
|
||||
{
|
||||
@ -425,8 +424,6 @@ bb_5:
|
||||
|
||||
TEST_CASE("DseInitialStackState2")
|
||||
{
|
||||
ScopedFastFlag luauCodegenFastcall3{FFlag::LuauCodegenFastcall3, true};
|
||||
|
||||
CHECK_EQ(
|
||||
"\n" + getCodegenAssembly(R"(
|
||||
local function foo(a)
|
||||
@ -945,7 +942,7 @@ bb_bytecode_0:
|
||||
|
||||
TEST_CASE("FastcallTypeInferThroughLocal")
|
||||
{
|
||||
ScopedFastFlag sffs[]{{FFlag::LuauCompileFastcall3, true}, {FFlag::LuauCodegenFastcall3, true}};
|
||||
ScopedFastFlag sffs[]{{FFlag::LuauCompileFastcall3, true}};
|
||||
|
||||
CHECK_EQ(
|
||||
"\n" + getCodegenAssembly(
|
||||
@ -997,7 +994,7 @@ bb_bytecode_1:
|
||||
|
||||
TEST_CASE("FastcallTypeInferThroughUpvalue")
|
||||
{
|
||||
ScopedFastFlag sffs[]{{FFlag::LuauCompileFastcall3, true}, {FFlag::LuauCodegenFastcall3, true}};
|
||||
ScopedFastFlag sffs[]{{FFlag::LuauCompileFastcall3, true}};
|
||||
|
||||
CHECK_EQ(
|
||||
"\n" + getCodegenAssembly(
|
||||
@ -1127,7 +1124,7 @@ bb_bytecode_4:
|
||||
|
||||
TEST_CASE("ArgumentTypeRefinement")
|
||||
{
|
||||
ScopedFastFlag sffs[]{{FFlag::LuauCompileFastcall3, true}, {FFlag::LuauCodegenFastcall3, true}};
|
||||
ScopedFastFlag sffs[]{{FFlag::LuauCompileFastcall3, true}};
|
||||
|
||||
CHECK_EQ(
|
||||
"\n" + getCodegenAssembly(
|
||||
@ -1436,7 +1433,7 @@ bb_2:
|
||||
|
||||
TEST_CASE("UnaryTypeResolve")
|
||||
{
|
||||
ScopedFastFlag sffs[]{{FFlag::LuauCompileFastcall3, true}, {FFlag::LuauCodegenFastcall3, true}};
|
||||
ScopedFastFlag sffs[]{{FFlag::LuauCompileFastcall3, true}};
|
||||
|
||||
CHECK_EQ(
|
||||
"\n" + getCodegenHeader(R"(
|
||||
|
@ -316,6 +316,9 @@ TEST_CASE_FIXTURE(Fixture, "clone_free_tables")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "clone_self_property")
|
||||
{
|
||||
// CLI-117082 ModuleTests.clone_self_property we don't infer self correctly, instead replacing it with unknown.
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
fileResolver.source["Module/A"] = R"(
|
||||
--!nonstrict
|
||||
local a = {}
|
||||
|
@ -415,7 +415,15 @@ TEST_CASE_FIXTURE(IsSubtypeFixture, "error_suppression")
|
||||
CHECK(!isSubtype(any, unk));
|
||||
}
|
||||
|
||||
CHECK(!isSubtype(err, str));
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
CHECK(isSubtype(err, str));
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK(!isSubtype(err, str));
|
||||
}
|
||||
|
||||
CHECK(!isSubtype(str, err));
|
||||
|
||||
CHECK(!isSubtype(err, unk));
|
||||
@ -701,6 +709,10 @@ TEST_CASE_FIXTURE(Fixture, "higher_order_function")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "higher_order_function_with_annotation")
|
||||
{
|
||||
// CLI-117088 - Inferring the type of a higher order function with an annotation sometimes doesn't fully constrain the type (there are free types
|
||||
// left over).
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
check(R"(
|
||||
function apply<a, b>(f: (a) -> b, x)
|
||||
return f(x)
|
||||
|
@ -72,12 +72,12 @@ struct SubtypeFixture : Fixture
|
||||
ScopePtr rootScope{new Scope(builtinTypes->emptyTypePack)};
|
||||
ScopePtr moduleScope{new Scope(rootScope)};
|
||||
|
||||
Subtyping subtyping = mkSubtyping(rootScope);
|
||||
Subtyping subtyping = mkSubtyping();
|
||||
BuiltinTypeFunctions builtinTypeFunctions{};
|
||||
|
||||
Subtyping mkSubtyping(const ScopePtr& scope)
|
||||
Subtyping mkSubtyping()
|
||||
{
|
||||
return Subtyping{builtinTypes, NotNull{&arena}, NotNull{&normalizer}, NotNull{&iceReporter}, NotNull{scope.get()}};
|
||||
return Subtyping{builtinTypes, NotNull{&arena}, NotNull{&normalizer}, NotNull{&iceReporter}};
|
||||
}
|
||||
|
||||
TypePackId pack(std::initializer_list<TypeId> tys)
|
||||
@ -184,7 +184,7 @@ struct SubtypeFixture : Fixture
|
||||
|
||||
SubtypingResult isSubtype(TypeId subTy, TypeId superTy)
|
||||
{
|
||||
return subtyping.isSubtype(subTy, superTy);
|
||||
return subtyping.isSubtype(subTy, superTy, NotNull{rootScope.get()});
|
||||
}
|
||||
|
||||
TypeId helloType = arena.addType(SingletonType{StringSingleton{"hello"}});
|
||||
@ -1210,7 +1210,7 @@ TEST_CASE_FIXTURE(SubtypeFixture, "(...any) -> () <: <T>(T...) -> ()")
|
||||
TypeId anysToNothing = arena.addType(FunctionType{builtinTypes->anyTypePack, builtinTypes->emptyTypePack});
|
||||
TypeId genericTToAnys = arena.addType(FunctionType{genericAs, builtinTypes->emptyTypePack});
|
||||
|
||||
CHECK_MESSAGE(subtyping.isSubtype(anysToNothing, genericTToAnys).isSubtype, "(...any) -> () <: <T>(T...) -> ()");
|
||||
CHECK_MESSAGE(isSubtype(anysToNothing, genericTToAnys).isSubtype, "(...any) -> () <: <T>(T...) -> ()");
|
||||
}
|
||||
|
||||
// See https://github.com/luau-lang/luau/issues/767
|
||||
@ -1220,7 +1220,7 @@ TEST_CASE_FIXTURE(SubtypeFixture, "(...unknown) -> () <: <T>(T...) -> ()")
|
||||
arena.addType(FunctionType{arena.addTypePack(VariadicTypePack{builtinTypes->unknownType}), builtinTypes->emptyTypePack});
|
||||
TypeId genericTToAnys = arena.addType(FunctionType{genericAs, builtinTypes->emptyTypePack});
|
||||
|
||||
CHECK_MESSAGE(subtyping.isSubtype(unknownsToNothing, genericTToAnys).isSubtype, "(...unknown) -> () <: <T>(T...) -> ()");
|
||||
CHECK_MESSAGE(isSubtype(unknownsToNothing, genericTToAnys).isSubtype, "(...unknown) -> () <: <T>(T...) -> ()");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(SubtypeFixture, "bill")
|
||||
@ -1233,8 +1233,8 @@ TEST_CASE_FIXTURE(SubtypeFixture, "bill")
|
||||
{{"a", builtinTypes->stringType}}, TableIndexer{builtinTypes->stringType, builtinTypes->numberType}, TypeLevel{}, nullptr, TableState::Sealed
|
||||
});
|
||||
|
||||
CHECK(subtyping.isSubtype(a, b).isSubtype);
|
||||
CHECK(subtyping.isSubtype(b, a).isSubtype);
|
||||
CHECK(isSubtype(a, b).isSubtype);
|
||||
CHECK(isSubtype(b, a).isSubtype);
|
||||
}
|
||||
|
||||
// TEST_CASE_FIXTURE(SubtypeFixture, "({[string]: number, a: string}) -> () <: ({[string]: number, a: string}) -> ()")
|
||||
@ -1256,7 +1256,7 @@ TEST_CASE_FIXTURE(SubtypeFixture, "fred")
|
||||
TypeId a = makeTheType();
|
||||
TypeId b = makeTheType();
|
||||
|
||||
CHECK_MESSAGE(subtyping.isSubtype(a, b).isSubtype, "({[string]: number, a: string}) -> () <: ({[string]: number, a: string}) -> ()");
|
||||
CHECK_MESSAGE(isSubtype(a, b).isSubtype, "({[string]: number, a: string}) -> () <: ({[string]: number, a: string}) -> ()");
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1273,17 +1273,17 @@ TEST_CASE_FIXTURE(SubtypeFixture, "unknown <: X")
|
||||
|
||||
TypeId genericX = arena.addType(GenericType(childScope.get(), "X"));
|
||||
|
||||
SubtypingResult usingGlobalScope = subtyping.isSubtype(builtinTypes->unknownType, genericX);
|
||||
SubtypingResult usingGlobalScope = isSubtype(builtinTypes->unknownType, genericX);
|
||||
CHECK_MESSAGE(!usingGlobalScope.isSubtype, "Expected " << builtinTypes->unknownType << " </: " << genericX);
|
||||
|
||||
Subtyping childSubtyping{mkSubtyping(childScope)};
|
||||
Subtyping childSubtyping{mkSubtyping()};
|
||||
|
||||
SubtypingResult usingChildScope = childSubtyping.isSubtype(builtinTypes->unknownType, genericX);
|
||||
SubtypingResult usingChildScope = childSubtyping.isSubtype(builtinTypes->unknownType, genericX, NotNull{childScope.get()});
|
||||
CHECK_MESSAGE(usingChildScope.isSubtype, "Expected " << builtinTypes->unknownType << " <: " << genericX);
|
||||
|
||||
Subtyping grandChildSubtyping{mkSubtyping(grandChildScope)};
|
||||
Subtyping grandChildSubtyping{mkSubtyping()};
|
||||
|
||||
SubtypingResult usingGrandChildScope = grandChildSubtyping.isSubtype(builtinTypes->unknownType, genericX);
|
||||
SubtypingResult usingGrandChildScope = grandChildSubtyping.isSubtype(builtinTypes->unknownType, genericX, NotNull{grandChildScope.get()});
|
||||
CHECK_MESSAGE(usingGrandChildScope.isSubtype, "Expected " << builtinTypes->unknownType << " <: " << genericX);
|
||||
}
|
||||
|
||||
|
@ -490,7 +490,6 @@ TEST_CASE_FIXTURE(Fixture, "another_other_higher_order_function")
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -778,11 +777,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "higher_order_function_4")
|
||||
end
|
||||
)");
|
||||
|
||||
// This function currently has a bug in the new solver reporting `{T} | {T}` is not a table.
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
LUAU_REQUIRE_ERRORS(result);
|
||||
else
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
/*
|
||||
* mergesort takes two arguments: an array of some type T and a function that takes two Ts.
|
||||
@ -1678,8 +1673,14 @@ end
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
CHECK_EQ(toString(result.errors[0]), R"(Type function instance add<a, number> depends on generic function parameters but does not appear in the function signature; this construct cannot be type-checked at this time)");
|
||||
CHECK_EQ(toString(result.errors[1]), R"(Type function instance add<a, number> depends on generic function parameters but does not appear in the function signature; this construct cannot be type-checked at this time)");
|
||||
CHECK_EQ(
|
||||
toString(result.errors[0]),
|
||||
R"(Type function instance add<a, number> depends on generic function parameters but does not appear in the function signature; this construct cannot be type-checked at this time)"
|
||||
);
|
||||
CHECK_EQ(
|
||||
toString(result.errors[1]),
|
||||
R"(Type function instance add<a, number> depends on generic function parameters but does not appear in the function signature; this construct cannot be type-checked at this time)"
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1717,7 +1718,8 @@ TEST_CASE_FIXTURE(Fixture, "inferred_higher_order_functions_are_quantified_at_th
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "inferred_higher_order_functions_are_quantified_at_the_right_time3")
|
||||
{
|
||||
// This test regresses in the new solver, but is sort of nonsensical insofar as `foo` is known to be `nil`, so it's "right" to not be able to call it.
|
||||
// This test regresses in the new solver, but is sort of nonsensical insofar as `foo` is known to be `nil`, so it's "right" to not be able to call
|
||||
// it.
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
@ -1749,7 +1751,10 @@ end
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK_EQ(toString(result.errors[0]), R"(Type function instance add<a, number> depends on generic function parameters but does not appear in the function signature; this construct cannot be type-checked at this time)");
|
||||
CHECK_EQ(
|
||||
toString(result.errors[0]),
|
||||
R"(Type function instance add<a, number> depends on generic function parameters but does not appear in the function signature; this construct cannot be type-checked at this time)"
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -2477,6 +2482,9 @@ a = function(a, b) return a + b end
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "simple_unannotated_mutual_recursion")
|
||||
{
|
||||
// CLI-117118 - TypeInferFunctions.simple_unannotated_mutual_recursion relies on unstable assertions to pass.
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
CheckResult result = check(R"(
|
||||
function even(n)
|
||||
if n == 0 then
|
||||
@ -2500,11 +2508,13 @@ end
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(5, result);
|
||||
// CLI-117117 Constraint solving is incomplete inTypeInferFunctions.simple_unannotated_mutual_recursion
|
||||
CHECK(get<ConstraintSolvingIncompleteError>(result.errors[0]));
|
||||
CHECK(
|
||||
toString(result.errors[1]) ==
|
||||
"Type pack '*blocked-tp-1*' could not be converted into 'boolean'; type *blocked-tp-1*.tail() (*blocked-tp-1*) is not a subtype of boolean (boolean)"
|
||||
);
|
||||
// This check is unstable between different machines and different runs of DCR because it depends on string equality between
|
||||
// blocked type numbers, which is not guaranteed.
|
||||
bool r = toString(result.errors[1]) == "Type pack '*blocked-tp-1*' could not be converted into 'boolean'; type *blocked-tp-1*.tail() "
|
||||
"(*blocked-tp-1*) is not a subtype of boolean (boolean)";
|
||||
CHECK(r);
|
||||
CHECK(
|
||||
toString(result.errors[2]) ==
|
||||
"Operator '-' could not be applied to operands of types unknown and number; there is no corresponding overload for __sub"
|
||||
|
@ -142,6 +142,8 @@ TEST_CASE_FIXTURE(Fixture, "properties_can_be_polytypes")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "properties_can_be_instantiated_polytypes")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local t: { m: (number)->number } = { m = function(x:number) return x+1 end }
|
||||
local function id<a>(x:a):a return x end
|
||||
@ -256,8 +258,10 @@ TEST_CASE_FIXTURE(Fixture, "check_mutual_generic_functions_errors")
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "generic_functions_in_types")
|
||||
TEST_CASE_FIXTURE(Fixture, "generic_functions_in_types_old_solver")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type T = { id: <a>(a) -> a }
|
||||
local x: T = { id = function<a>(x:a):a return x end }
|
||||
@ -267,8 +271,23 @@ TEST_CASE_FIXTURE(Fixture, "generic_functions_in_types")
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "generic_functions_in_types_new_solver")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type T = { read id: <a>(a) -> a }
|
||||
local x: T = { id = function<a>(x:a):a return x end }
|
||||
local y: string = x.id("hi")
|
||||
local z: number = x.id(37)
|
||||
)");
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "generic_factories")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type T<a> = { id: (a) -> a }
|
||||
type Factory = { build: <a>() -> T<a> }
|
||||
@ -290,6 +309,8 @@ TEST_CASE_FIXTURE(Fixture, "generic_factories")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "factories_of_generics")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type T = { id: <a>(a) -> a }
|
||||
type Factory = { build: () -> T }
|
||||
@ -445,7 +466,14 @@ TEST_CASE_FIXTURE(Fixture, "dont_leak_generic_types")
|
||||
local b: boolean = f(true)
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERRORS(result);
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERRORS(result);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "dont_leak_inferred_generic_types")
|
||||
@ -461,7 +489,14 @@ TEST_CASE_FIXTURE(Fixture, "dont_leak_inferred_generic_types")
|
||||
local y: number = id(37)
|
||||
end
|
||||
)");
|
||||
LUAU_REQUIRE_ERRORS(result);
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERRORS(result);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "dont_substitute_bound_types")
|
||||
@ -737,17 +772,19 @@ return exports
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "instantiated_function_argument_names")
|
||||
TEST_CASE_FIXTURE(Fixture, "instantiated_function_argument_names_old_solver")
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
local function f<T, U...>(a: T, ...: U...) end
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
f(1, 2, 3)
|
||||
CheckResult result = check(R"(
|
||||
local function f<T, U...>(a: T, ...: U...) end
|
||||
|
||||
f(1, 2, 3)
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
auto ty = findTypeAtPosition(Position(3, 0));
|
||||
auto ty = findTypeAtPosition(Position(3, 8));
|
||||
REQUIRE(ty);
|
||||
ToStringOptions opts;
|
||||
opts.functionTypeArguments = true;
|
||||
@ -756,6 +793,8 @@ f(1, 2, 3)
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "error_detailed_function_mismatch_generic_types")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type C = () -> ()
|
||||
type D = <T>() -> ()
|
||||
@ -771,6 +810,8 @@ local d: D = c
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "error_detailed_function_mismatch_generic_pack")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type C = () -> ()
|
||||
type D = <T...>() -> ()
|
||||
@ -845,6 +886,8 @@ Type 'number' could not be converted into 'string' in an invariant context)";
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "generic_type_pack_unification1")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
type Dispatcher = {
|
||||
@ -863,6 +906,8 @@ local TheDispatcher: Dispatcher = {
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "generic_type_pack_unification2")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
type Dispatcher = {
|
||||
@ -881,6 +926,8 @@ local TheDispatcher: Dispatcher = {
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "generic_type_pack_unification3")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
type Dispatcher = {
|
||||
@ -899,6 +946,8 @@ local TheDispatcher: Dispatcher = {
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "generic_argument_count_too_few")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
function test(a: number)
|
||||
return 1
|
||||
@ -916,6 +965,8 @@ wrapper(test)
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "generic_argument_count_too_many")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
function test2(a: number, b: string)
|
||||
return 1
|
||||
@ -1370,6 +1421,8 @@ TEST_CASE_FIXTURE(Fixture, "apply_type_function_nested_generics3")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "quantify_functions_even_if_they_have_an_explicit_generic")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
function foo<X>(f, x: X)
|
||||
return f(x)
|
||||
@ -1381,6 +1434,8 @@ TEST_CASE_FIXTURE(Fixture, "quantify_functions_even_if_they_have_an_explicit_gen
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "do_not_always_instantiate_generic_intersection_types")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
type Array<T> = { [number]: T }
|
||||
@ -1415,6 +1470,7 @@ end
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "higher_rank_polymorphism_should_not_accept_instantiated_arguments")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, false},
|
||||
{FFlag::LuauInstantiateInSubtyping, true},
|
||||
};
|
||||
|
||||
@ -1481,6 +1537,8 @@ TEST_CASE_FIXTURE(Fixture, "missing_generic_type_parameter")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "generic_type_functions_work_in_subtyping")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
|
||||
|
@ -554,17 +554,17 @@ TEST_CASE_FIXTURE(Fixture, "intersect_saturate_overloaded_functions")
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
const std::string expected1 = R"(Type
|
||||
'(nil) -> nil'
|
||||
'((number?) -> number?) & ((string?) -> string?)'
|
||||
could not be converted into
|
||||
'((number?) -> number?) & ((string?) -> string?)'; type (nil) -> nil.arguments()[0] (nil) is not a supertype of ((number?) -> number?) & ((string?) -> string?)[0].arguments()[0][0] (number)
|
||||
type (nil) -> nil.arguments()[0] (nil) is not a supertype of ((number?) -> number?) & ((string?) -> string?)[1].arguments()[0][0] (string))";
|
||||
'(nil) -> nil'; type ((number?) -> number?) & ((string?) -> string?)[0].returns()[0][0] (number) is not a subtype of (nil) -> nil.returns()[0] (nil)
|
||||
type ((number?) -> number?) & ((string?) -> string?)[1].returns()[0][0] (string) is not a subtype of (nil) -> nil.returns()[0] (nil))";
|
||||
const std::string expected2 = R"(Type
|
||||
'(number) -> number'
|
||||
'((number?) -> number?) & ((string?) -> string?)'
|
||||
could not be converted into
|
||||
'((number?) -> number?) & ((string?) -> string?)'; type (number) -> number.arguments()[0] (number) is not a supertype of ((number?) -> number?) & ((string?) -> string?)[0].arguments()[0][1] (nil)
|
||||
type (number) -> number.arguments()[0] (number) is not a supertype of ((number?) -> number?) & ((string?) -> string?)[1].arguments()[0][0] (string)
|
||||
type (number) -> number.arguments()[0] (number) is not a supertype of ((number?) -> number?) & ((string?) -> string?)[1].arguments()[0][1] (nil)
|
||||
type (number) -> number.returns()[0] (number) is not a subtype of ((number?) -> number?) & ((string?) -> string?)[1].returns()[0] (string?))";
|
||||
'(number) -> number'; type ((number?) -> number?) & ((string?) -> string?)[0].returns()[0][1] (nil) is not a subtype of (number) -> number.returns()[0] (number)
|
||||
type ((number?) -> number?) & ((string?) -> string?)[1].arguments()[0] (string?) is not a supertype of (number) -> number.arguments()[0] (number)
|
||||
type ((number?) -> number?) & ((string?) -> string?)[1].returns()[0][0] (string) is not a subtype of (number) -> number.returns()[0] (number)
|
||||
type ((number?) -> number?) & ((string?) -> string?)[1].returns()[0][1] (nil) is not a subtype of (number) -> number.returns()[0] (number))";
|
||||
CHECK_EQ(expected1, toString(result.errors[0]));
|
||||
CHECK_EQ(expected2, toString(result.errors[1]));
|
||||
}
|
||||
@ -609,13 +609,12 @@ TEST_CASE_FIXTURE(Fixture, "intersection_of_tables")
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
? "Type "
|
||||
"'{ p: number?, q: number?, r: number? } & { p: number?, q: string? }'"
|
||||
" could not be converted into "
|
||||
"'{ p: nil }'; none of the intersection parts are compatible"
|
||||
:
|
||||
R"(Type
|
||||
const std::string expected =
|
||||
(FFlag::DebugLuauDeferredConstraintResolution)
|
||||
? R"(Type '{ p: number?, q: number?, r: number? } & { p: number?, q: string? }' could not be converted into '{ p: nil }'; type { p: number?, q: number?, r: number? } & { p: number?, q: string? }[0][read "p"][0] (number) is not exactly { p: nil }[read "p"] (nil)
|
||||
type { p: number?, q: number?, r: number? } & { p: number?, q: string? }[1][read "p"][0] (number) is not exactly { p: nil }[read "p"] (nil))"
|
||||
:
|
||||
R"(Type
|
||||
'{| p: number?, q: number?, r: number? |} & {| p: number?, q: string? |}'
|
||||
could not be converted into
|
||||
'{| p: nil |}'; none of the intersection parts are compatible)";
|
||||
@ -633,28 +632,18 @@ TEST_CASE_FIXTURE(Fixture, "intersection_of_tables_with_top_properties")
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK_EQ(
|
||||
toString(result.errors[0]),
|
||||
"Type '{| p: number?, q: string? |}' could not be converted into '{| p: string?, q: number? |}'\n"
|
||||
"caused by:\n"
|
||||
" Property 'p' is not compatible. Type 'number?' could not be converted into 'string?'\n"
|
||||
"caused by:\n"
|
||||
" Not all union options are compatible. Type 'number' could not be converted into 'string?'\n"
|
||||
"caused by:\n"
|
||||
" None of the union options are compatible. For example: Type 'number' could not be converted into 'string' in an invariant context"
|
||||
);
|
||||
|
||||
CHECK_EQ(
|
||||
toString(result.errors[1]),
|
||||
"Type '{| p: number?, q: string? |}' could not be converted into '{| p: string?, q: number? |}'\n"
|
||||
"caused by:\n"
|
||||
" Property 'q' is not compatible. Type 'string?' could not be converted into 'number?'\n"
|
||||
"caused by:\n"
|
||||
" Not all union options are compatible. Type 'string' could not be converted into 'number?'\n"
|
||||
"caused by:\n"
|
||||
" None of the union options are compatible. For example: Type 'string' could not be converted into 'number' in an invariant context"
|
||||
R"(Type
|
||||
'{ p: number?, q: any } & { p: unknown, q: string? }'
|
||||
could not be converted into
|
||||
'{ p: string?, q: number? }'; type { p: number?, q: any } & { p: unknown, q: string? }[0][read "p"] (number?) is not exactly { p: string?, q: number? }[read "p"][0] (string)
|
||||
type { p: number?, q: any } & { p: unknown, q: string? }[0][read "p"][0] (number) is not exactly { p: string?, q: number? }[read "p"] (string?)
|
||||
type { p: number?, q: any } & { p: unknown, q: string? }[0][read "q"] (any) is not exactly { p: string?, q: number? }[read "q"] (number?)
|
||||
type { p: number?, q: any } & { p: unknown, q: string? }[1][read "p"] (unknown) is not exactly { p: string?, q: number? }[read "p"] (string?)
|
||||
type { p: number?, q: any } & { p: unknown, q: string? }[1][read "q"] (string?) is not exactly { p: string?, q: number? }[read "q"][0] (number)
|
||||
type { p: number?, q: any } & { p: unknown, q: string? }[1][read "q"][0] (string) is not exactly { p: string?, q: number? }[read "q"] (number?))",
|
||||
toString(result.errors[0])
|
||||
);
|
||||
}
|
||||
else
|
||||
@ -689,18 +678,42 @@ TEST_CASE_FIXTURE(Fixture, "overloaded_functions_returning_intersections")
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = (FFlag::DebugLuauDeferredConstraintResolution) ?
|
||||
R"(Type
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
CHECK_EQ(
|
||||
R"(Type
|
||||
'((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })'
|
||||
could not be converted into
|
||||
'(number?) -> { p: number, q: number, r: number }'; none of the intersection parts are compatible)"
|
||||
:
|
||||
R"(Type
|
||||
'(nil) -> { p: number, q: number, r: number }'; type ((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })[0].returns()[0][0] ({ p: number }) is not a subtype of (nil) -> { p: number, q: number, r: number }.returns()[0] ({ p: number, q: number, r: number })
|
||||
type ((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })[0].returns()[0][1] ({ q: number }) is not a subtype of (nil) -> { p: number, q: number, r: number }.returns()[0] ({ p: number, q: number, r: number })
|
||||
type ((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })[1].returns()[0][0] ({ p: number }) is not a subtype of (nil) -> { p: number, q: number, r: number }.returns()[0] ({ p: number, q: number, r: number })
|
||||
type ((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })[1].returns()[0][1] ({ r: number }) is not a subtype of (nil) -> { p: number, q: number, r: number }.returns()[0] ({ p: number, q: number, r: number }))",
|
||||
toString(result.errors[0])
|
||||
);
|
||||
CHECK_EQ(
|
||||
R"(Type
|
||||
'((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })'
|
||||
could not be converted into
|
||||
'(number?) -> { p: number, q: number, r: number }'; type ((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })[0].returns()[0][0] ({ p: number }) is not a subtype of (number?) -> { p: number, q: number, r: number }.returns()[0] ({ p: number, q: number, r: number })
|
||||
type ((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })[0].returns()[0][1] ({ q: number }) is not a subtype of (number?) -> { p: number, q: number, r: number }.returns()[0] ({ p: number, q: number, r: number })
|
||||
type ((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })[1].arguments()[0] (string?) is not a supertype of (number?) -> { p: number, q: number, r: number }.arguments()[0][0] (number)
|
||||
type ((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })[1].returns()[0][0] ({ p: number }) is not a subtype of (number?) -> { p: number, q: number, r: number }.returns()[0] ({ p: number, q: number, r: number })
|
||||
type ((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })[1].returns()[0][1] ({ r: number }) is not a subtype of (number?) -> { p: number, q: number, r: number }.returns()[0] ({ p: number, q: number, r: number }))",
|
||||
toString(result.errors[1])
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK_EQ(
|
||||
R"(Type
|
||||
'((number?) -> {| p: number |} & {| q: number |}) & ((string?) -> {| p: number |} & {| r: number |})'
|
||||
could not be converted into
|
||||
'(number?) -> {| p: number, q: number, r: number |}'; none of the intersection parts are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
'(number?) -> {| p: number, q: number, r: number |}'; none of the intersection parts are compatible)",
|
||||
toString(result.errors[0])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "overloaded_functions_mentioning_generic")
|
||||
@ -713,13 +726,19 @@ TEST_CASE_FIXTURE(Fixture, "overloaded_functions_mentioning_generic")
|
||||
end
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = R"(Type
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(0, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = R"(Type
|
||||
'((number?) -> a | number) & ((string?) -> a | string)'
|
||||
could not be converted into
|
||||
'(number?) -> a'; none of the intersection parts are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "overloaded_functions_mentioning_generics")
|
||||
@ -733,12 +752,20 @@ TEST_CASE_FIXTURE(Fixture, "overloaded_functions_mentioning_generics")
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = R"(Type
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = R"(Type
|
||||
'((a?) -> a | b) & ((c?) -> b | c)'
|
||||
could not be converted into
|
||||
'(a?) -> (a & c) | b'; none of the intersection parts are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "overloaded_functions_mentioning_generic_packs")
|
||||
@ -751,13 +778,35 @@ TEST_CASE_FIXTURE(Fixture, "overloaded_functions_mentioning_generic_packs")
|
||||
end
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = R"(Type
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
CHECK_EQ(
|
||||
R"(Type
|
||||
'((number?, a...) -> (number?, b...)) & ((string?, a...) -> (string?, b...))'
|
||||
could not be converted into
|
||||
'(nil, a...) -> (nil, b...)'; type ((number?, a...) -> (number?, b...)) & ((string?, a...) -> (string?, b...))[0].returns()[0][0] (number) is not a subtype of (nil, a...) -> (nil, b...).returns()[0] (nil)
|
||||
type ((number?, a...) -> (number?, b...)) & ((string?, a...) -> (string?, b...))[1].returns()[0][0] (string) is not a subtype of (nil, a...) -> (nil, b...).returns()[0] (nil))",
|
||||
toString(result.errors[0])
|
||||
);
|
||||
CHECK_EQ(
|
||||
R"(Type
|
||||
'((number?, a...) -> (number?, b...)) & ((string?, a...) -> (string?, b...))'
|
||||
could not be converted into
|
||||
'(nil, b...) -> (nil, a...)'; type ((number?, a...) -> (number?, b...)) & ((string?, a...) -> (string?, b...))[0].returns()[0][0] (number) is not a subtype of (nil, b...) -> (nil, a...).returns()[0] (nil)
|
||||
type ((number?, a...) -> (number?, b...)) & ((string?, a...) -> (string?, b...))[1].returns()[0][0] (string) is not a subtype of (nil, b...) -> (nil, a...).returns()[0] (nil))",
|
||||
toString(result.errors[1])
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = R"(Type
|
||||
'((number?, a...) -> (number?, b...)) & ((string?, a...) -> (string?, b...))'
|
||||
could not be converted into
|
||||
'(nil, b...) -> (nil, a...)'; none of the intersection parts are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_unknown_result")
|
||||
@ -858,12 +907,34 @@ TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_never_arguments")
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = R"(Type
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
const std::string expected1 = R"(Type
|
||||
'((never) -> string?) & ((number) -> number?)'
|
||||
could not be converted into
|
||||
'(never) -> nil'; type ((never) -> string?) & ((number) -> number?)[0].returns()[0][0] (number) is not a subtype of (never) -> nil.returns()[0] (nil)
|
||||
type ((never) -> string?) & ((number) -> number?)[1].returns()[0][0] (string) is not a subtype of (never) -> nil.returns()[0] (nil))";
|
||||
const std::string expected2 = R"(Type
|
||||
'((never) -> string?) & ((number) -> number?)'
|
||||
could not be converted into
|
||||
'(number?) -> nil'; type ((never) -> string?) & ((number) -> number?)[0].arguments()[0] (number) is not a supertype of (number?) -> nil.arguments()[0][1] (nil)
|
||||
type ((never) -> string?) & ((number) -> number?)[0].returns()[0][0] (number) is not a subtype of (number?) -> nil.returns()[0] (nil)
|
||||
type ((never) -> string?) & ((number) -> number?)[1].arguments()[0] (never) is not a supertype of (number?) -> nil.arguments()[0][0] (number)
|
||||
type ((never) -> string?) & ((number) -> number?)[1].arguments()[0] (never) is not a supertype of (number?) -> nil.arguments()[0][1] (nil)
|
||||
type ((never) -> string?) & ((number) -> number?)[1].returns()[0][0] (string) is not a subtype of (number?) -> nil.returns()[0] (nil))";
|
||||
CHECK_EQ(expected1, toString(result.errors[0]));
|
||||
CHECK_EQ(expected2, toString(result.errors[1]));
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = R"(Type
|
||||
'((never) -> string?) & ((number) -> number?)'
|
||||
could not be converted into
|
||||
'(number?) -> nil'; none of the intersection parts are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_overlapping_results_and_variadics")
|
||||
@ -999,6 +1070,10 @@ could not be converted into
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "intersect_metatables")
|
||||
{
|
||||
// CLI-117121 - Intersection of types are not compatible with the equivalent alias
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
@ -1165,10 +1240,9 @@ TEST_CASE_FIXTURE(Fixture, "less_greedy_unification_with_intersection_types")
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
LUAU_REQUIRE_ERROR_COUNT(3, result);
|
||||
|
||||
// TODO? We do not simplify types from explicit annotations.
|
||||
CHECK_EQ("({| x: number |} & {| x: string |}) -> {| x: number |} & {| x: string |}", toString(requireType("f")));
|
||||
CHECK_EQ("(never) -> { x: number } & { x: string }", toString(requireType("f")));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "less_greedy_unification_with_intersection_types_2")
|
||||
|
@ -50,6 +50,9 @@ TEST_CASE_FIXTURE(NegationFixture, "string_is_not_a_subtype_of_negated_string")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "cofinite_strings_can_be_compared_for_equality")
|
||||
{
|
||||
// CLI-117082 Cofinite strings cannot be compared for equality because normalization produces a large type with cycles
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
CheckResult result = check(R"(
|
||||
function f(e)
|
||||
if e == 'strictEqual' then
|
||||
|
@ -1500,6 +1500,9 @@ TEST_CASE_FIXTURE(RefinementClassFixture, "narrow_from_subclasses_of_instance_or
|
||||
|
||||
TEST_CASE_FIXTURE(RefinementClassFixture, "x_as_any_if_x_is_instance_elseif_x_is_table")
|
||||
{
|
||||
// CLI-117136 - this code doesn't finish constraint solving and has blocked types in the output
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
CheckResult result = check(R"(
|
||||
--!nonstrict
|
||||
|
||||
@ -1588,6 +1591,9 @@ TEST_CASE_FIXTURE(RefinementClassFixture, "isa_type_refinement_must_be_known_ahe
|
||||
|
||||
TEST_CASE_FIXTURE(RefinementClassFixture, "x_is_not_instance_or_else_not_part")
|
||||
{
|
||||
// CLI-117135 - RefinementTests.x_is_not_instance_or_else_not_part not correctly applying refinements to a function parameter
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
CheckResult result = check(R"(
|
||||
local function f(x: Part | Folder | string)
|
||||
if typeof(x) ~= "Instance" or not x:IsA("Part") then
|
||||
@ -1807,6 +1813,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "refine_unknown_to_table_then_clone_it")
|
||||
|
||||
TEST_CASE_FIXTURE(RefinementClassFixture, "refine_a_param_that_got_resolved_during_constraint_solving_stage")
|
||||
{
|
||||
// CLI-117134 - Applying a refinement causes an optional value access error.
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
CheckResult result = check(R"(
|
||||
type Id<T> = T
|
||||
|
||||
|
@ -15,9 +15,10 @@
|
||||
|
||||
using namespace Luau;
|
||||
|
||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
|
||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping);
|
||||
LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering);
|
||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
|
||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||
LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering)
|
||||
LUAU_FASTFLAG(LuauAcceptIndexingTableUnionsIntersections)
|
||||
|
||||
LUAU_DYNAMIC_FASTFLAG(LuauImproveNonFunctionCallError)
|
||||
|
||||
@ -4794,4 +4795,41 @@ end
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "indexing_branching_table")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::LuauAcceptIndexingTableUnionsIntersections, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local test = if true then { "meow", "woof" } else { 4, 81 }
|
||||
local test2 = test[1]
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
// unfortunate type duplication in the union
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK("number | string | string" == toString(requireType("test2")));
|
||||
else
|
||||
CHECK("number | string" == toString(requireType("test2")));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "indexing_branching_table2")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::LuauAcceptIndexingTableUnionsIntersections, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local test = if true then {} else {}
|
||||
local test2 = test[1]
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
// unfortunate type duplication in the union
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK("unknown | unknown" == toString(requireType("test2")));
|
||||
else
|
||||
CHECK("any" == toString(requireType("test2")));
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
@ -459,7 +459,9 @@ TEST_CASE_FIXTURE(TypeStateFixture, "typestates_preserve_error_suppression")
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "typestates_preserve_error_suppression_properties")
|
||||
{
|
||||
// early return if the flag isn't set since this is blocking gated commits
|
||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||
// unconditional return
|
||||
// CLI-117098 Type states with error suppressing properties doesn't infer the correct type for properties.
|
||||
if (!FFlag::DebugLuauDeferredConstraintResolution || FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
|
||||
CheckResult result = check(R"(
|
||||
|
@ -8,7 +8,8 @@
|
||||
|
||||
using namespace Luau;
|
||||
|
||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
|
||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
|
||||
LUAU_FASTFLAG(LuauAcceptIndexingTableUnionsIntersections)
|
||||
|
||||
TEST_SUITE_BEGIN("UnionTypes");
|
||||
|
||||
@ -636,7 +637,12 @@ TEST_CASE_FIXTURE(Fixture, "indexing_into_a_cyclic_union_doesnt_crash")
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
// this is a cyclic union of number arrays, so it _is_ a table, even if it's a nonsense type.
|
||||
// no need to generate a NotATable error here.
|
||||
if (FFlag::LuauAcceptIndexingTableUnionsIntersections)
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
else
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "table_union_write_indirect")
|
||||
|
@ -193,12 +193,20 @@ TEST_CASE_FIXTURE(Fixture, "call_never")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "assign_to_local_which_is_never")
|
||||
{
|
||||
// CLI-117119 - What do we do about assigning to never?
|
||||
CheckResult result = check(R"(
|
||||
local t: never
|
||||
t = 3
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "assign_to_global_which_is_never")
|
||||
@ -257,6 +265,9 @@ TEST_CASE_FIXTURE(Fixture, "pick_never_from_variadic_type_pack")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "index_on_union_of_tables_for_properties_that_is_never")
|
||||
{
|
||||
// CLI-117116 - We are erroneously warning when passing a valid table literal where we expect a union of tables.
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
CheckResult result = check(R"(
|
||||
type Disjoint = {foo: never, bar: unknown, tag: "ok"} | {foo: never, baz: unknown, tag: "err"}
|
||||
|
||||
@ -274,6 +285,9 @@ TEST_CASE_FIXTURE(Fixture, "index_on_union_of_tables_for_properties_that_is_neve
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "index_on_union_of_tables_for_properties_that_is_sorta_never")
|
||||
{
|
||||
// CLI-117116 - We are erroneously warning when passing a valid table literal where we expect a union of tables.
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
CheckResult result = check(R"(
|
||||
type Disjoint = {foo: string, bar: unknown, tag: "ok"} | {foo: never, baz: unknown, tag: "err"}
|
||||
|
||||
|
@ -15,15 +15,28 @@ TEST_SUITE_BEGIN("VisitType");
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "throw_when_limit_is_exceeded")
|
||||
{
|
||||
ScopedFastInt sfi{FInt::LuauVisitRecursionLimit, 3};
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
local t : {a: {b: {c: {d: {e: boolean}}}}}
|
||||
)");
|
||||
ScopedFastInt sfi{FInt::LuauVisitRecursionLimit, 3};
|
||||
TypeId tType = requireType("t");
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local t : {a: {b: {c: {d: {e: boolean}}}}}
|
||||
)");
|
||||
CHECK_THROWS_AS(toString(tType), RecursionLimitException);
|
||||
}
|
||||
else
|
||||
{
|
||||
ScopedFastInt sfi{FInt::LuauVisitRecursionLimit, 3};
|
||||
|
||||
TypeId tType = requireType("t");
|
||||
CheckResult result = check(R"(
|
||||
local t : {a: {b: {c: {d: {e: boolean}}}}}
|
||||
)");
|
||||
|
||||
CHECK_THROWS_AS(toString(tType), RecursionLimitException);
|
||||
TypeId tType = requireType("t");
|
||||
|
||||
CHECK_THROWS_AS(toString(tType), RecursionLimitException);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "dont_throw_when_limit_is_high_enough")
|
||||
|
@ -164,4 +164,23 @@ do
|
||||
assert(larget[vector(-0, 0, 0)] == 42)
|
||||
end
|
||||
|
||||
local function numvectemporary()
|
||||
local proptab = {}
|
||||
|
||||
proptab.vec3compsum = function(vec: vector)
|
||||
local num = vec.X + vec.Y
|
||||
local tmp = vec / num
|
||||
local num2 = num * 2
|
||||
return tmp, num2
|
||||
end
|
||||
|
||||
local a, b = proptab.vec3compsum(vector(2, 6, 0))
|
||||
|
||||
assert(a.X == 0.25)
|
||||
assert(a.Y == 0.75)
|
||||
assert(b == 16)
|
||||
end
|
||||
|
||||
numvectemporary()
|
||||
|
||||
return 'OK'
|
||||
|
@ -51,7 +51,7 @@ static bool skipFastFlag(const char* flagName)
|
||||
if (strncmp(flagName, "Debug", 5) == 0)
|
||||
return true;
|
||||
|
||||
if (strcmp(flagName, "StudioReportLuauAny") == 0)
|
||||
if (strcmp(flagName, "StudioReportLuauAny2") == 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
@ -1,45 +0,0 @@
|
||||
AutocompleteTest.anonymous_autofilled_generic_on_argument_type_pack_vararg
|
||||
AutocompleteTest.anonymous_autofilled_generic_type_pack_vararg
|
||||
AutocompleteTest.autocomplete_string_singletons
|
||||
AutocompleteTest.suggest_table_keys
|
||||
AutocompleteTest.type_correct_suggestion_for_overloads
|
||||
AutocompleteTest.type_correct_suggestion_in_table
|
||||
GenericsTests.do_not_always_instantiate_generic_intersection_types
|
||||
GenericsTests.error_detailed_function_mismatch_generic_pack
|
||||
GenericsTests.error_detailed_function_mismatch_generic_types
|
||||
GenericsTests.factories_of_generics
|
||||
GenericsTests.generic_argument_count_too_few
|
||||
GenericsTests.generic_argument_count_too_many
|
||||
GenericsTests.generic_factories
|
||||
GenericsTests.generic_functions_in_types
|
||||
GenericsTests.generic_type_functions_work_in_subtyping
|
||||
GenericsTests.generic_type_pack_unification1
|
||||
GenericsTests.generic_type_pack_unification2
|
||||
GenericsTests.generic_type_pack_unification3
|
||||
GenericsTests.higher_rank_polymorphism_should_not_accept_instantiated_arguments
|
||||
GenericsTests.instantiated_function_argument_names
|
||||
GenericsTests.properties_can_be_instantiated_polytypes
|
||||
GenericsTests.quantify_functions_even_if_they_have_an_explicit_generic
|
||||
IntersectionTypes.intersect_metatables
|
||||
IntersectionTypes.intersect_saturate_overloaded_functions
|
||||
IntersectionTypes.intersection_of_tables
|
||||
IntersectionTypes.intersection_of_tables_with_top_properties
|
||||
IntersectionTypes.less_greedy_unification_with_intersection_types
|
||||
IntersectionTypes.overloaded_functions_mentioning_generic
|
||||
IntersectionTypes.overloaded_functions_mentioning_generic_packs
|
||||
IntersectionTypes.overloaded_functions_mentioning_generics
|
||||
IntersectionTypes.overloaded_functions_returning_intersections
|
||||
IntersectionTypes.overloadeded_functions_with_never_arguments
|
||||
ModuleTests.clone_self_property
|
||||
Negations.cofinite_strings_can_be_compared_for_equality
|
||||
Normalize.higher_order_function_with_annotation
|
||||
RefinementTest.refine_a_param_that_got_resolved_during_constraint_solving_stage
|
||||
RefinementTest.x_as_any_if_x_is_instance_elseif_x_is_table
|
||||
RefinementTest.x_is_not_instance_or_else_not_part
|
||||
TypeInferFunctions.simple_unannotated_mutual_recursion
|
||||
TypeInferUnknownNever.assign_to_local_which_is_never
|
||||
TypeInferUnknownNever.index_on_union_of_tables_for_properties_that_is_never
|
||||
TypeInferUnknownNever.index_on_union_of_tables_for_properties_that_is_sorta_never
|
||||
TypePackTests.fuzz_typepack_iter_follow_2
|
||||
TypeStatesTest.typestates_preserve_error_suppression_properties
|
||||
VisitType.throw_when_limit_is_exceeded
|
Loading…
Reference in New Issue
Block a user