mirror of
https://github.com/luau-lang/luau.git
synced 2025-08-26 11:27:08 +01:00
Sync to upstream/release/676 (#1856)
We're back on track after the long weekend! ## General - `clang-format`ed new code. Keep your code tidy! - Disable some Luau tests that are broken currently. - Enable fragment autocomplete to do tagged union completion for modules typechecked in the old solver. ## New Type Solver - Fix false positives on generic type packs in non-strict mode. - Update type signature of `setmetatable` to be `<T, MT>(T, MT) -> setmetatable<T, MT>`. - Make local type aliases available in type functions. For example: ``` type Foo = number type Array<T> = {T} type function Bar(t) return types.unionof(Foo, Array(t)) end ``` ## VM/Runtime - Make sure `lua_unref` doesn't accept refs which did not exist in the table. --- Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Ariel Weiss <aaronweiss@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Sora Kanosue <skanosue@roblox.com> Co-authored-by: Talha Pathan <tpathan@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Varun Saini <61795485+vrn-sn@users.noreply.github.com> Co-authored-by: Menarul Alam <malam@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Ariel Weiss <aaronweiss@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com>
This commit is contained in:
parent
5965818283
commit
92b0338400
82 changed files with 1979 additions and 881 deletions
|
@ -10,12 +10,12 @@
|
|||
#include "Luau/InsertionOrderedMap.h"
|
||||
#include "Luau/Module.h"
|
||||
#include "Luau/ModuleResolver.h"
|
||||
#include "Luau/Normalize.h"
|
||||
#include "Luau/NotNull.h"
|
||||
#include "Luau/Polarity.h"
|
||||
#include "Luau/Refinement.h"
|
||||
#include "Luau/Symbol.h"
|
||||
#include "Luau/TypeFwd.h"
|
||||
#include "Luau/TypeIds.h"
|
||||
#include "Luau/TypeUtils.h"
|
||||
|
||||
#include <memory>
|
||||
|
@ -93,7 +93,7 @@ struct ConstraintGenerator
|
|||
std::vector<ConstraintPtr> constraints;
|
||||
|
||||
// The set of all free types introduced during constraint generation.
|
||||
DenseHashSet<TypeId> freeTypes{nullptr};
|
||||
TypeIds freeTypes;
|
||||
|
||||
// Map a function's signature scope back to its signature type.
|
||||
DenseHashMap<Scope*, TypeId> scopeToFunction{nullptr};
|
||||
|
@ -176,7 +176,7 @@ private:
|
|||
std::vector<TypeId> unionsToSimplify;
|
||||
|
||||
// Used to keep track of when we are inside a large table and should
|
||||
// opt *not* to do type inference for singletons.
|
||||
// opt *not* to do type inference for singletons.
|
||||
size_t largeTableDepth = 0;
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "Luau/Constraint.h"
|
||||
#include "Luau/DenseHash.h"
|
||||
#include "Luau/TypeIds.h"
|
||||
#include "Luau/Error.h"
|
||||
|
||||
#include <vector>
|
||||
|
@ -18,7 +18,7 @@ struct ConstraintSet
|
|||
std::vector<ConstraintPtr> constraints;
|
||||
|
||||
// The set of all free types created during constraint generation
|
||||
DenseHashSet<TypeId> freeTypes{nullptr};
|
||||
TypeIds freeTypes;
|
||||
|
||||
// Map a function's signature scope back to its signature type. Once we've
|
||||
// dispatched all of the constraints pertaining to a particular free type,
|
||||
|
@ -29,4 +29,4 @@ struct ConstraintSet
|
|||
std::vector<TypeError> errors;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace Luau
|
||||
|
|
|
@ -456,6 +456,14 @@ public:
|
|||
*/
|
||||
std::vector<NotNull<Constraint>> borrowConstraints(const std::vector<ConstraintPtr>& constraints);
|
||||
|
||||
std::pair<std::vector<TypeId>, std::vector<TypePackId>> saturateArguments(
|
||||
TypeArena* arena,
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
const TypeFun& fn,
|
||||
const std::vector<TypeId>& rawTypeArguments,
|
||||
const std::vector<TypePackId>& rawPackArguments
|
||||
);
|
||||
|
||||
void dump(NotNull<Scope> rootScope, struct ToStringOptions& opts);
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -464,7 +464,10 @@ struct ReservedIdentifier
|
|||
|
||||
struct UnexpectedArrayLikeTableItem
|
||||
{
|
||||
bool operator==(const UnexpectedArrayLikeTableItem&) const { return true; }
|
||||
bool operator==(const UnexpectedArrayLikeTableItem&) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
using TypeErrorData = Variant<
|
||||
|
|
74
Analysis/include/Luau/ExpectedTypeVisitor.h
Normal file
74
Analysis/include/Luau/ExpectedTypeVisitor.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#pragma once
|
||||
|
||||
#include "Luau/Ast.h"
|
||||
#include "Luau/DenseHash.h"
|
||||
#include "Luau/NotNull.h"
|
||||
#include "Luau/Type.h"
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
struct ExpectedTypeVisitor : public AstVisitor
|
||||
{
|
||||
|
||||
explicit ExpectedTypeVisitor(
|
||||
NotNull<DenseHashMap<const AstExpr*, TypeId>> astTypes,
|
||||
NotNull<DenseHashMap<const AstExpr*, TypeId>> astExpectedTypes,
|
||||
NotNull<DenseHashMap<const AstType*, TypeId>> astResolvedTypes,
|
||||
NotNull<TypeArena> arena,
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<Scope> rootScope
|
||||
);
|
||||
|
||||
// When we have an assignment, we grab the type of the left-hand-side
|
||||
// and we use it to inform what the type of the right-hand-side ought
|
||||
// to be. This is important for something like:
|
||||
//
|
||||
//
|
||||
// local function foobar(tbl: { prop: boolean })
|
||||
// tbl.prop = [autocomplete here]
|
||||
// end
|
||||
//
|
||||
// ... where the right hand side _must_ be a subtype of `boolean`
|
||||
bool visit(AstStatAssign* stat) override;
|
||||
|
||||
// Similar to an assignment, we can apply expected types to the
|
||||
// right-hand-side of a local based on the annotated type of the
|
||||
// left-hand-side.
|
||||
bool visit(AstStatLocal* stat) override;
|
||||
|
||||
// Compound assignments have the curious property that they do not change
|
||||
// type state, so we can use the left-hand-side to inform the
|
||||
// right-hand-side.
|
||||
bool visit(AstStatCompoundAssign* stat) override;
|
||||
|
||||
// When we are returning something, and we've inferred a return type (or have
|
||||
// a written return type), then we need to apply the expected types to the
|
||||
// return type expression.
|
||||
bool visit(AstStatReturn* stat) override;
|
||||
|
||||
// When we have a function call, we can apply expected types to all the
|
||||
// parameters.
|
||||
bool visit(AstExprCall* expr) override;
|
||||
|
||||
// If we have an expression of type:
|
||||
//
|
||||
// return X :: Y
|
||||
//
|
||||
// Then surely the expected type of `X` is `Y`
|
||||
bool visit(AstExprTypeAssertion* expr) override;
|
||||
|
||||
private:
|
||||
NotNull<DenseHashMap<const AstExpr*, TypeId>> astTypes;
|
||||
NotNull<DenseHashMap<const AstExpr*, TypeId>> astExpectedTypes;
|
||||
NotNull<DenseHashMap<const AstType*, TypeId>> astResolvedTypes;
|
||||
NotNull<TypeArena> arena;
|
||||
NotNull<BuiltinTypes> builtinTypes;
|
||||
NotNull<Scope> rootScope;
|
||||
|
||||
void applyExpectedType(const TypeId expectedType, const AstExpr* expr);
|
||||
};
|
||||
|
||||
|
||||
} // namespace Luau
|
|
@ -126,6 +126,9 @@ struct Module
|
|||
// we need a sentinel value for the map.
|
||||
DenseHashMap<const AstNode*, Scope*> astScopes{nullptr};
|
||||
|
||||
// Stable references for type aliases registered in the environment
|
||||
std::vector<std::unique_ptr<TypeFun>> typeFunctionAliases;
|
||||
|
||||
std::unordered_map<Name, TypeId> declaredGlobals;
|
||||
ErrorVec errors;
|
||||
LintResult lintResult;
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
#include "Luau/NotNull.h"
|
||||
#include "Luau/Set.h"
|
||||
#include "Luau/TypeFwd.h"
|
||||
#include "Luau/TypeIds.h"
|
||||
#include "Luau/UnifierSharedState.h"
|
||||
|
||||
#include <initializer_list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
@ -39,57 +39,6 @@ bool isSubtype(
|
|||
InternalErrorReporter& ice
|
||||
);
|
||||
|
||||
class TypeIds
|
||||
{
|
||||
private:
|
||||
DenseHashMap<TypeId, bool> types{nullptr};
|
||||
std::vector<TypeId> order;
|
||||
std::size_t hash = 0;
|
||||
|
||||
public:
|
||||
using iterator = std::vector<TypeId>::iterator;
|
||||
using const_iterator = std::vector<TypeId>::const_iterator;
|
||||
|
||||
TypeIds() = default;
|
||||
~TypeIds() = default;
|
||||
|
||||
TypeIds(std::initializer_list<TypeId> tys);
|
||||
|
||||
TypeIds(const TypeIds&) = default;
|
||||
TypeIds& operator=(const TypeIds&) = default;
|
||||
|
||||
TypeIds(TypeIds&&) = default;
|
||||
TypeIds& operator=(TypeIds&&) = default;
|
||||
|
||||
void insert(TypeId ty);
|
||||
/// Erase every element that does not also occur in tys
|
||||
void retain(const TypeIds& tys);
|
||||
void clear();
|
||||
|
||||
TypeId front() const;
|
||||
iterator begin();
|
||||
iterator end();
|
||||
const_iterator begin() const;
|
||||
const_iterator end() const;
|
||||
iterator erase(const_iterator it);
|
||||
void erase(TypeId ty);
|
||||
|
||||
size_t size() const;
|
||||
bool empty() const;
|
||||
size_t count(TypeId ty) const;
|
||||
|
||||
template<class Iterator>
|
||||
void insert(Iterator begin, Iterator end)
|
||||
{
|
||||
for (Iterator it = begin; it != end; ++it)
|
||||
insert(*it);
|
||||
}
|
||||
|
||||
bool operator==(const TypeIds& there) const;
|
||||
size_t getHash() const;
|
||||
bool isNever() const;
|
||||
};
|
||||
|
||||
} // namespace Luau
|
||||
|
||||
template<>
|
||||
|
@ -302,7 +251,7 @@ struct NormalizedType
|
|||
// we'd be reusing bad, stale data.
|
||||
bool isCacheable = true;
|
||||
|
||||
NormalizedType(NotNull<BuiltinTypes> builtinTypes);
|
||||
explicit NormalizedType(NotNull<BuiltinTypes> builtinTypes);
|
||||
|
||||
NormalizedType() = delete;
|
||||
~NormalizedType() = default;
|
||||
|
|
|
@ -101,6 +101,8 @@ struct Scope
|
|||
|
||||
std::optional<std::vector<TypeId>> interiorFreeTypes;
|
||||
std::optional<std::vector<TypePackId>> interiorFreeTypePacks;
|
||||
|
||||
NotNull<Scope> findNarrowestScopeContaining(Location);
|
||||
};
|
||||
|
||||
// Returns true iff the left scope encloses the right scope. A Scope* equal to
|
||||
|
|
|
@ -234,8 +234,14 @@ private:
|
|||
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 ExternType* subExternType, const ExternType* superExternType, NotNull<Scope> scope);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const ExternType* subExternType, TypeId superTy, const TableType* superTable, NotNull<Scope>);
|
||||
SubtypingResult isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const ExternType* subExternType,
|
||||
const ExternType* superExternType,
|
||||
NotNull<Scope> scope
|
||||
);
|
||||
SubtypingResult
|
||||
isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const ExternType* subExternType, TypeId superTy, const TableType* superTable, NotNull<Scope>);
|
||||
SubtypingResult isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const FunctionType* subFunction,
|
||||
|
@ -267,7 +273,12 @@ private:
|
|||
const NormalizedExternType& superExternType,
|
||||
NotNull<Scope> scope
|
||||
);
|
||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const NormalizedExternType& subExternType, const TypeIds& superTables, NotNull<Scope> scope);
|
||||
SubtypingResult isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const NormalizedExternType& subExternType,
|
||||
const TypeIds& superTables,
|
||||
NotNull<Scope> scope
|
||||
);
|
||||
SubtypingResult isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const NormalizedStringType& subString,
|
||||
|
|
|
@ -34,6 +34,7 @@ using ScopePtr = std::shared_ptr<Scope>;
|
|||
struct Module;
|
||||
|
||||
struct TypeFunction;
|
||||
struct TypeFun;
|
||||
struct Constraint;
|
||||
struct Subtyping;
|
||||
struct TypeChecker2;
|
||||
|
@ -608,7 +609,8 @@ struct UserDefinedFunctionData
|
|||
// References to AST elements are owned by the Module allocator which also stores this type
|
||||
AstStatTypeFunction* definition = nullptr;
|
||||
|
||||
DenseHashMap<Name, std::pair<AstStatTypeFunction*, size_t>> environment{""};
|
||||
DenseHashMap<Name, std::pair<AstStatTypeFunction*, size_t>> environmentFunction{""};
|
||||
DenseHashMap<Name, std::pair<TypeFun*, size_t>> environmentAlias{""};
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -21,6 +21,8 @@ namespace Luau
|
|||
struct TypeArena;
|
||||
struct TxnLog;
|
||||
struct ConstraintSolver;
|
||||
struct TypeFunctionRuntimeBuilderState;
|
||||
struct TypeFunctionContext;
|
||||
class Normalizer;
|
||||
|
||||
using StateRef = std::unique_ptr<lua_State, void (*)(lua_State*)>;
|
||||
|
@ -54,6 +56,9 @@ struct TypeFunctionRuntime
|
|||
// Output created by 'print' function
|
||||
std::vector<std::string> messages;
|
||||
|
||||
// Type builder, valid for the duration of a single evaluation
|
||||
TypeFunctionRuntimeBuilderState* runtimeBuilder = nullptr;
|
||||
|
||||
private:
|
||||
void prepareState();
|
||||
};
|
||||
|
|
|
@ -15,6 +15,8 @@ using lua_State = struct lua_State;
|
|||
namespace Luau
|
||||
{
|
||||
|
||||
struct TypeFunctionRuntime;
|
||||
|
||||
void* typeFunctionAlloc(void* ud, void* ptr, size_t osize, size_t nsize);
|
||||
|
||||
// Replica of types from Type.h
|
||||
|
@ -274,6 +276,8 @@ T* getMutable(TypeFunctionTypeId tv)
|
|||
|
||||
std::optional<std::string> checkResultForError(lua_State* L, const char* typeFunctionName, int luaResult);
|
||||
|
||||
TypeFunctionRuntime* getTypeFunctionRuntime(lua_State* L);
|
||||
|
||||
TypeFunctionType* allocateTypeFunctionType(lua_State* L, TypeFunctionTypeVariant type);
|
||||
TypeFunctionTypePackVar* allocateTypeFunctionTypePack(lua_State* L, TypeFunctionTypePackVariant type);
|
||||
|
||||
|
|
67
Analysis/include/Luau/TypeIds.h
Normal file
67
Analysis/include/Luau/TypeIds.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#pragma once
|
||||
|
||||
#include "Luau/DenseHash.h"
|
||||
#include "Luau/TypeFwd.h"
|
||||
|
||||
#include <vector>
|
||||
#include <initializer_list>
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
/*
|
||||
* An ordered, hashable set of TypeIds.
|
||||
*/
|
||||
class TypeIds
|
||||
{
|
||||
private:
|
||||
DenseHashMap<TypeId, bool> types{nullptr};
|
||||
std::vector<TypeId> order;
|
||||
std::size_t hash = 0;
|
||||
|
||||
public:
|
||||
using iterator = std::vector<TypeId>::iterator;
|
||||
using const_iterator = std::vector<TypeId>::const_iterator;
|
||||
|
||||
TypeIds() = default;
|
||||
~TypeIds() = default;
|
||||
|
||||
TypeIds(std::initializer_list<TypeId> tys);
|
||||
|
||||
TypeIds(const TypeIds&) = default;
|
||||
TypeIds& operator=(const TypeIds&) = default;
|
||||
|
||||
TypeIds(TypeIds&&) = default;
|
||||
TypeIds& operator=(TypeIds&&) = default;
|
||||
|
||||
void insert(TypeId ty);
|
||||
/// Erase every element that does not also occur in tys
|
||||
void retain(const TypeIds& tys);
|
||||
void clear();
|
||||
|
||||
TypeId front() const;
|
||||
iterator begin();
|
||||
iterator end();
|
||||
const_iterator begin() const;
|
||||
const_iterator end() const;
|
||||
iterator erase(const_iterator it);
|
||||
void erase(TypeId ty);
|
||||
|
||||
size_t size() const;
|
||||
bool empty() const;
|
||||
size_t count(TypeId ty) const;
|
||||
|
||||
template<class Iterator>
|
||||
void insert(Iterator begin, Iterator end)
|
||||
{
|
||||
for (Iterator it = begin; it != end; ++it)
|
||||
insert(*it);
|
||||
}
|
||||
|
||||
bool operator==(const TypeIds& there) const;
|
||||
size_t getHash() const;
|
||||
bool isNever() const;
|
||||
};
|
||||
|
||||
} // namespace Luau
|
|
@ -301,4 +301,21 @@ bool fastIsSubtype(TypeId subTy, TypeId superTy);
|
|||
*/
|
||||
std::optional<TypeId> extractMatchingTableType(std::vector<TypeId>& tables, TypeId exprType, NotNull<BuiltinTypes> builtinTypes);
|
||||
|
||||
/**
|
||||
* @param item A member of a table in an AST
|
||||
* @return Whether the item is a key-value pair with a statically defined string key.
|
||||
*
|
||||
* ```
|
||||
* {
|
||||
* ["foo"] = ..., -- is a record
|
||||
* bar = ..., -- is a record
|
||||
* ..., -- not a record: non-string key (number)
|
||||
* [true] = ..., -- not a record: non-string key (boolean)
|
||||
* [ foobar() ] = ..., -- not a record: unknown key value.
|
||||
* ["foo" .. "bar"] = ..., -- not a record (don't make us handle it).
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
bool isRecord(const AstExprTable::Item& item);
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -173,12 +173,7 @@ static bool checkTypeMatch(
|
|||
unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit;
|
||||
|
||||
Subtyping subtyping{
|
||||
builtinTypes,
|
||||
NotNull{typeArena},
|
||||
NotNull{simplifier.get()},
|
||||
NotNull{&normalizer},
|
||||
NotNull{&typeFunctionRuntime},
|
||||
NotNull{&iceReporter}
|
||||
builtinTypes, NotNull{typeArena}, NotNull{simplifier.get()}, NotNull{&normalizer}, NotNull{&typeFunctionRuntime}, NotNull{&iceReporter}
|
||||
};
|
||||
|
||||
return subtyping.isSubtype(subTy, superTy, scope).isSubtype;
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include "Luau/NotNull.h"
|
||||
#include "Luau/Subtyping.h"
|
||||
#include "Luau/Symbol.h"
|
||||
#include "Luau/ToString.h"
|
||||
#include "Luau/Type.h"
|
||||
#include "Luau/TypeChecker2.h"
|
||||
#include "Luau/TypeFunction.h"
|
||||
|
@ -30,10 +29,11 @@
|
|||
*/
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3)
|
||||
LUAU_FASTFLAGVARIABLE(LuauMagicFreezeCheckBlocked2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFormatUseLastPosition)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUpdateSetMetatableTypeSignature)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -310,8 +310,8 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
|
|||
|
||||
TypeArena& arena = globals.globalTypes;
|
||||
NotNull<BuiltinTypes> builtinTypes = globals.builtinTypes;
|
||||
Scope* globalScope = nullptr; // NotNull<Scope> when removing FFlag::LuauEagerGeneralization
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
Scope* globalScope = nullptr; // NotNull<Scope> when removing FFlag::LuauEagerGeneralization2
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
globalScope = globals.globalScope.get();
|
||||
|
||||
if (FFlag::LuauSolverV2)
|
||||
|
@ -386,19 +386,30 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
|
|||
TypeId genericT = arena.addType(GenericType{globalScope, "T"});
|
||||
TypeId tMetaMT = arena.addType(MetatableType{genericT, genericMT});
|
||||
|
||||
// clang-format off
|
||||
// setmetatable<T: {}, MT>(T, MT) -> { @metatable MT, T }
|
||||
addGlobalBinding(globals, "setmetatable",
|
||||
arena.addType(
|
||||
FunctionType{
|
||||
{genericT, genericMT},
|
||||
{},
|
||||
arena.addTypePack(TypePack{{genericT, genericMT}}),
|
||||
arena.addTypePack(TypePack{{tMetaMT}})
|
||||
}
|
||||
), "@luau"
|
||||
);
|
||||
// clang-format on
|
||||
if (FFlag::LuauUpdateSetMetatableTypeSignature)
|
||||
{
|
||||
// setmetatable<T: {}, MT>(T, MT) -> setmetatable<T, MT>
|
||||
TypeId setmtReturn = arena.addType(TypeFunctionInstanceType{builtinTypeFunctions().setmetatableFunc, {genericT, genericMT}});
|
||||
addGlobalBinding(
|
||||
globals, "setmetatable", makeFunction(arena, std::nullopt, {genericT, genericMT}, {}, {genericT, genericMT}, {setmtReturn}), "@luau"
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
// clang-format off
|
||||
// setmetatable<T: {}, MT>(T, MT) -> { @metatable MT, T }
|
||||
addGlobalBinding(globals, "setmetatable",
|
||||
arena.addType(
|
||||
FunctionType{
|
||||
{genericT, genericMT},
|
||||
{},
|
||||
arena.addTypePack(TypePack{{genericT, genericMT}}),
|
||||
arena.addTypePack(TypePack{{tMetaMT}})
|
||||
}
|
||||
), "@luau"
|
||||
);
|
||||
// clang-format on
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -701,9 +712,10 @@ bool MagicFormat::typeCheck(const MagicFunctionTypeCheckContext& context)
|
|||
{
|
||||
TypeId actualTy = params[i + paramOffset];
|
||||
TypeId expectedTy = expected[i];
|
||||
Location location = FFlag::LuauFormatUseLastPosition
|
||||
? context.callSite->args.data[std::min(context.callSite->args.size - 1, i + (calledWithSelf ? 0 : paramOffset))]->location
|
||||
: context.callSite->args.data[i + (calledWithSelf ? 0 : paramOffset)]->location;
|
||||
Location location =
|
||||
FFlag::LuauFormatUseLastPosition
|
||||
? context.callSite->args.data[std::min(context.callSite->args.size - 1, i + (calledWithSelf ? 0 : paramOffset))]->location
|
||||
: context.callSite->args.data[i + (calledWithSelf ? 0 : paramOffset)]->location;
|
||||
// use subtyping instead here
|
||||
SubtypingResult result = context.typechecker->subtyping->isSubtype(actualTy, expectedTy, context.checkScope);
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include "Luau/Constraint.h"
|
||||
#include "Luau/VisitType.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -51,7 +51,7 @@ struct ReferenceCountInitializer : TypeOnceVisitor
|
|||
|
||||
bool visit(TypeId, const TypeFunctionInstanceType&) override
|
||||
{
|
||||
return FFlag::LuauEagerGeneralization && traverseIntoTypeFunctions;
|
||||
return FFlag::LuauEagerGeneralization2 && traverseIntoTypeFunctions;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -104,7 +104,7 @@ DenseHashSet<TypeId> Constraint::getMaybeMutatedFreeTypes() const
|
|||
{
|
||||
rci.traverse(fchc->argsPack);
|
||||
}
|
||||
else if (auto fcc = get<FunctionCallConstraint>(*this); fcc && FFlag::LuauEagerGeneralization)
|
||||
else if (auto fcc = get<FunctionCallConstraint>(*this); fcc && FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
rci.traverseIntoTypeFunctions = false;
|
||||
rci.traverse(fcc->fn);
|
||||
|
@ -118,12 +118,12 @@ DenseHashSet<TypeId> Constraint::getMaybeMutatedFreeTypes() const
|
|||
else if (auto hpc = get<HasPropConstraint>(*this))
|
||||
{
|
||||
rci.traverse(hpc->resultType);
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
rci.traverse(hpc->subjectType);
|
||||
}
|
||||
else if (auto hic = get<HasIndexerConstraint>(*this))
|
||||
{
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
rci.traverse(hic->subjectType);
|
||||
rci.traverse(hic->resultType);
|
||||
// `HasIndexerConstraint` should not mutate `indexType`.
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "Luau/DenseHash.h"
|
||||
#include "Luau/InferPolarity.h"
|
||||
#include "Luau/ModuleResolver.h"
|
||||
#include "Luau/Normalize.h"
|
||||
#include "Luau/NotNull.h"
|
||||
#include "Luau/RecursionCounter.h"
|
||||
#include "Luau/Refinement.h"
|
||||
|
@ -33,11 +34,8 @@
|
|||
LUAU_FASTINT(LuauCheckRecursionLimit)
|
||||
LUAU_FASTFLAG(DebugLuauLogSolverToJson)
|
||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauRetainDefinitionAliasLocations)
|
||||
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauWeakNilRefinementType)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation)
|
||||
|
@ -51,6 +49,7 @@ LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
|
|||
LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops)
|
||||
LUAU_FASTFLAGVARIABLE(LuauDisablePrimitiveInferenceInLargeTables)
|
||||
LUAU_FASTINTVARIABLE(LuauPrimitiveInferenceInTableLimit, 500)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunctionAliases)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -180,6 +179,22 @@ bool hasFreeType(TypeId ty)
|
|||
return hft.result;
|
||||
}
|
||||
|
||||
struct GlobalNameCollector : public AstVisitor
|
||||
{
|
||||
DenseHashSet<AstName> names;
|
||||
|
||||
GlobalNameCollector()
|
||||
: names(AstName())
|
||||
{
|
||||
}
|
||||
|
||||
bool visit(AstExprGlobal* node) override
|
||||
{
|
||||
names.insert(node->name);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
ConstraintGenerator::ConstraintGenerator(
|
||||
|
@ -220,26 +235,14 @@ ConstraintSet ConstraintGenerator::run(AstStatBlock* block)
|
|||
{
|
||||
visitModuleRoot(block);
|
||||
|
||||
return ConstraintSet{
|
||||
NotNull{rootScope},
|
||||
std::move(constraints),
|
||||
std::move(freeTypes),
|
||||
std::move(scopeToFunction),
|
||||
std::move(errors)
|
||||
};
|
||||
return ConstraintSet{NotNull{rootScope}, std::move(constraints), std::move(freeTypes), std::move(scopeToFunction), std::move(errors)};
|
||||
}
|
||||
|
||||
ConstraintSet ConstraintGenerator::runOnFragment(const ScopePtr& resumeScope, AstStatBlock* block)
|
||||
{
|
||||
visitFragmentRoot(resumeScope, block);
|
||||
|
||||
return ConstraintSet{
|
||||
NotNull{rootScope},
|
||||
std::move(constraints),
|
||||
std::move(freeTypes),
|
||||
std::move(scopeToFunction),
|
||||
std::move(errors)
|
||||
};
|
||||
return ConstraintSet{NotNull{rootScope}, std::move(constraints), std::move(freeTypes), std::move(scopeToFunction), std::move(errors)};
|
||||
}
|
||||
|
||||
void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
|
||||
|
@ -254,7 +257,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
|
|||
rootScope->location = block->location;
|
||||
module->astScopes[block] = NotNull{scope.get()};
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
interiorFreeTypes.emplace_back();
|
||||
else
|
||||
DEPRECATED_interiorTypes.emplace_back();
|
||||
|
@ -290,7 +293,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
|
|||
}
|
||||
);
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
scope->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
|
||||
scope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks);
|
||||
|
@ -309,7 +312,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
|
|||
}
|
||||
);
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
interiorFreeTypes.pop_back();
|
||||
else
|
||||
DEPRECATED_interiorTypes.pop_back();
|
||||
|
@ -347,13 +350,13 @@ void ConstraintGenerator::visitFragmentRoot(const ScopePtr& resumeScope, AstStat
|
|||
// We prepopulate global data in the resumeScope to avoid writing data into the old modules scopes
|
||||
prepopulateGlobalScopeForFragmentTypecheck(globalScope, resumeScope, block);
|
||||
// Pre
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
interiorFreeTypes.emplace_back();
|
||||
else
|
||||
DEPRECATED_interiorTypes.emplace_back();
|
||||
visitBlockWithoutChildScope(resumeScope, block);
|
||||
// Post
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
interiorFreeTypes.pop_back();
|
||||
else
|
||||
DEPRECATED_interiorTypes.pop_back();
|
||||
|
@ -383,12 +386,12 @@ void ConstraintGenerator::visitFragmentRoot(const ScopePtr& resumeScope, AstStat
|
|||
|
||||
TypeId ConstraintGenerator::freshType(const ScopePtr& scope, Polarity polarity)
|
||||
{
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
auto ft = Luau::freshType(arena, builtinTypes, scope.get(), polarity);
|
||||
interiorFreeTypes.back().types.push_back(ft);
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
freeTypes.insert(ft);
|
||||
|
||||
return ft;
|
||||
|
@ -405,7 +408,7 @@ TypePackId ConstraintGenerator::freshTypePack(const ScopePtr& scope, Polarity po
|
|||
{
|
||||
FreeTypePack f{scope.get(), polarity};
|
||||
TypePackId result = arena->addTypePack(TypePackVar{std::move(f)});
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
interiorFreeTypes.back().typePacks.push_back(result);
|
||||
return result;
|
||||
}
|
||||
|
@ -818,8 +821,7 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc
|
|||
initialFun.typePackParams.push_back(genPack);
|
||||
}
|
||||
|
||||
if (FFlag::LuauRetainDefinitionAliasLocations)
|
||||
initialFun.definitionLocation = alias->location;
|
||||
initialFun.definitionLocation = alias->location;
|
||||
|
||||
if (alias->exported)
|
||||
scope->exportedTypeBindings[alias->name.value] = std::move(initialFun);
|
||||
|
@ -873,8 +875,7 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc
|
|||
|
||||
TypeFun typeFunction{std::move(quantifiedTypeParams), typeFunctionTy};
|
||||
|
||||
if (FFlag::LuauRetainDefinitionAliasLocations)
|
||||
typeFunction.definitionLocation = function->location;
|
||||
typeFunction.definitionLocation = function->location;
|
||||
|
||||
// Set type bindings and definition locations for this user-defined type function
|
||||
if (function->exported)
|
||||
|
@ -903,8 +904,7 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc
|
|||
|
||||
TypeId initialType = arena->addType(BlockedType{});
|
||||
TypeFun initialFun{initialType};
|
||||
if (FFlag::LuauRetainDefinitionAliasLocations)
|
||||
initialFun.definitionLocation = classDeclaration->location;
|
||||
initialFun.definitionLocation = classDeclaration->location;
|
||||
scope->exportedTypeBindings[classDeclaration->name.value] = std::move(initialFun);
|
||||
|
||||
classDefinitionLocations[classDeclaration->name.value] = classDeclaration->location;
|
||||
|
@ -936,20 +936,24 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc
|
|||
mainTypeFun = getMutable<TypeFunctionInstanceType>(it->second.type);
|
||||
}
|
||||
|
||||
// Fill it with all visible type functions
|
||||
// Fill it with all visible type functions and referenced type aliases
|
||||
if (mainTypeFun)
|
||||
{
|
||||
GlobalNameCollector globalNameCollector;
|
||||
stat->visit(&globalNameCollector);
|
||||
|
||||
UserDefinedFunctionData& userFuncData = mainTypeFun->userFuncData;
|
||||
size_t level = 0;
|
||||
|
||||
auto addToEnvironment = [this](UserDefinedFunctionData& userFuncData, ScopePtr scope, const Name& name, TypeId type, size_t level)
|
||||
auto addToEnvironment =
|
||||
[this, &globalNameCollector](UserDefinedFunctionData& userFuncData, ScopePtr scope, const Name& name, TypeFun tf, size_t level)
|
||||
{
|
||||
if (userFuncData.environment.find(name))
|
||||
return;
|
||||
|
||||
if (auto ty = get<TypeFunctionInstanceType>(type); ty && ty->userFuncData.definition)
|
||||
if (auto ty = get<TypeFunctionInstanceType>(tf.type); ty && ty->userFuncData.definition)
|
||||
{
|
||||
userFuncData.environment[name] = std::make_pair(ty->userFuncData.definition, level);
|
||||
if (userFuncData.environmentFunction.find(name))
|
||||
return;
|
||||
|
||||
userFuncData.environmentFunction[name] = std::make_pair(ty->userFuncData.definition, level);
|
||||
|
||||
if (auto it = astTypeFunctionEnvironmentScopes.find(ty->userFuncData.definition))
|
||||
{
|
||||
|
@ -957,15 +961,35 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc
|
|||
scope->bindings[ty->userFuncData.definition->name] = Binding{existing->typeId, ty->userFuncData.definition->location};
|
||||
}
|
||||
}
|
||||
else if (FFlag::LuauUserTypeFunctionAliases && !get<TypeFunctionInstanceType>(tf.type))
|
||||
{
|
||||
if (userFuncData.environmentAlias.find(name))
|
||||
return;
|
||||
|
||||
AstName astName = module->names->get(name.c_str());
|
||||
|
||||
// Only register globals that we have detected to be used
|
||||
if (!globalNameCollector.names.find(astName))
|
||||
return;
|
||||
|
||||
// Function evaluation environment needs a stable reference to the alias
|
||||
module->typeFunctionAliases.push_back(std::make_unique<TypeFun>(tf));
|
||||
|
||||
userFuncData.environmentAlias[name] = std::make_pair(module->typeFunctionAliases.back().get(), level);
|
||||
|
||||
// TODO: create a specific type alias type
|
||||
scope->bindings[astName] = Binding{builtinTypes->anyType, tf.definitionLocation.value_or(Location())};
|
||||
}
|
||||
};
|
||||
|
||||
for (Scope* curr = scope.get(); curr; curr = curr->parent.get())
|
||||
// Go up the scopes to register type functions and alises, but without reaching into the global scope
|
||||
for (Scope* curr = scope.get(); curr && (!FFlag::LuauUserTypeFunctionAliases || curr != globalScope.get()); curr = curr->parent.get())
|
||||
{
|
||||
for (auto& [name, tf] : curr->privateTypeBindings)
|
||||
addToEnvironment(userFuncData, typeFunctionEnvScope, name, tf.type, level);
|
||||
addToEnvironment(userFuncData, typeFunctionEnvScope, name, tf, level);
|
||||
|
||||
for (auto& [name, tf] : curr->exportedTypeBindings)
|
||||
addToEnvironment(userFuncData, typeFunctionEnvScope, name, tf.type, level);
|
||||
addToEnvironment(userFuncData, typeFunctionEnvScope, name, tf, level);
|
||||
|
||||
level++;
|
||||
}
|
||||
|
@ -1396,7 +1420,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatLocalFuncti
|
|||
FunctionSignature sig = checkFunctionSignature(scope, function->func, /* expectedType */ std::nullopt, function->name->location);
|
||||
sig.bodyScope->bindings[function->name] = Binding{sig.signature, function->name->location};
|
||||
|
||||
bool sigFullyDefined = FFlag::LuauEagerGeneralization ? false : !hasFreeType(sig.signature);
|
||||
bool sigFullyDefined = FFlag::LuauEagerGeneralization2 ? false : !hasFreeType(sig.signature);
|
||||
if (sigFullyDefined)
|
||||
emplaceType<BoundType>(asMutable(functionType), sig.signature);
|
||||
|
||||
|
@ -1456,7 +1480,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatFunction* f
|
|||
|
||||
Checkpoint start = checkpoint(this);
|
||||
FunctionSignature sig = checkFunctionSignature(scope, function->func, /* expectedType */ std::nullopt, function->name->location);
|
||||
bool sigFullyDefined = FFlag::LuauEagerGeneralization ? false : !hasFreeType(sig.signature);
|
||||
bool sigFullyDefined = FFlag::LuauEagerGeneralization2 ? false : !hasFreeType(sig.signature);
|
||||
|
||||
DefId def = dfg->getDef(function->name);
|
||||
|
||||
|
@ -1770,7 +1794,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio
|
|||
// Place this function as a child of the non-type function scope
|
||||
scope->children.push_back(NotNull{sig.signatureScope.get()});
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
interiorFreeTypes.emplace_back();
|
||||
else
|
||||
DEPRECATED_interiorTypes.push_back(std::vector<TypeId>{});
|
||||
|
@ -1788,7 +1812,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio
|
|||
}
|
||||
);
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
sig.signatureScope->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
|
||||
sig.signatureScope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks);
|
||||
|
@ -1797,7 +1821,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio
|
|||
sig.signatureScope->interiorFreeTypes = std::move(DEPRECATED_interiorTypes.back());
|
||||
|
||||
getMutable<BlockedType>(generalizedTy)->setOwner(gc);
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
interiorFreeTypes.pop_back();
|
||||
else
|
||||
DEPRECATED_interiorTypes.pop_back();
|
||||
|
@ -1884,7 +1908,8 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareExte
|
|||
{
|
||||
reportError(
|
||||
declaredExternType->location,
|
||||
GenericError{format("Cannot use non-class type '%s' as a superclass of class '%s'", superName.c_str(), declaredExternType->name.value)}
|
||||
GenericError{format("Cannot use non-class type '%s' as a superclass of class '%s'", superName.c_str(), declaredExternType->name.value)
|
||||
}
|
||||
);
|
||||
|
||||
return ControlFlow::None;
|
||||
|
@ -2466,7 +2491,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantStrin
|
|||
return Inference{builtinTypes->stringType};
|
||||
|
||||
TypeId freeTy = nullptr;
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
freeTy = freshType(scope, Polarity::Positive);
|
||||
FreeType* ft = getMutable<FreeType>(freeTy);
|
||||
|
@ -2507,7 +2532,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantBool*
|
|||
return Inference{builtinTypes->booleanType};
|
||||
|
||||
TypeId freeTy = nullptr;
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
freeTy = freshType(scope, Polarity::Positive);
|
||||
FreeType* ft = getMutable<FreeType>(freeTy);
|
||||
|
@ -2605,9 +2630,7 @@ Inference ConstraintGenerator::checkIndexName(
|
|||
{
|
||||
result = arena->addType(BlockedType{});
|
||||
|
||||
auto c = addConstraint(
|
||||
scope, indexee->location, HasPropConstraint{result, obj, std::move(index), ValueContext::RValue, inConditional(typeContext)}
|
||||
);
|
||||
auto c = addConstraint(scope, indexee->location, HasPropConstraint{result, obj, index, ValueContext::RValue, inConditional(typeContext)});
|
||||
getMutable<BlockedType>(result)->setOwner(c);
|
||||
}
|
||||
|
||||
|
@ -2668,7 +2691,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprFunction* fun
|
|||
Checkpoint startCheckpoint = checkpoint(this);
|
||||
FunctionSignature sig = checkFunctionSignature(scope, func, expectedType);
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
interiorFreeTypes.emplace_back();
|
||||
else
|
||||
DEPRECATED_interiorTypes.push_back(std::vector<TypeId>{});
|
||||
|
@ -2686,7 +2709,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprFunction* fun
|
|||
}
|
||||
);
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
sig.signatureScope->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
|
||||
sig.signatureScope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks);
|
||||
|
@ -3190,10 +3213,11 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr,
|
|||
ttv->definitionLocation = expr->location;
|
||||
ttv->scope = scope.get();
|
||||
|
||||
if (FFlag::LuauDisablePrimitiveInferenceInLargeTables && FInt::LuauPrimitiveInferenceInTableLimit > 0 && expr->items.size > size_t(FInt::LuauPrimitiveInferenceInTableLimit))
|
||||
if (FFlag::LuauDisablePrimitiveInferenceInLargeTables && FInt::LuauPrimitiveInferenceInTableLimit > 0 &&
|
||||
expr->items.size > size_t(FInt::LuauPrimitiveInferenceInTableLimit))
|
||||
largeTableDepth++;
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
interiorFreeTypes.back().types.push_back(ty);
|
||||
else
|
||||
DEPRECATED_interiorTypes.back().push_back(ty);
|
||||
|
@ -3301,7 +3325,8 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr,
|
|||
);
|
||||
}
|
||||
|
||||
if (FFlag::LuauDisablePrimitiveInferenceInLargeTables && FInt::LuauPrimitiveInferenceInTableLimit > 0 && expr->items.size > size_t(FInt::LuauPrimitiveInferenceInTableLimit))
|
||||
if (FFlag::LuauDisablePrimitiveInferenceInLargeTables && FInt::LuauPrimitiveInferenceInTableLimit > 0 &&
|
||||
expr->items.size > size_t(FInt::LuauPrimitiveInferenceInTableLimit))
|
||||
largeTableDepth--;
|
||||
|
||||
return Inference{ty};
|
||||
|
@ -3453,7 +3478,7 @@ ConstraintGenerator::FunctionSignature ConstraintGenerator::checkFunctionSignatu
|
|||
|
||||
LUAU_ASSERT(nullptr != varargPack);
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
// Some of the types in argTypes will eventually be generics, and some
|
||||
// will not. The ones that are not generic will be pruned when
|
||||
|
@ -3518,7 +3543,7 @@ ConstraintGenerator::FunctionSignature ConstraintGenerator::checkFunctionSignatu
|
|||
if (expectedType && get<FreeType>(*expectedType))
|
||||
bindFreeType(*expectedType, actualFunctionType);
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
scopeToFunction[signatureScope.get()] = actualFunctionType;
|
||||
|
||||
return {
|
||||
|
@ -3809,33 +3834,33 @@ TypeId ConstraintGenerator::resolveType_(const ScopePtr& scope, AstType* ty, boo
|
|||
}
|
||||
else if (auto unionAnnotation = ty->as<AstTypeUnion>())
|
||||
{
|
||||
if (unionAnnotation->types.size == 1)
|
||||
result = resolveType_(scope, unionAnnotation->types.data[0], inTypeArguments);
|
||||
else
|
||||
if (unionAnnotation->types.size == 1)
|
||||
result = resolveType_(scope, unionAnnotation->types.data[0], inTypeArguments);
|
||||
else
|
||||
{
|
||||
std::vector<TypeId> parts;
|
||||
for (AstType* part : unionAnnotation->types)
|
||||
{
|
||||
std::vector<TypeId> parts;
|
||||
for (AstType* part : unionAnnotation->types)
|
||||
{
|
||||
parts.push_back(resolveType_(scope, part, inTypeArguments));
|
||||
}
|
||||
|
||||
result = arena->addType(UnionType{parts});
|
||||
parts.push_back(resolveType_(scope, part, inTypeArguments));
|
||||
}
|
||||
|
||||
result = arena->addType(UnionType{parts});
|
||||
}
|
||||
}
|
||||
else if (auto intersectionAnnotation = ty->as<AstTypeIntersection>())
|
||||
{
|
||||
if (intersectionAnnotation->types.size == 1)
|
||||
result = resolveType_(scope, intersectionAnnotation->types.data[0], inTypeArguments);
|
||||
else
|
||||
if (intersectionAnnotation->types.size == 1)
|
||||
result = resolveType_(scope, intersectionAnnotation->types.data[0], inTypeArguments);
|
||||
else
|
||||
{
|
||||
std::vector<TypeId> parts;
|
||||
for (AstType* part : intersectionAnnotation->types)
|
||||
{
|
||||
std::vector<TypeId> parts;
|
||||
for (AstType* part : intersectionAnnotation->types)
|
||||
{
|
||||
parts.push_back(resolveType_(scope, part, inTypeArguments));
|
||||
}
|
||||
|
||||
result = arena->addType(IntersectionType{parts});
|
||||
parts.push_back(resolveType_(scope, part, inTypeArguments));
|
||||
}
|
||||
|
||||
result = arena->addType(IntersectionType{parts});
|
||||
}
|
||||
}
|
||||
else if (auto typeGroupAnnotation = ty->as<AstTypeGroup>())
|
||||
{
|
||||
|
|
|
@ -33,9 +33,8 @@ LUAU_FASTFLAGVARIABLE(DebugLuauLogBindings)
|
|||
LUAU_FASTINTVARIABLE(LuauSolverRecursionLimit, 500)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification)
|
||||
LUAU_FASTFLAGVARIABLE(LuauHasPropProperBlock)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||
LUAU_FASTFLAG(LuauDeprecatedAttribute)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTrackInferredFunctionTypeFromCall)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAddCallConstraintForIterableFunctions)
|
||||
LUAU_FASTFLAGVARIABLE(LuauGuardAgainstMalformedTypeAliasExpansion2)
|
||||
|
@ -43,6 +42,7 @@ LUAU_FASTFLAGVARIABLE(LuauInsertErrorTypesIntoIndexerResult)
|
|||
LUAU_FASTFLAGVARIABLE(LuauClipVariadicAnysFromArgsToGenericFuncs2)
|
||||
LUAU_FASTFLAG(LuauSubtypeGenericsAndNegations)
|
||||
LUAU_FASTFLAG(LuauNoMoreInjectiveTypeFunctions)
|
||||
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -103,7 +103,7 @@ size_t HashBlockedConstraintId::operator()(const BlockedConstraintId& bci) const
|
|||
return true;
|
||||
}
|
||||
|
||||
static std::pair<std::vector<TypeId>, std::vector<TypePackId>> saturateArguments(
|
||||
std::pair<std::vector<TypeId>, std::vector<TypePackId>> saturateArguments(
|
||||
TypeArena* arena,
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
const TypeFun& fn,
|
||||
|
@ -418,7 +418,7 @@ void ConstraintSolver::run()
|
|||
}
|
||||
|
||||
// Free types that have no constraints at all can be generalized right away.
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
for (TypeId ty : constraintSet.freeTypes)
|
||||
{
|
||||
|
@ -479,7 +479,7 @@ void ConstraintSolver::run()
|
|||
// expansion types, etc, so we need to follow it.
|
||||
ty = follow(ty);
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
if (seen.contains(ty))
|
||||
continue;
|
||||
|
@ -498,7 +498,7 @@ void ConstraintSolver::run()
|
|||
if (refCount <= 1)
|
||||
unblock(ty, Location{});
|
||||
|
||||
if (FFlag::LuauEagerGeneralization && refCount == 0)
|
||||
if (FFlag::LuauEagerGeneralization2 && refCount == 0)
|
||||
generalizeOneType(ty);
|
||||
}
|
||||
}
|
||||
|
@ -676,7 +676,7 @@ void ConstraintSolver::initFreeTypeTracking()
|
|||
auto [refCount, _] = unresolvedConstraints.try_insert(ty, 0);
|
||||
refCount += 1;
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
auto [it, fresh] = mutatedFreeTypeToConstraint.try_emplace(ty, nullptr);
|
||||
it->second.insert(c.get());
|
||||
|
@ -691,7 +691,7 @@ void ConstraintSolver::initFreeTypeTracking()
|
|||
}
|
||||
|
||||
// Also check flag integrity while we're here
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauSubtypeGenericsAndNegations);
|
||||
LUAU_ASSERT(FFlag::LuauNoMoreInjectiveTypeFunctions);
|
||||
|
@ -739,7 +739,7 @@ void ConstraintSolver::bind(NotNull<const Constraint> constraint, TypeId ty, Typ
|
|||
constraint, ty, constraint->scope, builtinTypes->neverType, builtinTypes->unknownType, Polarity::Mixed
|
||||
); // FIXME? Is this the right polarity?
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
trackInteriorFreeType(constraint->scope, ty);
|
||||
|
||||
return;
|
||||
|
@ -900,7 +900,7 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<co
|
|||
{
|
||||
for (TypeId ty : *constraint->scope->interiorFreeTypes) // NOLINT(bugprone-unchecked-optional-access)
|
||||
{
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
ty = follow(ty);
|
||||
if (auto freeTy = get<FreeType>(ty))
|
||||
|
@ -922,7 +922,7 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<co
|
|||
}
|
||||
}
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
if (constraint->scope->interiorFreeTypePacks)
|
||||
{
|
||||
|
@ -942,7 +942,7 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<co
|
|||
}
|
||||
}
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
if (c.noGenerics)
|
||||
{
|
||||
|
@ -1378,7 +1378,6 @@ void ConstraintSolver::fillInDiscriminantTypes(NotNull<const Constraint> constra
|
|||
// We also need to unconditionally unblock these types, otherwise
|
||||
// you end up with funky looking "Blocked on *no-refine*."
|
||||
unblock(*ty, constraint->location);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1388,7 +1387,7 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
|
|||
TypePackId argsPack = follow(c.argsPack);
|
||||
TypePackId result = follow(c.result);
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
if (isBlocked(fn))
|
||||
return block(c.fn, constraint);
|
||||
|
@ -1528,7 +1527,7 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
|
|||
|
||||
const bool occursCheckPassed = u2.unify(overloadToUse, inferredTy);
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
for (TypeId freeTy : u2.newFreshTypes)
|
||||
trackInteriorFreeType(constraint->scope, freeTy);
|
||||
|
@ -1942,7 +1941,7 @@ bool ConstraintSolver::tryDispatchHasIndexer(
|
|||
TypeId upperBound =
|
||||
arena->addType(TableType{/* props */ {}, TableIndexer{indexType, resultType}, TypeLevel{}, ft->scope, TableState::Unsealed});
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
TypeId sr = follow(simplifyIntersection(constraint->scope, constraint->location, ft->upperBound, upperBound));
|
||||
|
||||
|
@ -1973,7 +1972,7 @@ bool ConstraintSolver::tryDispatchHasIndexer(
|
|||
|
||||
FreeType freeResult{tt->scope, builtinTypes->neverType, builtinTypes->unknownType, Polarity::Mixed};
|
||||
emplace<FreeType>(constraint, resultType, freeResult);
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
trackInteriorFreeType(constraint->scope, resultType);
|
||||
|
||||
tt->indexer = TableIndexer{indexType, resultType};
|
||||
|
@ -2056,7 +2055,6 @@ bool ConstraintSolver::tryDispatchHasIndexer(
|
|||
r = follow(r);
|
||||
if (FFlag::LuauInsertErrorTypesIntoIndexerResult || !get<ErrorType>(r))
|
||||
results.insert(r);
|
||||
|
||||
}
|
||||
|
||||
if (0 == results.size())
|
||||
|
@ -2163,7 +2161,7 @@ bool ConstraintSolver::tryDispatch(const AssignPropConstraint& c, NotNull<const
|
|||
{
|
||||
auto lhsFreeUpperBound = follow(lhsFree->upperBound);
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
const auto [blocked, maybeTy, isIndex] = lookupTableProp(constraint, lhsType, propName, ValueContext::LValue);
|
||||
if (!blocked.empty())
|
||||
|
@ -3064,7 +3062,7 @@ TablePropLookupResult ConstraintSolver::lookupTableProp(
|
|||
{
|
||||
const TypeId upperBound = follow(ft->upperBound);
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
if (get<TableType>(upperBound) || get<PrimitiveType>(upperBound))
|
||||
{
|
||||
|
@ -3532,7 +3530,7 @@ void ConstraintSolver::shiftReferences(TypeId source, TypeId target)
|
|||
|
||||
// Any constraint that might have mutated source may now mutate target
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
auto it = mutatedFreeTypeToConstraint.find(source);
|
||||
if (it != mutatedFreeTypeToConstraint.end())
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#include <unordered_set>
|
||||
|
||||
LUAU_FASTINTVARIABLE(LuauIndentTypeMismatchMaxTypeLength, 10)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauBetterCannotCallFunctionPrimitive)
|
||||
|
||||
|
@ -663,7 +663,7 @@ struct ErrorConverter
|
|||
}
|
||||
|
||||
// binary operators
|
||||
const auto binaryOps = FFlag::LuauEagerGeneralization ? kBinaryOps : DEPRECATED_kBinaryOps;
|
||||
const auto binaryOps = FFlag::LuauEagerGeneralization2 ? kBinaryOps : DEPRECATED_kBinaryOps;
|
||||
if (auto binaryString = binaryOps.find(tfit->function->name); binaryString != binaryOps.end())
|
||||
{
|
||||
std::string result = "Operator '" + std::string(binaryString->second) + "' could not be applied to operands of types ";
|
||||
|
@ -718,10 +718,10 @@ struct ErrorConverter
|
|||
"'";
|
||||
}
|
||||
|
||||
if ((FFlag::LuauEagerGeneralization ? kUnreachableTypeFunctions : DEPRECATED_kUnreachableTypeFunctions).count(tfit->function->name))
|
||||
if ((FFlag::LuauEagerGeneralization2 ? kUnreachableTypeFunctions : DEPRECATED_kUnreachableTypeFunctions).count(tfit->function->name))
|
||||
{
|
||||
return "Type function instance " + Luau::toString(e.ty) + " is uninhabited\n" +
|
||||
"This is likely to be a bug, please report it at https://github.com/luau-lang/luau/issues";
|
||||
"This is likely to be a bug, please report it at https://github.com/luau-lang/luau/issues";
|
||||
}
|
||||
|
||||
// Everything should be specialized above to report a more descriptive error that hopefully does not mention "type functions" explicitly.
|
||||
|
|
213
Analysis/src/ExpectedTypeVisitor.cpp
Normal file
213
Analysis/src/ExpectedTypeVisitor.cpp
Normal file
|
@ -0,0 +1,213 @@
|
|||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#include "Luau/ExpectedTypeVisitor.h"
|
||||
|
||||
#include "Luau/Scope.h"
|
||||
#include "Luau/TypeArena.h"
|
||||
#include "Luau/TypePack.h"
|
||||
#include "Luau/TypeUtils.h"
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
ExpectedTypeVisitor::ExpectedTypeVisitor(
|
||||
NotNull<DenseHashMap<const AstExpr*, TypeId>> astTypes,
|
||||
NotNull<DenseHashMap<const AstExpr*, TypeId>> astExpectedTypes,
|
||||
NotNull<DenseHashMap<const AstType*, TypeId>> astResolvedTypes,
|
||||
NotNull<TypeArena> arena,
|
||||
NotNull<BuiltinTypes> builtinTypes,
|
||||
NotNull<Scope> rootScope
|
||||
)
|
||||
: astTypes(astTypes)
|
||||
, astExpectedTypes(astExpectedTypes)
|
||||
, astResolvedTypes(astResolvedTypes)
|
||||
, arena(arena)
|
||||
, builtinTypes(builtinTypes)
|
||||
, rootScope(rootScope)
|
||||
{
|
||||
}
|
||||
|
||||
bool ExpectedTypeVisitor::visit(AstStatAssign* stat)
|
||||
{
|
||||
for (size_t idx = 0; idx < std::min(stat->vars.size, stat->values.size); idx++)
|
||||
{
|
||||
if (auto lhsType = astTypes->find(stat->vars.data[idx]))
|
||||
applyExpectedType(*lhsType, stat->values.data[idx]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExpectedTypeVisitor::visit(AstStatLocal* stat)
|
||||
{
|
||||
for (size_t idx = 0; idx < std::min(stat->vars.size, stat->values.size); idx++)
|
||||
{
|
||||
if (auto annot = astResolvedTypes->find(stat->vars.data[idx]->annotation))
|
||||
applyExpectedType(*annot, stat->values.data[idx]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExpectedTypeVisitor::visit(AstStatCompoundAssign* stat)
|
||||
{
|
||||
if (auto lhsType = astTypes->find(stat->var))
|
||||
applyExpectedType(*lhsType, stat->value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExpectedTypeVisitor::visit(AstStatReturn* stat)
|
||||
{
|
||||
auto scope = rootScope->findNarrowestScopeContaining(stat->location);
|
||||
|
||||
auto it = begin(scope->returnType);
|
||||
size_t idx = 0;
|
||||
|
||||
while (idx < stat->list.size && it != end(scope->returnType))
|
||||
{
|
||||
applyExpectedType(*it, stat->list.data[idx]);
|
||||
it++;
|
||||
idx++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExpectedTypeVisitor::visit(AstExprCall* expr)
|
||||
{
|
||||
auto ty = astTypes->find(expr->func);
|
||||
if (!ty)
|
||||
return true;
|
||||
|
||||
const FunctionType* ftv = get<FunctionType>(follow(*ty));
|
||||
|
||||
// FIXME: Bidirectional type checking of overloaded functions is not yet
|
||||
// supported, which means we *also* do not provide autocomplete for
|
||||
// the arguments of overloaded functions.
|
||||
if (!ftv)
|
||||
return true;
|
||||
|
||||
auto it = begin(ftv->argTypes);
|
||||
size_t idx = 0;
|
||||
|
||||
if (expr->self && it != end(ftv->argTypes))
|
||||
{
|
||||
// If we have a `foo:bar(...)` call, then the first type in the arg
|
||||
// pack will be the type of `self`, so we just skip that.
|
||||
it++;
|
||||
}
|
||||
|
||||
while (idx < expr->args.size && it != end(ftv->argTypes))
|
||||
{
|
||||
applyExpectedType(*it, expr->args.data[idx]);
|
||||
it++;
|
||||
idx++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExpectedTypeVisitor::visit(AstExprTypeAssertion* expr)
|
||||
{
|
||||
if (auto annot = astResolvedTypes->find(expr->annotation))
|
||||
applyExpectedType(*annot, expr->expr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ExpectedTypeVisitor::applyExpectedType(TypeId expectedType, const AstExpr* expr)
|
||||
{
|
||||
expectedType = follow(expectedType);
|
||||
|
||||
// No matter what, we set the expected type of the current expression to
|
||||
// whatever was just passed in. We may traverse the type and do more.
|
||||
(*astExpectedTypes)[expr] = expectedType;
|
||||
|
||||
if (const auto exprTable = expr->as<AstExprTable>())
|
||||
{
|
||||
const auto expectedTableType = get<TableType>(expectedType);
|
||||
if (!expectedTableType)
|
||||
{
|
||||
if (auto utv = get<UnionType>(expectedType))
|
||||
{
|
||||
if (auto exprType = astTypes->find(expr))
|
||||
{
|
||||
std::vector<TypeId> parts{begin(utv), end(utv)};
|
||||
if (auto tt = extractMatchingTableType(parts, *exprType, builtinTypes))
|
||||
{
|
||||
applyExpectedType(*tt, expr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have a table, then the expected type for any given key is a
|
||||
// union between all the possible keys and an indexer type (if it exists).
|
||||
std::vector<TypeId> possibleKeyTypes;
|
||||
possibleKeyTypes.reserve(expectedTableType->props.size() + (expectedTableType->indexer ? 1 : 0));
|
||||
for (const auto& [name, _] : expectedTableType->props)
|
||||
{
|
||||
possibleKeyTypes.push_back(arena->addType(SingletonType{StringSingleton{name}}));
|
||||
}
|
||||
|
||||
if (expectedTableType->indexer)
|
||||
possibleKeyTypes.push_back(expectedTableType->indexer->indexType);
|
||||
|
||||
TypeId expectedKeyType = nullptr;
|
||||
if (possibleKeyTypes.size() == 0)
|
||||
expectedKeyType = builtinTypes->neverType;
|
||||
else if (possibleKeyTypes.size() == 1)
|
||||
expectedKeyType = possibleKeyTypes[0];
|
||||
else
|
||||
expectedKeyType = arena->addType(UnionType{std::move(possibleKeyTypes)});
|
||||
|
||||
for (const AstExprTable::Item& item : exprTable->items)
|
||||
{
|
||||
if (isRecord(item))
|
||||
{
|
||||
const AstArray<char>& s = item.key->as<AstExprConstantString>()->value;
|
||||
std::string keyStr{s.data, s.data + s.size};
|
||||
|
||||
// No mater what, we can claim that the expected key type is the
|
||||
// union of all possible props plus the indexer.
|
||||
applyExpectedType(expectedKeyType, item.key);
|
||||
|
||||
// - If the property is defined and has a read type, apply it
|
||||
// as an expected type. e.g.:
|
||||
//
|
||||
// -- _ will have expected type `number`
|
||||
// local t: { [string]: number, write foo: boolean } = { foo = _ }
|
||||
//
|
||||
// - Otherwise if the property has an indexer, apply the result type.
|
||||
// - Otherwise do nothing.
|
||||
if (auto it = expectedTableType->props.find(keyStr); it != expectedTableType->props.end() && it->second.readTy)
|
||||
{
|
||||
applyExpectedType(*it->second.readTy, item.value);
|
||||
}
|
||||
else if (expectedTableType->indexer)
|
||||
{
|
||||
applyExpectedType(expectedTableType->indexer->indexResultType, item.value);
|
||||
}
|
||||
}
|
||||
else if (item.kind == AstExprTable::Item::List && expectedTableType->indexer)
|
||||
{
|
||||
applyExpectedType(expectedTableType->indexer->indexResultType, item.value);
|
||||
}
|
||||
else if (item.kind == AstExprTable::Item::General && expectedTableType->indexer)
|
||||
{
|
||||
applyExpectedType(expectedTableType->indexer->indexResultType, item.value);
|
||||
applyExpectedType(expectedKeyType, item.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (auto group = expr->as<AstExprGroup>())
|
||||
{
|
||||
applyExpectedType(expectedType, group->expr);
|
||||
}
|
||||
else if (auto ternary = expr->as<AstExprIfElse>())
|
||||
{
|
||||
applyExpectedType(expectedType, ternary->trueExpr);
|
||||
applyExpectedType(expectedType, ternary->falseExpr);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Luau
|
|
@ -6,6 +6,7 @@
|
|||
#include "Luau/Autocomplete.h"
|
||||
#include "Luau/Common.h"
|
||||
#include "Luau/EqSatSimplification.h"
|
||||
#include "Luau/ExpectedTypeVisitor.h"
|
||||
#include "Luau/ModuleResolver.h"
|
||||
#include "Luau/Parser.h"
|
||||
#include "Luau/ParseOptions.h"
|
||||
|
@ -35,6 +36,8 @@ LUAU_FASTFLAGVARIABLE(LuauFragmentAcMemoryLeak)
|
|||
LUAU_FASTFLAGVARIABLE(LuauGlobalVariableModuleIsolation)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFragmentAutocompleteIfRecommendations)
|
||||
LUAU_FASTFLAG(LuauExpectedTypeVisitor)
|
||||
LUAU_FASTFLAGVARIABLE(LuauPopulateRefinedTypesInFragmentFromOldSolver)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -669,6 +672,7 @@ void cloneTypesFromFragment(
|
|||
destScope->lvalueTypes[d] = Luau::cloneIncremental(*lValue, *destArena, cloneState, destScope);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& [d, loc] : f.localBindingsReferenced)
|
||||
{
|
||||
if (std::optional<std::pair<Symbol, Binding>> pair = staleScope->linearSearchForBindingPair(loc->name.value, true))
|
||||
|
@ -685,6 +689,21 @@ void cloneTypesFromFragment(
|
|||
}
|
||||
}
|
||||
|
||||
if (FFlag::LuauPopulateRefinedTypesInFragmentFromOldSolver && !staleModule->checkedInNewSolver)
|
||||
{
|
||||
for (const auto& [d, loc] : f.localBindingsReferenced)
|
||||
{
|
||||
for (const Scope* stale = staleScope; stale; stale = stale->parent.get())
|
||||
{
|
||||
if (auto res = stale->refinements.find(Symbol(loc)); res != stale->refinements.end())
|
||||
{
|
||||
destScope->rvalueRefinements[d] = Luau::cloneIncremental(res->second, *destArena, cloneState, destScope);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Second - any referenced type alias bindings need to be placed in scope so type annotation can be resolved.
|
||||
// If the actual type alias appears in the fragment on the lhs as a definition (in declaredAliases), it will be processed during typechecking
|
||||
// anyway
|
||||
|
@ -1191,6 +1210,19 @@ FragmentTypeCheckResult typecheckFragment_(
|
|||
|
||||
reportWaypoint(reporter, FragmentAutocompleteWaypoint::ConstraintSolverEnd);
|
||||
|
||||
if (FFlag::LuauExpectedTypeVisitor)
|
||||
{
|
||||
ExpectedTypeVisitor etv{
|
||||
NotNull{&incrementalModule->astTypes},
|
||||
NotNull{&incrementalModule->astExpectedTypes},
|
||||
NotNull{&incrementalModule->astResolvedTypes},
|
||||
NotNull{&incrementalModule->internalTypes},
|
||||
frontend.builtinTypes,
|
||||
NotNull{freshChildOfNearestScope.get()}
|
||||
};
|
||||
root->visit(&etv);
|
||||
}
|
||||
|
||||
// In frontend we would forbid internal types
|
||||
// because this is just for autocomplete, we don't actually care
|
||||
// We also don't even need to typecheck - just synthesize types as best as we can
|
||||
|
@ -1243,7 +1275,8 @@ std::pair<FragmentTypeCheckStatus, FragmentTypeCheckResult> typecheckFragment(
|
|||
FrontendOptions frontendOptions = opts.value_or(frontend.options);
|
||||
const ScopePtr& closestScope = FFlag::LuauBetterScopeSelection ? findClosestScope(module, parseResult.scopePos)
|
||||
: findClosestScope_DEPRECATED(module, parseResult.nearestStatement);
|
||||
FragmentTypeCheckResult result = typecheckFragment_(frontend, parseResult.root, module, closestScope, cursorPos, std::move(parseResult.alloc), frontendOptions, reporter);
|
||||
FragmentTypeCheckResult result =
|
||||
typecheckFragment_(frontend, parseResult.root, module, closestScope, cursorPos, std::move(parseResult.alloc), frontendOptions, reporter);
|
||||
result.ancestry = std::move(parseResult.ancestry);
|
||||
reportFragmentString(reporter, tryParse->fragmentToParse);
|
||||
return {FragmentTypeCheckStatus::Success, result};
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "Luau/DataFlowGraph.h"
|
||||
#include "Luau/DcrLogger.h"
|
||||
#include "Luau/EqSatSimplification.h"
|
||||
#include "Luau/ExpectedTypeVisitor.h"
|
||||
#include "Luau/FileResolver.h"
|
||||
#include "Luau/NonStrictTypeChecker.h"
|
||||
#include "Luau/NotNull.h"
|
||||
|
@ -39,13 +40,14 @@ LUAU_FASTINT(LuauTarjanChildLimit)
|
|||
LUAU_FASTFLAG(LuauInferInNoCheckMode)
|
||||
LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3)
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJsonFile)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauForbidInternalTypes)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauForceStrictMode)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauForceNonStrictMode)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNewSolverTypecheckCatchTimeouts)
|
||||
LUAU_FASTFLAGVARIABLE(LuauExpectedTypeVisitor)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -1436,13 +1438,13 @@ ModulePtr check(
|
|||
requireCycles
|
||||
};
|
||||
|
||||
// FIXME: Delete this flag when clipping FFlag::LuauEagerGeneralization.
|
||||
// FIXME: Delete this flag when clipping FFlag::LuauEagerGeneralization2.
|
||||
//
|
||||
// This optional<> only exists so that we can run one constructor when the flag
|
||||
// is set, and another when it is unset.
|
||||
std::optional<ConstraintSolver> cs;
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
ConstraintSet constraintSet = cg.run(sourceModule.root);
|
||||
result->errors = std::move(constraintSet.errors);
|
||||
|
@ -1610,6 +1612,19 @@ ModulePtr check(
|
|||
};
|
||||
}
|
||||
|
||||
if (FFlag::LuauExpectedTypeVisitor)
|
||||
{
|
||||
ExpectedTypeVisitor etv{
|
||||
NotNull{&result->astTypes},
|
||||
NotNull{&result->astExpectedTypes},
|
||||
NotNull{&result->astResolvedTypes},
|
||||
NotNull{&result->internalTypes},
|
||||
builtinTypes,
|
||||
NotNull{parentScope.get()}
|
||||
};
|
||||
sourceModule.root->visit(&etv);
|
||||
}
|
||||
|
||||
unfreeze(result->interfaceTypes);
|
||||
result->clonePublicInterface(builtinTypes, *iceHandler);
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAGVARIABLE(LuauEagerGeneralization2)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -469,7 +469,7 @@ struct FreeTypeSearcher : TypeVisitor
|
|||
|
||||
bool visit(TypeId ty, const FreeType& ft) override
|
||||
{
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
if (!subsumes(scope, ft.scope))
|
||||
return true;
|
||||
|
@ -520,7 +520,7 @@ struct FreeTypeSearcher : TypeVisitor
|
|||
|
||||
if ((tt.state == TableState::Free || tt.state == TableState::Unsealed) && subsumes(scope, tt.scope))
|
||||
{
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
unsealedTables.insert(ty);
|
||||
else
|
||||
{
|
||||
|
@ -574,7 +574,6 @@ struct FreeTypeSearcher : TypeVisitor
|
|||
traverse(*prop.writeTy);
|
||||
polarity = p;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -594,7 +593,7 @@ struct FreeTypeSearcher : TypeVisitor
|
|||
|
||||
if (tt.indexer)
|
||||
{
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
// {[K]: V} is equivalent to three functions: get, set, and iterate
|
||||
//
|
||||
|
@ -652,7 +651,7 @@ struct FreeTypeSearcher : TypeVisitor
|
|||
if (!subsumes(scope, ftp.scope))
|
||||
return true;
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
GeneralizationParams<TypePackId>& params = typePacks[tp];
|
||||
++params.useCount;
|
||||
|
@ -1247,8 +1246,7 @@ struct RemoveType : Substitution // NOLINT
|
|||
* @param needle The type to be removed.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
static std::optional<
|
||||
TypeId> removeType(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, TypeId haystack, TypeId needle)
|
||||
static std::optional<TypeId> removeType(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, TypeId haystack, TypeId needle)
|
||||
{
|
||||
RemoveType rt{builtinTypes, arena, needle};
|
||||
return rt.substitute(haystack);
|
||||
|
@ -1276,7 +1274,7 @@ GeneralizationResult<TypeId> generalizeType(
|
|||
|
||||
if (!hasLowerBound && !hasUpperBound)
|
||||
{
|
||||
if (!isWithinFunction || (!FFlag::LuauEagerGeneralization && (params.polarity != Polarity::Mixed && params.useCount == 1)))
|
||||
if (!isWithinFunction || (!FFlag::LuauEagerGeneralization2 && (params.polarity != Polarity::Mixed && params.useCount == 1)))
|
||||
emplaceType<BoundType>(asMutable(freeTy), builtinTypes->unknownType);
|
||||
else
|
||||
{
|
||||
|
@ -1308,7 +1306,7 @@ GeneralizationResult<TypeId> generalizeType(
|
|||
|
||||
if (follow(lb) != freeTy)
|
||||
emplaceType<BoundType>(asMutable(freeTy), lb);
|
||||
else if (!isWithinFunction || (!FFlag::LuauEagerGeneralization && params.useCount == 1))
|
||||
else if (!isWithinFunction || (!FFlag::LuauEagerGeneralization2 && params.useCount == 1))
|
||||
emplaceType<BoundType>(asMutable(freeTy), builtinTypes->unknownType);
|
||||
else
|
||||
{
|
||||
|
@ -1425,7 +1423,7 @@ std::optional<TypeId> generalize(
|
|||
FreeTypeSearcher fts{scope, cachedTypes};
|
||||
fts.traverse(ty);
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
FunctionType* functionTy = getMutable<FunctionType>(ty);
|
||||
auto pushGeneric = [&](TypeId t)
|
||||
|
@ -1586,7 +1584,6 @@ struct GenericCounter : TypeVisitor
|
|||
traverse(*prop.writeTy);
|
||||
polarity = p;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1653,7 +1650,7 @@ void pruneUnnecessaryGenerics(
|
|||
TypeId ty
|
||||
)
|
||||
{
|
||||
if (!FFlag::LuauEagerGeneralization)
|
||||
if (!FFlag::LuauEagerGeneralization2)
|
||||
return;
|
||||
|
||||
ty = follow(ty);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include "Luau/Scope.h"
|
||||
#include "Luau/VisitType.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -133,7 +133,7 @@ struct InferPolarity : TypeVisitor
|
|||
template<typename TID>
|
||||
static void inferGenericPolarities_(NotNull<TypeArena> arena, NotNull<Scope> scope, TID ty)
|
||||
{
|
||||
if (!FFlag::LuauEagerGeneralization)
|
||||
if (!FFlag::LuauEagerGeneralization2)
|
||||
return;
|
||||
|
||||
InferPolarity infer{arena, scope};
|
||||
|
|
|
@ -2294,36 +2294,36 @@ private:
|
|||
|
||||
bool visit(AstExprLocal* node) override
|
||||
{
|
||||
const FunctionType* fty = getFunctionType(node);
|
||||
bool shouldReport = fty && fty->isDeprecatedFunction && !inScope(fty);
|
||||
const FunctionType* fty = getFunctionType(node);
|
||||
bool shouldReport = fty && fty->isDeprecatedFunction && !inScope(fty);
|
||||
|
||||
if (shouldReport)
|
||||
report(node->location, node->local->name.value);
|
||||
if (shouldReport)
|
||||
report(node->location, node->local->name.value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool visit(AstExprGlobal* node) override
|
||||
{
|
||||
const FunctionType* fty = getFunctionType(node);
|
||||
bool shouldReport = fty && fty->isDeprecatedFunction && !inScope(fty);
|
||||
const FunctionType* fty = getFunctionType(node);
|
||||
bool shouldReport = fty && fty->isDeprecatedFunction && !inScope(fty);
|
||||
|
||||
if (shouldReport)
|
||||
report(node->location, node->name.value);
|
||||
if (shouldReport)
|
||||
report(node->location, node->name.value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool visit(AstStatLocalFunction* node) override
|
||||
{
|
||||
check(node->func);
|
||||
return false;
|
||||
check(node->func);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool visit(AstStatFunction* node) override
|
||||
{
|
||||
check(node->func);
|
||||
return false;
|
||||
check(node->func);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool visit(AstExprIndexName* node) override
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#include <algorithm>
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2);
|
||||
LUAU_FASTFLAG(LuauRetainDefinitionAliasLocations)
|
||||
LUAU_FASTFLAG(LuauUserTypeFunctionAliases)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -265,10 +265,7 @@ struct ClonePublicInterface : Substitution
|
|||
|
||||
TypeId type = cloneType(tf.type);
|
||||
|
||||
if (FFlag::LuauRetainDefinitionAliasLocations)
|
||||
return TypeFun{typeParams, typePackParams, type, tf.definitionLocation};
|
||||
else
|
||||
return TypeFun{typeParams, typePackParams, type};
|
||||
return TypeFun{typeParams, typePackParams, type, tf.definitionLocation};
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -309,6 +306,14 @@ void Module::clonePublicInterface(NotNull<BuiltinTypes> builtinTypes, InternalEr
|
|||
ty = clonePublicInterface.cloneType(ty);
|
||||
}
|
||||
|
||||
if (FFlag::LuauUserTypeFunctionAliases)
|
||||
{
|
||||
for (auto& tf : typeFunctionAliases)
|
||||
{
|
||||
*tf = clonePublicInterface.cloneTypeFun(*tf);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy external stuff over to Module itself
|
||||
this->returnType = moduleScope->returnType;
|
||||
this->exportedTypeBindings = moduleScope->exportedTypeBindings;
|
||||
|
|
|
@ -24,6 +24,7 @@ LUAU_FASTFLAG(DebugLuauMagicTypes)
|
|||
|
||||
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictVisitTypes2)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictFixGenericTypePacks)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -1091,23 +1092,42 @@ struct NonStrictTypeChecker
|
|||
Scope* scope = findInnermostScope(tp->location);
|
||||
LUAU_ASSERT(scope);
|
||||
|
||||
std::optional<TypePackId> alias = scope->lookupPack(tp->genericName.value);
|
||||
if (!alias.has_value())
|
||||
if (FFlag::LuauNewNonStrictFixGenericTypePacks)
|
||||
{
|
||||
if (std::optional<TypePackId> alias = scope->lookupPack(tp->genericName.value))
|
||||
return;
|
||||
|
||||
if (scope->lookupType(tp->genericName.value))
|
||||
{
|
||||
reportError(
|
||||
return reportError(
|
||||
SwappedGenericTypeParameter{
|
||||
tp->genericName.value,
|
||||
SwappedGenericTypeParameter::Kind::Pack,
|
||||
},
|
||||
tp->location
|
||||
);
|
||||
}
|
||||
|
||||
reportError(UnknownSymbol{tp->genericName.value, UnknownSymbol::Context::Type}, tp->location);
|
||||
}
|
||||
else
|
||||
{
|
||||
reportError(UnknownSymbol{tp->genericName.value, UnknownSymbol::Context::Type}, tp->location);
|
||||
std::optional<TypePackId> alias = scope->lookupPack(tp->genericName.value);
|
||||
if (!alias.has_value())
|
||||
{
|
||||
if (scope->lookupType(tp->genericName.value))
|
||||
{
|
||||
reportError(
|
||||
SwappedGenericTypeParameter{
|
||||
tp->genericName.value,
|
||||
SwappedGenericTypeParameter::Kind::Pack,
|
||||
},
|
||||
tp->location
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reportError(UnknownSymbol{tp->genericName.value, UnknownSymbol::Context::Type}, tp->location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,150 +33,6 @@ static bool shouldEarlyExit(NormalizationResult res)
|
|||
return false;
|
||||
}
|
||||
|
||||
TypeIds::TypeIds(std::initializer_list<TypeId> tys)
|
||||
{
|
||||
for (TypeId ty : tys)
|
||||
insert(ty);
|
||||
}
|
||||
|
||||
void TypeIds::insert(TypeId ty)
|
||||
{
|
||||
ty = follow(ty);
|
||||
|
||||
// get a reference to the slot for `ty` in `types`
|
||||
bool& entry = types[ty];
|
||||
|
||||
// if `ty` is fresh, we can set it to `true`, add it to the order and hash and be done.
|
||||
if (!entry)
|
||||
{
|
||||
entry = true;
|
||||
order.push_back(ty);
|
||||
hash ^= std::hash<TypeId>{}(ty);
|
||||
}
|
||||
}
|
||||
|
||||
void TypeIds::clear()
|
||||
{
|
||||
order.clear();
|
||||
types.clear();
|
||||
hash = 0;
|
||||
}
|
||||
|
||||
TypeId TypeIds::front() const
|
||||
{
|
||||
return order.at(0);
|
||||
}
|
||||
|
||||
TypeIds::iterator TypeIds::begin()
|
||||
{
|
||||
return order.begin();
|
||||
}
|
||||
|
||||
TypeIds::iterator TypeIds::end()
|
||||
{
|
||||
return order.end();
|
||||
}
|
||||
|
||||
TypeIds::const_iterator TypeIds::begin() const
|
||||
{
|
||||
return order.begin();
|
||||
}
|
||||
|
||||
TypeIds::const_iterator TypeIds::end() const
|
||||
{
|
||||
return order.end();
|
||||
}
|
||||
|
||||
TypeIds::iterator TypeIds::erase(TypeIds::const_iterator it)
|
||||
{
|
||||
TypeId ty = *it;
|
||||
types[ty] = false;
|
||||
hash ^= std::hash<TypeId>{}(ty);
|
||||
return order.erase(it);
|
||||
}
|
||||
|
||||
void TypeIds::erase(TypeId ty)
|
||||
{
|
||||
const_iterator it = std::find(order.begin(), order.end(), ty);
|
||||
if (it == order.end())
|
||||
return;
|
||||
|
||||
erase(it);
|
||||
}
|
||||
|
||||
size_t TypeIds::size() const
|
||||
{
|
||||
return order.size();
|
||||
}
|
||||
|
||||
bool TypeIds::empty() const
|
||||
{
|
||||
return order.empty();
|
||||
}
|
||||
|
||||
size_t TypeIds::count(TypeId ty) const
|
||||
{
|
||||
ty = follow(ty);
|
||||
const bool* val = types.find(ty);
|
||||
return (val && *val) ? 1 : 0;
|
||||
}
|
||||
|
||||
void TypeIds::retain(const TypeIds& there)
|
||||
{
|
||||
for (auto it = begin(); it != end();)
|
||||
{
|
||||
if (there.count(*it))
|
||||
it++;
|
||||
else
|
||||
it = erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
size_t TypeIds::getHash() const
|
||||
{
|
||||
return hash;
|
||||
}
|
||||
|
||||
bool TypeIds::isNever() const
|
||||
{
|
||||
return std::all_of(
|
||||
begin(),
|
||||
end(),
|
||||
[&](TypeId i)
|
||||
{
|
||||
// If each typeid is never, then I guess typeid's is also never?
|
||||
return get<NeverType>(i) != nullptr;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
bool TypeIds::operator==(const TypeIds& there) const
|
||||
{
|
||||
// we can early return if the hashes don't match.
|
||||
if (hash != there.hash)
|
||||
return false;
|
||||
|
||||
// we have to check equality of the sets themselves if not.
|
||||
|
||||
// if the sets are unequal sizes, then they cannot possibly be equal.
|
||||
// it is important to use `order` here and not `types` since the mappings
|
||||
// may have different sizes since removal is not possible, and so erase
|
||||
// simply writes `false` into the map.
|
||||
if (order.size() != there.order.size())
|
||||
return false;
|
||||
|
||||
// otherwise, we'll need to check that every element we have here is in `there`.
|
||||
for (auto ty : order)
|
||||
{
|
||||
// if it's not, we'll return `false`
|
||||
if (there.count(ty) == 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
// otherwise, we've proven the two equal!
|
||||
return true;
|
||||
}
|
||||
|
||||
NormalizedStringType::NormalizedStringType() {}
|
||||
|
||||
NormalizedStringType::NormalizedStringType(bool isCofinite, std::map<std::string, TypeId> singletons)
|
||||
|
|
|
@ -255,6 +255,29 @@ bool Scope::shouldWarnGlobal(std::string name) const
|
|||
return false;
|
||||
}
|
||||
|
||||
NotNull<Scope> Scope::findNarrowestScopeContaining(Location location)
|
||||
{
|
||||
Scope* bestScope = this;
|
||||
|
||||
bool didNarrow;
|
||||
do
|
||||
{
|
||||
didNarrow = false;
|
||||
for (auto scope : bestScope->children)
|
||||
{
|
||||
if (scope->location.encloses(location))
|
||||
{
|
||||
bestScope = scope.get();
|
||||
didNarrow = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (didNarrow && bestScope->children.size() > 0);
|
||||
|
||||
return NotNull{bestScope};
|
||||
}
|
||||
|
||||
|
||||
bool subsumesStrict(Scope* left, Scope* right)
|
||||
{
|
||||
while (right)
|
||||
|
|
|
@ -21,7 +21,7 @@ LUAU_FASTFLAGVARIABLE(DebugLuauSubtypingCheckPathValidity)
|
|||
LUAU_FASTINTVARIABLE(LuauSubtypingReasoningLimit, 100)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSubtypeGenericsAndNegations)
|
||||
LUAU_FASTFLAG(LuauClipVariadicAnysFromArgsToGenericFuncs2)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -675,20 +675,21 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
|||
result.isSubtype = ok;
|
||||
result.isCacheable = false;
|
||||
}
|
||||
else if (auto superGeneric = get<GenericType>(superTy); FFlag::LuauSubtypeGenericsAndNegations && superGeneric && variance == Variance::Contravariant)
|
||||
else if (auto superGeneric = get<GenericType>(superTy);
|
||||
FFlag::LuauSubtypeGenericsAndNegations && superGeneric && variance == Variance::Contravariant)
|
||||
{
|
||||
bool ok = bindGeneric(env, subTy, superTy);
|
||||
result.isSubtype = ok;
|
||||
result.isCacheable = false;
|
||||
}
|
||||
else if (auto pair = get2<FreeType, FreeType>(subTy, superTy); FFlag::LuauEagerGeneralization && pair)
|
||||
else if (auto pair = get2<FreeType, FreeType>(subTy, superTy); FFlag::LuauEagerGeneralization2 && pair)
|
||||
{
|
||||
// Any two free types are potentially subtypes of one another because
|
||||
// both of them could be narrowed to never.
|
||||
result = {true};
|
||||
result.assumedConstraints.emplace_back(SubtypeConstraint{subTy, superTy});
|
||||
}
|
||||
else if (auto superFree = get<FreeType>(superTy); superFree && FFlag::LuauEagerGeneralization)
|
||||
else if (auto superFree = get<FreeType>(superTy); superFree && FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
// Given SubTy <: (LB <: SuperTy <: UB)
|
||||
//
|
||||
|
@ -703,7 +704,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
|||
if (result.isSubtype)
|
||||
result.assumedConstraints.emplace_back(SubtypeConstraint{subTy, superTy});
|
||||
}
|
||||
else if (auto subFree = get<FreeType>(subTy); subFree && FFlag::LuauEagerGeneralization)
|
||||
else if (auto subFree = get<FreeType>(subTy); subFree && FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
// Given (LB <: SubTy <: UB) <: SuperTy
|
||||
//
|
||||
|
@ -767,7 +768,8 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
|||
result.isSubtype = ok;
|
||||
result.isCacheable = false;
|
||||
}
|
||||
else if (auto superGeneric = get<GenericType>(superTy); !FFlag::LuauSubtypeGenericsAndNegations && superGeneric && variance == Variance::Contravariant)
|
||||
else if (auto superGeneric = get<GenericType>(superTy);
|
||||
!FFlag::LuauSubtypeGenericsAndNegations && superGeneric && variance == Variance::Contravariant)
|
||||
{
|
||||
bool ok = bindGeneric(env, subTy, superTy);
|
||||
result.isSubtype = ok;
|
||||
|
@ -1450,7 +1452,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Tabl
|
|||
{
|
||||
SubtypingResult result{true};
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
if (subTable->props.empty() && !subTable->indexer && subTable->state == TableState::Sealed && superTable->indexer)
|
||||
{
|
||||
|
@ -1506,7 +1508,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Tabl
|
|||
{
|
||||
if (subTable->indexer)
|
||||
result.andAlso(isInvariantWith(env, *subTable->indexer, *superTable->indexer, scope));
|
||||
else if (FFlag::LuauEagerGeneralization && subTable->state != TableState::Sealed)
|
||||
else if (FFlag::LuauEagerGeneralization2 && subTable->state != TableState::Sealed)
|
||||
{
|
||||
// As above, we assume that {| |} <: {T} because the unsealed table
|
||||
// on the left will eventually gain the necessary indexer.
|
||||
|
@ -1548,7 +1550,12 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Meta
|
|||
}
|
||||
}
|
||||
|
||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const ExternType* subExternType, const ExternType* superExternType, NotNull<Scope> scope)
|
||||
SubtypingResult Subtyping::isCovariantWith(
|
||||
SubtypingEnvironment& env,
|
||||
const ExternType* subExternType,
|
||||
const ExternType* superExternType,
|
||||
NotNull<Scope> scope
|
||||
)
|
||||
{
|
||||
return {isSubclass(subExternType, superExternType)};
|
||||
}
|
||||
|
@ -1737,9 +1744,8 @@ SubtypingResult Subtyping::isCovariantWith(
|
|||
|
||||
SubtypingResult result = isCovariantWith(env, subNorm->tops, superNorm->tops, scope);
|
||||
result.andAlso(isCovariantWith(env, subNorm->booleans, superNorm->booleans, scope));
|
||||
result.andAlso(
|
||||
isCovariantWith(env, subNorm->externTypes, superNorm->externTypes, scope).orElse(isCovariantWith(env, subNorm->externTypes, superNorm->tables, scope))
|
||||
);
|
||||
result.andAlso(isCovariantWith(env, subNorm->externTypes, superNorm->externTypes, scope)
|
||||
.orElse(isCovariantWith(env, subNorm->externTypes, 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));
|
||||
|
|
|
@ -19,16 +19,6 @@ LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
|
|||
namespace Luau
|
||||
{
|
||||
|
||||
static bool isRecord(const AstExprTable::Item& item)
|
||||
{
|
||||
if (item.kind == AstExprTable::Item::Record)
|
||||
return true;
|
||||
else if (item.kind == AstExprTable::Item::General && item.key->is<AstExprConstantString>())
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
TypeId matchLiteralType(
|
||||
NotNull<DenseHashMap<const AstExpr*, TypeId>> astTypes,
|
||||
NotNull<DenseHashMap<const AstExpr*, TypeId>> astExpectedTypes,
|
||||
|
|
|
@ -1381,7 +1381,11 @@ struct Printer
|
|||
LUAU_ASSERT(!forVarArg);
|
||||
if (const auto cstNode = lookupCstNode<CstTypePackExplicit>(explicitTp))
|
||||
visualizeTypeList(
|
||||
explicitTp->typeList, FFlag::LuauStoreReturnTypesAsPackOnAst ? cstNode->hasParentheses : true, cstNode->openParenthesesPosition, cstNode->closeParenthesesPosition, cstNode->commaPositions
|
||||
explicitTp->typeList,
|
||||
FFlag::LuauStoreReturnTypesAsPackOnAst ? cstNode->hasParentheses : true,
|
||||
cstNode->openParenthesesPosition,
|
||||
cstNode->closeParenthesesPosition,
|
||||
cstNode->commaPositions
|
||||
);
|
||||
else
|
||||
visualizeTypeList(explicitTp->typeList, unconditionallyParenthesize);
|
||||
|
@ -1986,7 +1990,7 @@ struct Printer
|
|||
if (FFlag::LuauStoreLocalAnnotationColonPositions)
|
||||
visualize(*a->var, cstNode ? cstNode->annotationColonPosition : Position{0, 0});
|
||||
else
|
||||
visualize(*a->var, Position{0,0});
|
||||
visualize(*a->var, Position{0, 0});
|
||||
|
||||
if (cstNode)
|
||||
advance(cstNode->equalsPosition);
|
||||
|
|
|
@ -1079,7 +1079,7 @@ void persist(TypeId ty)
|
|||
queue.push_back(ttv->indexer->indexResultType);
|
||||
}
|
||||
}
|
||||
else if (auto etv= get<ExternType>(t))
|
||||
else if (auto etv = get<ExternType>(t))
|
||||
{
|
||||
for (const auto& [_name, prop] : etv->props)
|
||||
queue.push_back(prop.type());
|
||||
|
|
|
@ -34,6 +34,7 @@ LUAU_FASTFLAGVARIABLE(LuauTypeCheckerAcceptNumberConcats)
|
|||
LUAU_FASTFLAGVARIABLE(LuauTypeCheckerStricterIndexCheck)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
|
||||
LUAU_FASTFLAG(LuauNewNonStrictFixGenericTypePacks)
|
||||
LUAU_FASTFLAGVARIABLE(LuauReportSubtypingErrors)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSkipMalformedTypeAliases)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTableLiteralSubtypeSpecificCheck)
|
||||
|
@ -782,7 +783,6 @@ void TypeChecker2::visit(AstStatReturn* ret)
|
|||
|
||||
for (AstExpr* expr : ret->list)
|
||||
visit(expr, ValueContext::RValue);
|
||||
|
||||
}
|
||||
|
||||
void TypeChecker2::visit(AstStatExpr* expr)
|
||||
|
@ -2796,22 +2796,41 @@ void TypeChecker2::visit(AstTypePackGeneric* tp)
|
|||
Scope* scope = findInnermostScope(tp->location);
|
||||
LUAU_ASSERT(scope);
|
||||
|
||||
std::optional<TypePackId> alias = scope->lookupPack(tp->genericName.value);
|
||||
if (!alias.has_value())
|
||||
if (FFlag::LuauNewNonStrictFixGenericTypePacks)
|
||||
{
|
||||
if (std::optional<TypePackId> alias = scope->lookupPack(tp->genericName.value))
|
||||
return;
|
||||
|
||||
if (scope->lookupType(tp->genericName.value))
|
||||
{
|
||||
reportError(
|
||||
return reportError(
|
||||
SwappedGenericTypeParameter{
|
||||
tp->genericName.value,
|
||||
SwappedGenericTypeParameter::Kind::Pack,
|
||||
},
|
||||
tp->location
|
||||
);
|
||||
}
|
||||
else
|
||||
|
||||
reportError(UnknownSymbol{tp->genericName.value, UnknownSymbol::Context::Type}, tp->location);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::optional<TypePackId> alias = scope->lookupPack(tp->genericName.value);
|
||||
if (!alias.has_value())
|
||||
{
|
||||
reportError(UnknownSymbol{tp->genericName.value, UnknownSymbol::Context::Type}, tp->location);
|
||||
if (scope->lookupType(tp->genericName.value))
|
||||
{
|
||||
reportError(
|
||||
SwappedGenericTypeParameter{
|
||||
tp->genericName.value,
|
||||
SwappedGenericTypeParameter::Kind::Pack,
|
||||
},
|
||||
tp->location
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
reportError(UnknownSymbol{tp->genericName.value, UnknownSymbol::Context::Type}, tp->location);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2944,14 +2963,6 @@ void TypeChecker2::explainError(TypePackId subTy, TypePackId superTy, Location l
|
|||
reportError(TypePackMismatch{superTy, subTy, reasonings.toString()}, location);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
bool isRecord(const AstExprTable::Item& item)
|
||||
{
|
||||
return item.kind == AstExprTable::Item::Record || (item.kind == AstExprTable::Item::General && item.key->is<AstExprConstantString>());
|
||||
}
|
||||
}
|
||||
|
||||
bool TypeChecker2::testPotentialLiteralIsSubtype(AstExpr* expr, TypeId expectedType)
|
||||
{
|
||||
auto exprType = follow(lookupType(expr));
|
||||
|
@ -2982,7 +2993,7 @@ bool TypeChecker2::testPotentialLiteralIsSubtype(AstExpr* expr, TypeId expectedT
|
|||
return testIsSubtype(exprType, expectedType, expr->location);
|
||||
}
|
||||
|
||||
Set<std::optional<std::string> > missingKeys{{}};
|
||||
Set<std::optional<std::string>> missingKeys{{}};
|
||||
for (const auto& [name, prop] : expectedTableType->props)
|
||||
{
|
||||
if (FFlag::LuauEnableWriteOnlyProperties)
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "Luau/Unifier2.h"
|
||||
#include "Luau/VecDeque.h"
|
||||
#include "Luau/VisitType.h"
|
||||
#include "Luau/ApplyTypeFunction.h"
|
||||
|
||||
#include "lua.h"
|
||||
#include "lualib.h"
|
||||
|
@ -47,8 +48,8 @@ LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyApplicationCartesianProductLimit, 5'0
|
|||
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyUseGuesserDepth, -1);
|
||||
|
||||
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies)
|
||||
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
||||
|
@ -56,6 +57,7 @@ LUAU_FASTFLAGVARIABLE(LuauNarrowIntersectionNevers)
|
|||
LUAU_FASTFLAGVARIABLE(LuauRefineWaitForBlockedTypesInTarget)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNoMoreInjectiveTypeFunctions)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNotAllBinaryTypeFunsHaveDefaults)
|
||||
LUAU_FASTFLAG(LuauUserTypeFunctionAliases)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -283,7 +285,7 @@ struct TypeFunctionReducer
|
|||
}
|
||||
else if (is<GenericType>(ty))
|
||||
{
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
return SkipTestResult::Generic;
|
||||
else
|
||||
return SkipTestResult::Irreducible;
|
||||
|
@ -305,7 +307,7 @@ struct TypeFunctionReducer
|
|||
}
|
||||
else if (is<GenericTypePack>(ty))
|
||||
{
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
return SkipTestResult::Generic;
|
||||
else
|
||||
return SkipTestResult::Irreducible;
|
||||
|
@ -569,6 +571,27 @@ struct LuauTempThreadPopper
|
|||
lua_State* L = nullptr;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class ScopedAssign
|
||||
{
|
||||
public:
|
||||
ScopedAssign(T& target, const T& value)
|
||||
: target(&target)
|
||||
, oldValue(target)
|
||||
{
|
||||
target = value;
|
||||
}
|
||||
|
||||
~ScopedAssign()
|
||||
{
|
||||
*target = oldValue;
|
||||
}
|
||||
|
||||
private:
|
||||
T* target = nullptr;
|
||||
T oldValue;
|
||||
};
|
||||
|
||||
static FunctionGraphReductionResult reduceFunctionsInternal(
|
||||
VecDeque<TypeId> queuedTys,
|
||||
VecDeque<TypePackId> queuedTps,
|
||||
|
@ -789,6 +812,97 @@ struct FindUserTypeFunctionBlockers : TypeOnceVisitor
|
|||
}
|
||||
};
|
||||
|
||||
static int evaluateTypeAliasCall(lua_State* L)
|
||||
{
|
||||
TypeFun* tf = (TypeFun*)lua_tolightuserdata(L, lua_upvalueindex(1));
|
||||
|
||||
TypeFunctionRuntime* runtime = getTypeFunctionRuntime(L);
|
||||
TypeFunctionRuntimeBuilderState* runtimeBuilder = runtime->runtimeBuilder;
|
||||
|
||||
ApplyTypeFunction applyTypeFunction{runtimeBuilder->ctx->arena};
|
||||
|
||||
int argumentCount = lua_gettop(L);
|
||||
std::vector<TypeId> rawTypeArguments;
|
||||
|
||||
for (int i = 0; i < argumentCount; i++)
|
||||
{
|
||||
TypeFunctionTypeId tfty = getTypeUserData(L, i + 1);
|
||||
TypeId ty = deserialize(tfty, runtimeBuilder);
|
||||
|
||||
if (!runtimeBuilder->errors.empty())
|
||||
luaL_error(L, "failed to deserialize type at argument %d", i + 1);
|
||||
|
||||
rawTypeArguments.push_back(ty);
|
||||
}
|
||||
|
||||
// Check if we have enough arguments, by typical typechecking rules
|
||||
size_t typesRequired = tf->typeParams.size();
|
||||
size_t packsRequired = tf->typePackParams.size();
|
||||
|
||||
size_t typesProvided = rawTypeArguments.size() > typesRequired ? typesRequired : rawTypeArguments.size();
|
||||
size_t extraTypes = rawTypeArguments.size() > typesRequired ? rawTypeArguments.size() - typesRequired : 0;
|
||||
size_t packsProvided = 0;
|
||||
|
||||
if (extraTypes != 0 && packsProvided == 0)
|
||||
{
|
||||
// Extra types are only collected into a pack if a pack is expected
|
||||
if (packsRequired != 0)
|
||||
packsProvided += 1;
|
||||
else
|
||||
typesProvided += extraTypes;
|
||||
}
|
||||
|
||||
for (size_t i = typesProvided; i < typesRequired; ++i)
|
||||
{
|
||||
if (tf->typeParams[i].defaultValue)
|
||||
typesProvided += 1;
|
||||
}
|
||||
|
||||
for (size_t i = packsProvided; i < packsRequired; ++i)
|
||||
{
|
||||
if (tf->typePackParams[i].defaultValue)
|
||||
packsProvided += 1;
|
||||
}
|
||||
|
||||
if (extraTypes == 0 && packsProvided + 1 == packsRequired)
|
||||
packsProvided += 1;
|
||||
|
||||
if (typesProvided != typesRequired || packsProvided != packsRequired)
|
||||
luaL_error(L, "not enough arguments to call");
|
||||
|
||||
// Prepare final types and packs
|
||||
auto [types, packs] = saturateArguments(runtimeBuilder->ctx->arena, runtimeBuilder->ctx->builtins, *tf, rawTypeArguments, {});
|
||||
|
||||
for (size_t i = 0; i < types.size(); ++i)
|
||||
applyTypeFunction.typeArguments[tf->typeParams[i].ty] = types[i];
|
||||
|
||||
for (size_t i = 0; i < packs.size(); ++i)
|
||||
applyTypeFunction.typePackArguments[tf->typePackParams[i].tp] = packs[i];
|
||||
|
||||
std::optional<TypeId> maybeInstantiated = applyTypeFunction.substitute(tf->type);
|
||||
|
||||
if (!maybeInstantiated.has_value())
|
||||
{
|
||||
luaL_error(L, "failed to instantiate type alias");
|
||||
return true;
|
||||
}
|
||||
|
||||
TypeId target = follow(*maybeInstantiated);
|
||||
|
||||
FunctionGraphReductionResult result = reduceTypeFunctions(target, Location{}, *runtimeBuilder->ctx);
|
||||
|
||||
if (!result.errors.empty())
|
||||
luaL_error(L, "failed to reduce type function with: %s", toString(result.errors.front()).c_str());
|
||||
|
||||
TypeFunctionTypeId serializedTy = serialize(follow(target), runtimeBuilder);
|
||||
|
||||
if (!runtimeBuilder->errors.empty())
|
||||
luaL_error(L, "%s", runtimeBuilder->errors.front().c_str());
|
||||
|
||||
allocTypeUserData(L, serializedTy->type);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
|
||||
TypeId instance,
|
||||
const std::vector<TypeId>& typeParams,
|
||||
|
@ -819,11 +933,21 @@ TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
|
|||
for (auto typeParam : typeParams)
|
||||
check.traverse(follow(typeParam));
|
||||
|
||||
if (FFlag::LuauUserTypeFunctionAliases)
|
||||
{
|
||||
// Check that our environment doesn't depend on any type aliases that are blocked
|
||||
for (auto& [name, definition] : typeFunction->userFuncData.environmentAlias)
|
||||
{
|
||||
if (definition.first->typeParams.empty() && definition.first->typePackParams.empty())
|
||||
check.traverse(follow(definition.first->type));
|
||||
}
|
||||
}
|
||||
|
||||
if (!check.blockingTypes.empty())
|
||||
return {std::nullopt, Reduction::MaybeOk, check.blockingTypes, {}};
|
||||
|
||||
// Ensure that whole type function environment is registered
|
||||
for (auto& [name, definition] : typeFunction->userFuncData.environment)
|
||||
for (auto& [name, definition] : typeFunction->userFuncData.environmentFunction)
|
||||
{
|
||||
// Cannot evaluate if a potential dependency couldn't be parsed
|
||||
if (definition.first->hasErrors)
|
||||
|
@ -849,8 +973,13 @@ TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
|
|||
lua_State* L = lua_newthread(global);
|
||||
LuauTempThreadPopper popper(global);
|
||||
|
||||
std::unique_ptr<TypeFunctionRuntimeBuilderState> runtimeBuilder = std::make_unique<TypeFunctionRuntimeBuilderState>(ctx);
|
||||
|
||||
ScopedAssign setRuntimeBuilder(ctx->typeFunctionRuntime->runtimeBuilder, runtimeBuilder.get());
|
||||
ScopedAssign enableReduction(ctx->normalizer->sharedState->reentrantTypeReduction, false);
|
||||
|
||||
// Build up the environment table of each function we have visible
|
||||
for (auto& [_, curr] : typeFunction->userFuncData.environment)
|
||||
for (auto& [_, curr] : typeFunction->userFuncData.environmentFunction)
|
||||
{
|
||||
// Environment table has to be filled only once in the current execution context
|
||||
if (ctx->typeFunctionRuntime->initialized.find(curr.first))
|
||||
|
@ -870,7 +999,7 @@ TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
|
|||
lua_getfenv(L, -1);
|
||||
lua_setreadonly(L, -1, false);
|
||||
|
||||
for (auto& [name, definition] : typeFunction->userFuncData.environment)
|
||||
for (auto& [name, definition] : typeFunction->userFuncData.environmentFunction)
|
||||
{
|
||||
// Filter visibility based on original scope depth
|
||||
if (definition.second >= curr.second)
|
||||
|
@ -885,6 +1014,39 @@ TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
|
|||
}
|
||||
}
|
||||
|
||||
if (FFlag::LuauUserTypeFunctionAliases)
|
||||
{
|
||||
for (auto& [name, definition] : typeFunction->userFuncData.environmentAlias)
|
||||
{
|
||||
// Filter visibility based on original scope depth
|
||||
if (definition.second >= curr.second)
|
||||
{
|
||||
if (definition.first->typeParams.empty() && definition.first->typePackParams.empty())
|
||||
{
|
||||
TypeId ty = follow(definition.first->type);
|
||||
|
||||
// This is checked at the top of the function, and should still be true.
|
||||
LUAU_ASSERT(!isPending(ty, ctx->solver));
|
||||
|
||||
TypeFunctionTypeId serializedTy = serialize(ty, runtimeBuilder.get());
|
||||
|
||||
// Only register aliases that are representable in type environment
|
||||
if (runtimeBuilder->errors.empty())
|
||||
{
|
||||
allocTypeUserData(L, serializedTy->type);
|
||||
lua_setfield(L, -2, name.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lua_pushlightuserdata(L, definition.first);
|
||||
lua_pushcclosure(L, evaluateTypeAliasCall, name.c_str(), 1);
|
||||
lua_setfield(L, -2, name.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lua_setreadonly(L, -1, true);
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
|
@ -901,8 +1063,6 @@ TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
|
|||
|
||||
resetTypeFunctionState(L);
|
||||
|
||||
std::unique_ptr<TypeFunctionRuntimeBuilderState> runtimeBuilder = std::make_unique<TypeFunctionRuntimeBuilderState>(ctx);
|
||||
|
||||
// Push serialized arguments onto the stack
|
||||
for (auto typeParam : typeParams)
|
||||
{
|
||||
|
@ -1101,7 +1261,7 @@ TypeFunctionReductionResult<TypeId> unmTypeFunction(
|
|||
if (isPending(operandTy, ctx->solver))
|
||||
return {std::nullopt, Reduction::MaybeOk, {operandTy}, {}};
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
operandTy = follow(operandTy);
|
||||
|
||||
std::shared_ptr<const NormalizedType> normTy = ctx->normalizer->normalize(operandTy);
|
||||
|
@ -1698,7 +1858,7 @@ TypeFunctionReductionResult<TypeId> orTypeFunction(
|
|||
return {rhsTy, Reduction::MaybeOk, {}, {}};
|
||||
|
||||
// check to see if both operand types are resolved enough, and wait to reduce if not
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(lhsTy))
|
||||
return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}};
|
||||
|
@ -1745,7 +1905,7 @@ static TypeFunctionReductionResult<TypeId> comparisonTypeFunction(
|
|||
if (lhsTy == instance || rhsTy == instance)
|
||||
return {ctx->builtins->neverType, Reduction::MaybeOk, {}, {}};
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(lhsTy))
|
||||
return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}};
|
||||
|
@ -2099,7 +2259,7 @@ bool isSimpleDiscriminant(TypeId ty)
|
|||
return isApproximateTruthy(ty) || isApproximateFalsy(ty);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
||||
TypeId instance,
|
||||
|
@ -2119,9 +2279,8 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
|||
for (size_t i = 1; i < typeParams.size(); i++)
|
||||
discriminantTypes.push_back(follow(typeParams.at(i)));
|
||||
|
||||
const bool targetIsPending = FFlag::LuauEagerGeneralization
|
||||
? is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(targetTy)
|
||||
: isPending(targetTy, ctx->solver);
|
||||
const bool targetIsPending = FFlag::LuauEagerGeneralization2 ? is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(targetTy)
|
||||
: isPending(targetTy, ctx->solver);
|
||||
|
||||
// check to see if both operand types are resolved enough, and wait to reduce if not
|
||||
if (targetIsPending)
|
||||
|
@ -2206,7 +2365,7 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
|||
if (is<TableType>(target) || isSimpleDiscriminant(discriminant))
|
||||
{
|
||||
SimplifyResult result = simplifyIntersection(ctx->builtins, ctx->arena, target, discriminant);
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
// Simplification considers free and generic types to be
|
||||
// 'blocking', but that's not suitable for refine<>.
|
||||
|
@ -2615,8 +2774,8 @@ TypeFunctionReductionResult<TypeId> keyofFunctionImpl(
|
|||
if (!normTy)
|
||||
return {std::nullopt, Reduction::MaybeOk, {}, {}};
|
||||
|
||||
// if we don't have either just tables or just extern types, we've got nothing to get keys of (at least until a future version perhaps adds extern types
|
||||
// as well)
|
||||
// if we don't have either just tables or just extern types, we've got nothing to get keys of (at least until a future version perhaps adds extern
|
||||
// types as well)
|
||||
if (normTy->hasTables() == normTy->hasExternTypes())
|
||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||
|
||||
|
@ -2985,7 +3144,8 @@ TypeFunctionReductionResult<TypeId> indexFunctionImpl(
|
|||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||
|
||||
// at least one class is guaranteed to be in the iterator by .hasExternTypes()
|
||||
for (auto externTypeIter = indexeeNormTy->externTypes.ordering.begin(); externTypeIter != indexeeNormTy->externTypes.ordering.end(); ++externTypeIter)
|
||||
for (auto externTypeIter = indexeeNormTy->externTypes.ordering.begin(); externTypeIter != indexeeNormTy->externTypes.ordering.end();
|
||||
++externTypeIter)
|
||||
{
|
||||
auto externTy = get<ExternType>(*externTypeIter);
|
||||
if (!externTy)
|
||||
|
@ -3335,7 +3495,7 @@ BuiltinTypeFunctions::BuiltinTypeFunctions()
|
|||
, ltFunc{"lt", ltTypeFunction}
|
||||
, leFunc{"le", leTypeFunction}
|
||||
, eqFunc{"eq", eqTypeFunction}
|
||||
, refineFunc{"refine", refineTypeFunction, /*canReduceGenerics*/ FFlag::LuauEagerGeneralization}
|
||||
, refineFunc{"refine", refineTypeFunction, /*canReduceGenerics*/ FFlag::LuauEagerGeneralization2}
|
||||
, singletonFunc{"singleton", singletonTypeFunction}
|
||||
, unionFunc{"union", unionTypeFunction}
|
||||
, intersectFunc{"intersect", intersectTypeFunction}
|
||||
|
|
|
@ -63,7 +63,7 @@ std::optional<std::string> checkResultForError(lua_State* L, const char* typeFun
|
|||
}
|
||||
}
|
||||
|
||||
static TypeFunctionRuntime* getTypeFunctionRuntime(lua_State* L)
|
||||
TypeFunctionRuntime* getTypeFunctionRuntime(lua_State* L)
|
||||
{
|
||||
return static_cast<TypeFunctionRuntime*>(lua_getthreaddata(lua_mainthread(L)));
|
||||
}
|
||||
|
|
|
@ -209,9 +209,7 @@ private:
|
|||
{
|
||||
// Since there aren't any new class types being created in type functions, we will deserialize by using a direct reference to the original
|
||||
// class
|
||||
target = typeFunctionRuntime->typeArena.allocate(
|
||||
TypeFunctionExternType{{}, std::nullopt, std::nullopt, std::nullopt, std::nullopt, ty}
|
||||
);
|
||||
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionExternType{{}, std::nullopt, std::nullopt, std::nullopt, std::nullopt, ty});
|
||||
}
|
||||
else if (auto g = get<GenericType>(ty))
|
||||
{
|
||||
|
|
153
Analysis/src/TypeIds.cpp
Normal file
153
Analysis/src/TypeIds.cpp
Normal file
|
@ -0,0 +1,153 @@
|
|||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
|
||||
#include "Luau/Type.h"
|
||||
#include "Luau/TypeIds.h"
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
TypeIds::TypeIds(std::initializer_list<TypeId> tys)
|
||||
{
|
||||
for (TypeId ty : tys)
|
||||
insert(ty);
|
||||
}
|
||||
|
||||
void TypeIds::insert(TypeId ty)
|
||||
{
|
||||
ty = follow(ty);
|
||||
|
||||
// get a reference to the slot for `ty` in `types`
|
||||
bool& entry = types[ty];
|
||||
|
||||
// if `ty` is fresh, we can set it to `true`, add it to the order and hash and be done.
|
||||
if (!entry)
|
||||
{
|
||||
entry = true;
|
||||
order.push_back(ty);
|
||||
hash ^= std::hash<TypeId>{}(ty);
|
||||
}
|
||||
}
|
||||
|
||||
void TypeIds::clear()
|
||||
{
|
||||
order.clear();
|
||||
types.clear();
|
||||
hash = 0;
|
||||
}
|
||||
|
||||
TypeId TypeIds::front() const
|
||||
{
|
||||
return order.at(0);
|
||||
}
|
||||
|
||||
TypeIds::iterator TypeIds::begin()
|
||||
{
|
||||
return order.begin();
|
||||
}
|
||||
|
||||
TypeIds::iterator TypeIds::end()
|
||||
{
|
||||
return order.end();
|
||||
}
|
||||
|
||||
TypeIds::const_iterator TypeIds::begin() const
|
||||
{
|
||||
return order.begin();
|
||||
}
|
||||
|
||||
TypeIds::const_iterator TypeIds::end() const
|
||||
{
|
||||
return order.end();
|
||||
}
|
||||
|
||||
TypeIds::iterator TypeIds::erase(TypeIds::const_iterator it)
|
||||
{
|
||||
TypeId ty = *it;
|
||||
types[ty] = false;
|
||||
hash ^= std::hash<TypeId>{}(ty);
|
||||
return order.erase(it);
|
||||
}
|
||||
|
||||
void TypeIds::erase(TypeId ty)
|
||||
{
|
||||
const_iterator it = std::find(order.begin(), order.end(), ty);
|
||||
if (it == order.end())
|
||||
return;
|
||||
|
||||
erase(it);
|
||||
}
|
||||
|
||||
size_t TypeIds::size() const
|
||||
{
|
||||
return order.size();
|
||||
}
|
||||
|
||||
bool TypeIds::empty() const
|
||||
{
|
||||
return order.empty();
|
||||
}
|
||||
|
||||
size_t TypeIds::count(TypeId ty) const
|
||||
{
|
||||
ty = follow(ty);
|
||||
const bool* val = types.find(ty);
|
||||
return (val && *val) ? 1 : 0;
|
||||
}
|
||||
|
||||
void TypeIds::retain(const TypeIds& tys)
|
||||
{
|
||||
for (auto it = begin(); it != end();)
|
||||
{
|
||||
if (tys.count(*it))
|
||||
it++;
|
||||
else
|
||||
it = erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
size_t TypeIds::getHash() const
|
||||
{
|
||||
return hash;
|
||||
}
|
||||
|
||||
bool TypeIds::isNever() const
|
||||
{
|
||||
return std::all_of(
|
||||
begin(),
|
||||
end(),
|
||||
[&](TypeId i)
|
||||
{
|
||||
// If each typeid is never, then I guess typeid's is also never?
|
||||
return get<NeverType>(i) != nullptr;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
bool TypeIds::operator==(const TypeIds& there) const
|
||||
{
|
||||
// we can early return if the hashes don't match.
|
||||
if (hash != there.hash)
|
||||
return false;
|
||||
|
||||
// we have to check equality of the sets themselves if not.
|
||||
|
||||
// if the sets are unequal sizes, then they cannot possibly be equal.
|
||||
// it is important to use `order` here and not `types` since the mappings
|
||||
// may have different sizes since removal is not possible, and so erase
|
||||
// simply writes `false` into the map.
|
||||
if (order.size() != there.order.size())
|
||||
return false;
|
||||
|
||||
// otherwise, we'll need to check that every element we have here is in `there`.
|
||||
for (auto ty : order)
|
||||
{
|
||||
// if it's not, we'll return `false`
|
||||
if (there.count(ty) == 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
// otherwise, we've proven the two equal!
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -34,7 +34,6 @@ LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification)
|
|||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
|
||||
LUAU_FASTFLAG(LuauRetainDefinitionAliasLocations)
|
||||
LUAU_FASTFLAGVARIABLE(LuauReduceCheckBinaryExprStackPressure)
|
||||
|
||||
namespace Luau
|
||||
|
@ -1664,10 +1663,7 @@ void TypeChecker::prototype(const ScopePtr& scope, const AstStatTypeAlias& typea
|
|||
FreeType* ftv = getMutable<FreeType>(ty);
|
||||
LUAU_ASSERT(ftv);
|
||||
ftv->forwardedTypeAlias = true;
|
||||
if (FFlag::LuauRetainDefinitionAliasLocations)
|
||||
bindingsMap[name] = {std::move(generics), std::move(genericPacks), ty, typealias.location};
|
||||
else
|
||||
bindingsMap[name] = {std::move(generics), std::move(genericPacks), ty};
|
||||
bindingsMap[name] = {std::move(generics), std::move(genericPacks), ty, typealias.location};
|
||||
|
||||
scope->typeAliasLocations[name] = typealias.location;
|
||||
scope->typeAliasNameLocations[name] = typealias.nameLocation;
|
||||
|
@ -1712,10 +1708,7 @@ void TypeChecker::prototype(const ScopePtr& scope, const AstStatDeclareExternTyp
|
|||
TypeId metaTy = addType(TableType{TableState::Sealed, scope->level});
|
||||
|
||||
etv->metatable = metaTy;
|
||||
if (FFlag::LuauRetainDefinitionAliasLocations)
|
||||
scope->exportedTypeBindings[className] = TypeFun{{}, classTy, declaredExternType.location};
|
||||
else
|
||||
scope->exportedTypeBindings[className] = TypeFun{{}, classTy};
|
||||
scope->exportedTypeBindings[className] = TypeFun{{}, classTy, declaredExternType.location};
|
||||
}
|
||||
|
||||
ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatDeclareExternType& declaredExternType)
|
||||
|
@ -4330,7 +4323,7 @@ void TypeChecker::checkArgumentList(
|
|||
|
||||
if (exceedsLoopCount())
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
TypePackId varPack = addTypePack(TypePackVar{TypePack{rest, argIter.tail()}});
|
||||
state.tryUnify(varPack, tail);
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include <algorithm>
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauErrorSuppressionTypeFunctionArgs)
|
||||
|
||||
namespace Luau
|
||||
|
@ -306,7 +306,7 @@ TypePack extendTypePack(
|
|||
TypePack newPack;
|
||||
newPack.tail = arena.freshTypePack(ftp->scope, ftp->polarity);
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
trackInteriorFreeTypePack(ftp->scope, *newPack.tail);
|
||||
|
||||
if (FFlag::LuauSolverV2)
|
||||
|
@ -588,7 +588,7 @@ void trackInteriorFreeType(Scope* scope, TypeId ty)
|
|||
void trackInteriorFreeTypePack(Scope* scope, TypePackId tp)
|
||||
{
|
||||
LUAU_ASSERT(tp);
|
||||
if (!FFlag::LuauEagerGeneralization)
|
||||
if (!FFlag::LuauEagerGeneralization2)
|
||||
return;
|
||||
|
||||
for (; scope; scope = scope->parent.get())
|
||||
|
@ -685,4 +685,15 @@ std::optional<TypeId> extractMatchingTableType(std::vector<TypeId>& tables, Type
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool isRecord(const AstExprTable::Item& item)
|
||||
{
|
||||
if (item.kind == AstExprTable::Item::Record)
|
||||
return true;
|
||||
else if (item.kind == AstExprTable::Item::General && item.key->is<AstExprConstantString>())
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||
LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -329,12 +329,12 @@ bool Unifier2::unify(TypeId subTy, const FunctionType* superFn)
|
|||
|
||||
for (TypePackId genericPack : subFn->genericPacks)
|
||||
{
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
genericPack = follow(genericPack);
|
||||
|
||||
// TODO: Clip this follow() with LuauEagerGeneralization
|
||||
// TODO: Clip this follow() with LuauEagerGeneralization2
|
||||
const GenericTypePack* gen = get<GenericTypePack>(follow(genericPack));
|
||||
if (gen)
|
||||
genericPackSubstitutions[genericPack] = freshTypePack(scope, gen->polarity);
|
||||
|
@ -465,7 +465,7 @@ bool Unifier2::unify(TableType* subTable, const TableType* superTable)
|
|||
{
|
||||
result &= unify(subTable->indexer->indexType, superTable->indexer->indexType);
|
||||
result &= unify(subTable->indexer->indexResultType, superTable->indexer->indexResultType);
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
// FIXME: We can probably do something more efficient here.
|
||||
result &= unify(superTable->indexer->indexType, subTable->indexer->indexType);
|
||||
|
|
|
@ -393,7 +393,7 @@ public:
|
|||
std::optional<Position> separatorPosition;
|
||||
|
||||
CstExprConstantString* stringInfo = nullptr; // only if Kind == StringProperty
|
||||
Position stringPosition{0, 0}; // only if Kind == StringProperty
|
||||
Position stringPosition{0, 0}; // only if Kind == StringProperty
|
||||
};
|
||||
|
||||
CstTypeTable(AstArray<Item> items, bool isArray);
|
||||
|
|
|
@ -456,7 +456,7 @@ private:
|
|||
AstType* annotation;
|
||||
Position colonPosition;
|
||||
|
||||
explicit Binding(const Name& name, AstType* annotation = nullptr, Position colonPosition = {0,0})
|
||||
explicit Binding(const Name& name, AstType* annotation = nullptr, Position colonPosition = {0, 0})
|
||||
: name(name)
|
||||
, annotation(annotation)
|
||||
, colonPosition(colonPosition)
|
||||
|
|
|
@ -94,7 +94,11 @@ CstStatReturn::CstStatReturn(AstArray<Position> commaPositions)
|
|||
{
|
||||
}
|
||||
|
||||
CstStatLocal::CstStatLocal(AstArray<Position> varsAnnotationColonPositions, AstArray<Position> varsCommaPositions, AstArray<Position> valuesCommaPositions)
|
||||
CstStatLocal::CstStatLocal(
|
||||
AstArray<Position> varsAnnotationColonPositions,
|
||||
AstArray<Position> varsCommaPositions,
|
||||
AstArray<Position> valuesCommaPositions
|
||||
)
|
||||
: CstNode(CstClassIndex())
|
||||
, varsAnnotationColonPositions(varsAnnotationColonPositions)
|
||||
, varsCommaPositions(varsCommaPositions)
|
||||
|
@ -102,7 +106,12 @@ CstStatLocal::CstStatLocal(AstArray<Position> varsAnnotationColonPositions, AstA
|
|||
{
|
||||
}
|
||||
|
||||
CstStatFor::CstStatFor(Position annotationColonPosition, Position equalsPosition, Position endCommaPosition, std::optional<Position> stepCommaPosition)
|
||||
CstStatFor::CstStatFor(
|
||||
Position annotationColonPosition,
|
||||
Position equalsPosition,
|
||||
Position endCommaPosition,
|
||||
std::optional<Position> stepCommaPosition
|
||||
)
|
||||
: CstNode(CstClassIndex())
|
||||
, annotationColonPosition(annotationColonPosition)
|
||||
, equalsPosition(equalsPosition)
|
||||
|
@ -111,7 +120,11 @@ CstStatFor::CstStatFor(Position annotationColonPosition, Position equalsPosition
|
|||
{
|
||||
}
|
||||
|
||||
CstStatForIn::CstStatForIn(AstArray<Position> varsAnnotationColonPositions, AstArray<Position> varsCommaPositions, AstArray<Position> valuesCommaPositions)
|
||||
CstStatForIn::CstStatForIn(
|
||||
AstArray<Position> varsAnnotationColonPositions,
|
||||
AstArray<Position> varsCommaPositions,
|
||||
AstArray<Position> valuesCommaPositions
|
||||
)
|
||||
: CstNode(CstClassIndex())
|
||||
, varsAnnotationColonPositions(varsAnnotationColonPositions)
|
||||
, varsCommaPositions(varsCommaPositions)
|
||||
|
|
|
@ -709,7 +709,8 @@ AstStat* Parser::parseFor()
|
|||
if (options.storeCstData)
|
||||
{
|
||||
if (FFlag::LuauStoreLocalAnnotationColonPositions)
|
||||
cstNodeMap[node] = allocator.alloc<CstStatForIn>(extractAnnotationColonPositions(names), varsCommaPosition, copy(valuesCommaPositions));
|
||||
cstNodeMap[node] =
|
||||
allocator.alloc<CstStatForIn>(extractAnnotationColonPositions(names), varsCommaPosition, copy(valuesCommaPositions));
|
||||
else
|
||||
cstNodeMap[node] = allocator.alloc<CstStatForIn>(AstArray<Position>{}, varsCommaPosition, copy(valuesCommaPositions));
|
||||
}
|
||||
|
@ -1009,7 +1010,8 @@ AstStat* Parser::parseLocal(const AstArray<AstAttr*>& attributes)
|
|||
if (options.storeCstData)
|
||||
{
|
||||
if (FFlag::LuauStoreLocalAnnotationColonPositions)
|
||||
cstNodeMap[node] = allocator.alloc<CstStatLocal>(extractAnnotationColonPositions(names), varsCommaPositions, copy(valuesCommaPositions));
|
||||
cstNodeMap[node] =
|
||||
allocator.alloc<CstStatLocal>(extractAnnotationColonPositions(names), varsCommaPositions, copy(valuesCommaPositions));
|
||||
else
|
||||
cstNodeMap[node] = allocator.alloc<CstStatLocal>(AstArray<Position>{}, varsCommaPositions, copy(valuesCommaPositions));
|
||||
}
|
||||
|
@ -1333,7 +1335,9 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
|||
foundExtern = true;
|
||||
nextLexeme();
|
||||
if (AstName(lexer.current().name) != "type")
|
||||
return reportStatError(lexer.current().location, {}, {}, "Expected `type` keyword after `extern`, but got %s instead", lexer.current().name);
|
||||
return reportStatError(
|
||||
lexer.current().location, {}, {}, "Expected `type` keyword after `extern`, but got %s instead", lexer.current().name
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1354,7 +1358,11 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
|||
if (foundExtern)
|
||||
{
|
||||
if (AstName(lexer.current().name) != "with")
|
||||
report(lexer.current().location, "Expected `with` keyword before listing properties of the external type, but got %s instead", lexer.current().name);
|
||||
report(
|
||||
lexer.current().location,
|
||||
"Expected `with` keyword before listing properties of the external type, but got %s instead",
|
||||
lexer.current().name
|
||||
);
|
||||
else
|
||||
nextLexeme();
|
||||
}
|
||||
|
@ -1452,9 +1460,9 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
|||
|
||||
expectAndConsume(':', "property type annotation");
|
||||
AstType* propType = parseType();
|
||||
props.push_back(
|
||||
AstDeclaredExternTypeProperty{propName->name, propName->location, propType, false, Location(propStart, lexer.previousLocation())}
|
||||
);
|
||||
props.push_back(AstDeclaredExternTypeProperty{
|
||||
propName->name, propName->location, propType, false, Location(propStart, lexer.previousLocation())
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -1533,9 +1541,9 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
|||
|
||||
expectAndConsume(':', "property type annotation");
|
||||
AstType* propType = parseType();
|
||||
props.push_back(
|
||||
AstDeclaredExternTypeProperty{propName->name, propName->location, propType, false, Location(propStart, lexer.previousLocation())}
|
||||
);
|
||||
props.push_back(AstDeclaredExternTypeProperty{
|
||||
propName->name, propName->location, propType, false, Location(propStart, lexer.previousLocation())
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2618,7 +2626,7 @@ AstType* Parser::parseTableType(bool inDeclarationContext)
|
|||
tableSeparator(),
|
||||
lexer.current().location.begin,
|
||||
allocator.alloc<CstExprConstantString>(sourceString, style, blockDepth),
|
||||
stringPosition
|
||||
stringPosition
|
||||
});
|
||||
}
|
||||
else
|
||||
|
|
|
@ -14,16 +14,11 @@ void requireConfigInit(luarequire_Configuration* config);
|
|||
|
||||
struct ReplRequirer
|
||||
{
|
||||
using CompileOptions = Luau::CompileOptions(*)();
|
||||
using BoolCheck = bool(*)();
|
||||
using Coverage = void(*)(lua_State*, int);
|
||||
using CompileOptions = Luau::CompileOptions (*)();
|
||||
using BoolCheck = bool (*)();
|
||||
using Coverage = void (*)(lua_State*, int);
|
||||
|
||||
ReplRequirer(
|
||||
CompileOptions copts,
|
||||
BoolCheck coverageActive,
|
||||
BoolCheck codegenEnabled,
|
||||
Coverage coverageTrack
|
||||
);
|
||||
ReplRequirer(CompileOptions copts, BoolCheck coverageActive, BoolCheck codegenEnabled, Coverage coverageTrack);
|
||||
|
||||
CompileOptions copts;
|
||||
BoolCheck coverageActive;
|
||||
|
|
|
@ -39,8 +39,8 @@ inline constexpr RegisterA64 rBase = x25; // StkId base
|
|||
|
||||
// Native code is as stackless as the interpreter, so we can place some data on the stack once and have it accessible at any point
|
||||
// See CodeGenA64.cpp for layout
|
||||
inline constexpr unsigned kStashSlots = 9; // stashed non-volatile registers
|
||||
inline constexpr unsigned kTempSlots = 1; // 8 bytes of temporary space, such luxury!
|
||||
inline constexpr unsigned kStashSlots = 9; // stashed non-volatile registers
|
||||
inline constexpr unsigned kTempSlots = 1; // 8 bytes of temporary space, such luxury!
|
||||
inline constexpr unsigned kSpillSlots = 22; // slots for spilling temporary registers
|
||||
|
||||
inline constexpr unsigned kStackSize = (kStashSlots + kTempSlots + kSpillSlots) * 8;
|
||||
|
|
|
@ -189,6 +189,7 @@ target_sources(Luau.Analysis PRIVATE
|
|||
Analysis/include/Luau/Documentation.h
|
||||
Analysis/include/Luau/Error.h
|
||||
Analysis/include/Luau/EqSatSimplification.h
|
||||
Analysis/include/Luau/ExpectedTypeVisitor.h
|
||||
Analysis/include/Luau/FileResolver.h
|
||||
Analysis/include/Luau/FragmentAutocomplete.h
|
||||
Analysis/include/Luau/Frontend.h
|
||||
|
@ -237,6 +238,7 @@ target_sources(Luau.Analysis PRIVATE
|
|||
Analysis/include/Luau/TypeFunctionRuntime.h
|
||||
Analysis/include/Luau/TypeFunctionRuntimeBuilder.h
|
||||
Analysis/include/Luau/TypeFwd.h
|
||||
Analysis/include/Luau/TypeIds.h
|
||||
Analysis/include/Luau/TypeInfer.h
|
||||
Analysis/include/Luau/TypeOrPack.h
|
||||
Analysis/include/Luau/TypePack.h
|
||||
|
@ -267,6 +269,7 @@ target_sources(Luau.Analysis PRIVATE
|
|||
Analysis/src/EmbeddedBuiltinDefinitions.cpp
|
||||
Analysis/src/Error.cpp
|
||||
Analysis/src/EqSatSimplification.cpp
|
||||
Analysis/src/ExpectedTypeVisitor.cpp
|
||||
Analysis/src/FileResolver.cpp
|
||||
Analysis/src/FragmentAutocomplete.cpp
|
||||
Analysis/src/Frontend.cpp
|
||||
|
@ -306,6 +309,7 @@ target_sources(Luau.Analysis PRIVATE
|
|||
Analysis/src/TypeFunctionReductionGuesser.cpp
|
||||
Analysis/src/TypeFunctionRuntime.cpp
|
||||
Analysis/src/TypeFunctionRuntimeBuilder.cpp
|
||||
Analysis/src/TypeIds.cpp
|
||||
Analysis/src/TypeInfer.cpp
|
||||
Analysis/src/TypeOrPack.cpp
|
||||
Analysis/src/TypePack.cpp
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauUnrefExisting, false)
|
||||
|
||||
/*
|
||||
* This file contains most implementations of core Lua APIs from lua.h.
|
||||
*
|
||||
|
@ -1445,9 +1447,26 @@ void lua_unref(lua_State* L, int ref)
|
|||
|
||||
global_State* g = L->global;
|
||||
LuaTable* reg = hvalue(registry(L));
|
||||
TValue* slot = luaH_setnum(L, reg, ref);
|
||||
setnvalue(slot, g->registryfree); // NB: no barrier needed because value isn't collectable
|
||||
g->registryfree = ref;
|
||||
|
||||
if (DFFlag::LuauUnrefExisting)
|
||||
{
|
||||
const TValue* slot = luaH_getnum(reg, ref);
|
||||
api_check(L, slot != luaO_nilobject);
|
||||
|
||||
// similar to how 'luaH_setnum' makes non-nil slot value mutable
|
||||
TValue* mutableSlot = (TValue*)slot;
|
||||
|
||||
// NB: no barrier needed because value isn't collectable
|
||||
setnvalue(mutableSlot, g->registryfree);
|
||||
|
||||
g->registryfree = ref;
|
||||
}
|
||||
else
|
||||
{
|
||||
TValue* slot = luaH_setnum(L, reg, ref);
|
||||
setnvalue(slot, g->registryfree); // NB: no barrier needed because value isn't collectable
|
||||
g->registryfree = ref;
|
||||
}
|
||||
}
|
||||
|
||||
void lua_setuserdatatag(lua_State* L, int idx, int tag)
|
||||
|
|
|
@ -642,4 +642,3 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstGenericTypePackWithDefault")
|
|||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
||||
|
|
|
@ -19,7 +19,8 @@
|
|||
LUAU_FASTFLAG(LuauTraceTypesInNonstrictMode2)
|
||||
LUAU_FASTFLAG(LuauSetMetatableDoesNotTimeTravel)
|
||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||
LUAU_FASTFLAG(LuauExpectedTypeVisitor)
|
||||
|
||||
using namespace Luau;
|
||||
|
||||
|
@ -41,6 +42,9 @@ struct ACFixtureImpl : BaseType
|
|||
FrontendOptions opts;
|
||||
opts.forAutocomplete = true;
|
||||
opts.retainFullTypeGraphs = true;
|
||||
// NOTE: Autocomplete does *not* require strict checking, meaning we should
|
||||
// try to check all of these examples in `--!nocheck` mode.
|
||||
this->configResolver.defaultConfig.mode = Mode::NoCheck;
|
||||
this->frontend.check("MainModule", opts);
|
||||
|
||||
return Luau::autocomplete(this->frontend, "MainModule", Position{row, column}, nullCallback);
|
||||
|
@ -51,6 +55,9 @@ struct ACFixtureImpl : BaseType
|
|||
FrontendOptions opts;
|
||||
opts.forAutocomplete = true;
|
||||
opts.retainFullTypeGraphs = true;
|
||||
// NOTE: Autocomplete does *not* require strict checking, meaning we should
|
||||
// try to check all of these examples in `--!nocheck` mode.
|
||||
this->configResolver.defaultConfig.mode = Mode::NoCheck;
|
||||
this->frontend.check("MainModule", opts);
|
||||
|
||||
return Luau::autocomplete(this->frontend, "MainModule", getPosition(marker), callback);
|
||||
|
@ -61,6 +68,9 @@ struct ACFixtureImpl : BaseType
|
|||
FrontendOptions opts;
|
||||
opts.forAutocomplete = true;
|
||||
opts.retainFullTypeGraphs = true;
|
||||
// NOTE: Autocomplete does *not* require strict checking, meaning we should
|
||||
// try to check all of these examples in `--!nocheck` mode.
|
||||
this->configResolver.defaultConfig.mode = Mode::NoCheck;
|
||||
this->frontend.check(name, opts);
|
||||
|
||||
return Luau::autocomplete(this->frontend, name, pos, callback);
|
||||
|
@ -103,7 +113,9 @@ struct ACFixtureImpl : BaseType
|
|||
}
|
||||
LUAU_ASSERT("Digit expected after @ symbol" && prevChar != '@');
|
||||
|
||||
return BaseType::check(filteredSource);
|
||||
// NOTE: Autocomplete does *not* require strict checking, meaning we should
|
||||
// try to check all of these examples in `--!nocheck` mode.
|
||||
return BaseType::check(Mode::NoCheck, filteredSource, std::nullopt);
|
||||
}
|
||||
|
||||
LoadDefinitionFileResult loadDefinition(const std::string& source)
|
||||
|
@ -2191,7 +2203,10 @@ local fp: @1= f
|
|||
if (FFlag::LuauSolverV2)
|
||||
REQUIRE_EQ("({ x: number, y: number }) -> number", toString(requireType("f")));
|
||||
else
|
||||
REQUIRE_EQ("({| x: number, y: number |}) -> number", toString(requireType("f")));
|
||||
{
|
||||
// NOTE: All autocomplete tests occur under no-check mode.
|
||||
REQUIRE_EQ("({| x: number, y: number |}) -> (...any)", toString(requireType("f")));
|
||||
}
|
||||
CHECK(ac.entryMap.count("({ x: number, y: number }) -> number"));
|
||||
}
|
||||
|
||||
|
@ -3120,7 +3135,6 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_string_singletons")
|
|||
|
||||
TEST_CASE_FIXTURE(ACFixture, "string_singleton_as_table_key_iso")
|
||||
{
|
||||
|
||||
check(R"(
|
||||
type Direction = "up" | "down"
|
||||
local b: {[Direction]: boolean} = {["@2"] = true}
|
||||
|
@ -4454,9 +4468,9 @@ TEST_CASE_FIXTURE(ACExternTypeFixture, "ac_dont_overflow_on_recursive_union")
|
|||
|
||||
auto ac = autocomplete('1');
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
// This `if` statement is because `LuauEagerGeneralization`
|
||||
// This `if` statement is because `LuauEagerGeneralization2`
|
||||
// sets some flags
|
||||
CHECK(ac.entryMap.count("BaseMethod") > 0);
|
||||
CHECK(ac.entryMap.count("Method") > 0);
|
||||
|
@ -4466,7 +4480,6 @@ TEST_CASE_FIXTURE(ACExternTypeFixture, "ac_dont_overflow_on_recursive_union")
|
|||
// Otherwise, we don't infer anything for `value`, which is _fine_.
|
||||
CHECK(ac.entryMap.empty());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ACBuiltinsFixture, "type_function_has_types_definitions")
|
||||
|
@ -4536,4 +4549,62 @@ end
|
|||
CHECK_EQ(ac.entryMap.count("number"), 1);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ACFixture, "autocomplete_for_assignment")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauExpectedTypeVisitor, true};
|
||||
|
||||
check(R"(
|
||||
local function foobar(tbl: { tag: "left" | "right" })
|
||||
tbl.tag = "@1"
|
||||
end
|
||||
)");
|
||||
|
||||
auto ac = autocomplete('1');
|
||||
CHECK_EQ(ac.entryMap.count("left"), 1);
|
||||
CHECK_EQ(ac.entryMap.count("right"), 1);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ACFixture, "autocomplete_in_local_table")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauExpectedTypeVisitor, true};
|
||||
|
||||
check(R"(
|
||||
type Entry = { field: number, prop: string }
|
||||
local x : {Entry} = {}
|
||||
x[1] = {
|
||||
f@1,
|
||||
p@2,
|
||||
}
|
||||
|
||||
local t : { key1: boolean, thing2: CFrame, aaa3: vector } = {
|
||||
k@3,
|
||||
th@4,
|
||||
}
|
||||
)");
|
||||
|
||||
auto ac1 = autocomplete('1');
|
||||
CHECK_EQ(ac1.entryMap.count("field"), 1);
|
||||
auto ac2 = autocomplete('2');
|
||||
CHECK_EQ(ac2.entryMap.count("prop"), 1);
|
||||
auto ac3 = autocomplete('3');
|
||||
CHECK_EQ(ac3.entryMap.count("key1"), 1);
|
||||
auto ac4 = autocomplete('4');
|
||||
CHECK_EQ(ac4.entryMap.count("thing2"), 1);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ACFixture, "autocomplete_in_type_assertion")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauExpectedTypeVisitor, true};
|
||||
|
||||
check(R"(
|
||||
type Entry = { field: number, prop: string }
|
||||
return ( { f@1, p@2 } :: Entry )
|
||||
)");
|
||||
|
||||
auto ac1 = autocomplete('1');
|
||||
CHECK_EQ(ac1.entryMap.count("field"), 1);
|
||||
auto ac2 = autocomplete('2');
|
||||
CHECK_EQ(ac2.entryMap.count("prop"), 1);
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
|
|
@ -56,7 +56,6 @@ struct DataFlowGraphFixture
|
|||
CHECK(phi->operands.size() == operandSet.size());
|
||||
for (auto o : phi->operands)
|
||||
CHECK(operandSet.contains(o.get()));
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ LUAU_FASTFLAG(DebugLuauFreezeArena)
|
|||
LUAU_FASTFLAG(DebugLuauForceAllNewSolverTests)
|
||||
|
||||
LUAU_FASTFLAG(LuauTypeFunOptional)
|
||||
LUAU_FASTFLAG(LuauUpdateSetMetatableTypeSignature)
|
||||
|
||||
#define DOES_NOT_PASS_NEW_SOLVER_GUARD_IMPL(line) ScopedFastFlag sff_##line{FFlag::LuauSolverV2, FFlag::DebugLuauForceAllNewSolverTests};
|
||||
|
||||
|
@ -148,6 +149,7 @@ struct Fixture
|
|||
// Most often those are changes related to builtin type definitions.
|
||||
// In that case, flag can be forced to 'true' using the example below:
|
||||
// ScopedFastFlag sff_LuauExampleFlagDefinition{FFlag::LuauExampleFlagDefinition, true};
|
||||
ScopedFastFlag sff_LuauUpdateSetMetatableTypeSignature{FFlag::LuauUpdateSetMetatableTypeSignature, true};
|
||||
|
||||
// Arena freezing marks the `TypeArena`'s underlying memory as read-only, raising an access violation whenever you mutate it.
|
||||
// This is useful for tracking down violations of Luau's memory model.
|
||||
|
|
|
@ -31,6 +31,7 @@ LUAU_FASTFLAG(LuauBlockDiffFragmentSelection)
|
|||
LUAU_FASTFLAG(LuauFragmentAcMemoryLeak)
|
||||
LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation)
|
||||
LUAU_FASTFLAG(LuauFragmentAutocompleteIfRecommendations)
|
||||
LUAU_FASTFLAG(LuauPopulateRefinedTypesInFragmentFromOldSolver)
|
||||
|
||||
static std::optional<AutocompleteEntryMap> nullCallback(std::string tag, std::optional<const ExternType*> ptr, std::optional<std::string> contents)
|
||||
{
|
||||
|
@ -65,6 +66,7 @@ struct FragmentAutocompleteFixtureImpl : BaseType
|
|||
ScopedFastFlag luauFragmentAcMemoryLeak{FFlag::LuauFragmentAcMemoryLeak, true};
|
||||
ScopedFastFlag luauGlobalVariableModuleIsolation{FFlag::LuauGlobalVariableModuleIsolation, true};
|
||||
ScopedFastFlag luauFragmentAutocompleteIfRecommendations{FFlag::LuauFragmentAutocompleteIfRecommendations, true};
|
||||
ScopedFastFlag luauPopulateRefinedTypesInFragmentFromOldSolver{FFlag::LuauPopulateRefinedTypesInFragmentFromOldSolver, true};
|
||||
|
||||
FragmentAutocompleteFixtureImpl()
|
||||
: BaseType(true)
|
||||
|
@ -146,6 +148,37 @@ struct FragmentAutocompleteFixtureImpl : BaseType
|
|||
return Luau::tryFragmentAutocomplete(this->frontend, "MainModule", cursorPos, context, nullCallback);
|
||||
}
|
||||
|
||||
void autocompleteFragmentInNewSolver(
|
||||
const std::string& document,
|
||||
const std::string& updated,
|
||||
Position cursorPos,
|
||||
std::function<void(FragmentAutocompleteStatusResult& result)> assertions,
|
||||
std::optional<Position> fragmentEndPosition = std::nullopt
|
||||
)
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
|
||||
this->check(document, getOptions());
|
||||
|
||||
FragmentAutocompleteStatusResult result = autocompleteFragment(updated, cursorPos, fragmentEndPosition);
|
||||
CHECK(result.status != FragmentAutocompleteStatus::InternalIce);
|
||||
assertions(result);
|
||||
}
|
||||
|
||||
void autocompleteFragmentInOldSolver(
|
||||
const std::string& document,
|
||||
const std::string& updated,
|
||||
Position cursorPos,
|
||||
std::function<void(FragmentAutocompleteStatusResult& result)> assertions,
|
||||
std::optional<Position> fragmentEndPosition = std::nullopt
|
||||
)
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
|
||||
this->check(document, getOptions());
|
||||
|
||||
FragmentAutocompleteStatusResult result = autocompleteFragment(updated, cursorPos, fragmentEndPosition);
|
||||
CHECK(result.status != FragmentAutocompleteStatus::InternalIce);
|
||||
assertions(result);
|
||||
}
|
||||
|
||||
void autocompleteFragmentInBothSolvers(
|
||||
const std::string& document,
|
||||
|
@ -3702,6 +3735,138 @@ end
|
|||
);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "tagged_union_completion_first_branch_of_union_old_solver")
|
||||
{
|
||||
const std::string source = R"(
|
||||
type Ok<T> = { type: "ok", value: T}
|
||||
type Err<E> = { type : "err", error : E}
|
||||
type Result<T,E> = Ok<T> | Err<E>
|
||||
|
||||
local result = {} :: Result<number, string>
|
||||
|
||||
if result.type == "ok" then
|
||||
|
||||
end
|
||||
)";
|
||||
|
||||
const std::string dest = R"(
|
||||
type Ok<T> = { type: "ok", value: T}
|
||||
type Err<E> = { type : "err", error : E}
|
||||
type Result<T,E> = Ok<T> | Err<E>
|
||||
|
||||
local result = {} :: Result<number, string>
|
||||
|
||||
if result.type == "ok" then
|
||||
result.
|
||||
end
|
||||
)";
|
||||
autocompleteFragmentInOldSolver(source, dest, Position{8, 11}, [](auto& result){
|
||||
REQUIRE(result.result);
|
||||
CHECK_EQ(result.result->acResults.entryMap.count("type"), 1);
|
||||
CHECK_EQ(result.result->acResults.entryMap.count("value"), 1);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "tagged_union_completion_second_branch_of_union_old_solver")
|
||||
{
|
||||
const std::string source = R"(
|
||||
type Ok<T> = { type: "ok", value: T}
|
||||
type Err<E> = { type : "err", error : E}
|
||||
type Result<T,E> = Ok<T> | Err<E>
|
||||
|
||||
local result = {} :: Result<number, string>
|
||||
|
||||
if result.type == "err" then
|
||||
|
||||
end
|
||||
)";
|
||||
|
||||
const std::string dest = R"(
|
||||
type Ok<T> = { type: "ok", value: T}
|
||||
type Err<E> = { type : "err", error : E}
|
||||
type Result<T,E> = Ok<T> | Err<E>
|
||||
|
||||
local result = {} :: Result<number, string>
|
||||
|
||||
if result.type == "err" then
|
||||
result.
|
||||
end
|
||||
)";
|
||||
|
||||
autocompleteFragmentInOldSolver(source, dest, Position{8, 11}, [](auto& result){
|
||||
REQUIRE(result.result);
|
||||
CHECK_EQ(result.result->acResults.entryMap.count("type"), 1);
|
||||
CHECK_EQ(result.result->acResults.entryMap.count("error"), 1);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "tagged_union_completion_first_branch_of_union_new_solver" * doctest::skip(true))
|
||||
{
|
||||
// TODO: CLI-155619 - Fragment autocomplete needs to use stale refinement information for modules typechecked in the new solver as well
|
||||
const std::string source = R"(
|
||||
type Ok<T> = { type: "ok", value: T}
|
||||
type Err<E> = { type : "err", error : E}
|
||||
type Result<T,E> = Ok<T> | Err<E>
|
||||
|
||||
local result = {} :: Result<number, string>
|
||||
|
||||
if result.type == "ok" then
|
||||
|
||||
end
|
||||
)";
|
||||
|
||||
const std::string dest = R"(
|
||||
type Ok<T> = { type: "ok", value: T}
|
||||
type Err<E> = { type : "err", error : E}
|
||||
type Result<T,E> = Ok<T> | Err<E>
|
||||
|
||||
local result = {} :: Result<number, string>
|
||||
|
||||
if result.type == "ok" then
|
||||
result.
|
||||
end
|
||||
)";
|
||||
autocompleteFragmentInNewSolver(source, dest, Position{8, 11}, [](auto& result){
|
||||
REQUIRE(result.result);
|
||||
CHECK_EQ(result.result->acResults.entryMap.count("type"), 1);
|
||||
CHECK_EQ(result.result->acResults.entryMap.count("value"), 1);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "tagged_union_completion_second_branch_of_union_new_solver" * doctest::skip(true))
|
||||
{
|
||||
// TODO: CLI-155619 - Fragment autocomplete needs to use stale refinement information for modules typechecked in the new solver as well
|
||||
const std::string source = R"(
|
||||
type Ok<T> = { type: "ok", value: T}
|
||||
type Err<E> = { type : "err", error : E}
|
||||
type Result<T,E> = Ok<T> | Err<E>
|
||||
|
||||
local result = {} :: Result<number, string>
|
||||
|
||||
if result.type == "err" then
|
||||
|
||||
end
|
||||
)";
|
||||
|
||||
const std::string dest = R"(
|
||||
type Ok<T> = { type: "ok", value: T}
|
||||
type Err<E> = { type : "err", error : E}
|
||||
type Result<T,E> = Ok<T> | Err<E>
|
||||
|
||||
local result = {} :: Result<number, string>
|
||||
|
||||
if result.type == "err" then
|
||||
result.
|
||||
end
|
||||
)";
|
||||
|
||||
autocompleteFragmentInNewSolver(source, dest, Position{8, 11}, [](auto& result){
|
||||
REQUIRE(result.result);
|
||||
CHECK_EQ(result.result->acResults.entryMap.count("type"), 1);
|
||||
CHECK_EQ(result.result->acResults.entryMap.count("error"), 1);
|
||||
});
|
||||
}
|
||||
|
||||
// NOLINTEND(bugprone-unchecked-optional-access)
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
|
|
@ -897,7 +897,8 @@ TEST_CASE_FIXTURE(FrontendFixture, "it_should_be_safe_to_stringify_errors_when_f
|
|||
{
|
||||
CHECK_EQ(
|
||||
"Table type '{ count: string }' not compatible with type '{ Count: number }' because the former is missing field 'Count'",
|
||||
toString(result.errors[0]));
|
||||
toString(result.errors[0])
|
||||
);
|
||||
}
|
||||
else
|
||||
REQUIRE_EQ(
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
using namespace Luau;
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||
LUAU_FASTFLAG(DebugLuauForbidInternalTypes)
|
||||
LUAU_FASTFLAG(LuauTrackInferredFunctionTypeFromCall)
|
||||
|
||||
|
@ -115,7 +115,8 @@ TEST_CASE_FIXTURE(GeneralizationFixture, "dont_traverse_into_class_types_when_ge
|
|||
{
|
||||
auto [propTy, _] = freshType();
|
||||
|
||||
TypeId cursedExternType = arena.addType(ExternType{"Cursed", {{"oh_no", Property::readonly(propTy)}}, std::nullopt, std::nullopt, {}, {}, "", {}});
|
||||
TypeId cursedExternType =
|
||||
arena.addType(ExternType{"Cursed", {{"oh_no", Property::readonly(propTy)}}, std::nullopt, std::nullopt, {}, {}, "", {}});
|
||||
|
||||
auto genExternType = generalize(cursedExternType);
|
||||
REQUIRE(genExternType);
|
||||
|
@ -226,7 +227,7 @@ TEST_CASE_FIXTURE(GeneralizationFixture, "('a) -> 'a")
|
|||
|
||||
TEST_CASE_FIXTURE(GeneralizationFixture, "(t1, (t1 <: 'b)) -> () where t1 = ('a <: (t1 <: 'b) & {number} & {number})")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::LuauEagerGeneralization, true};
|
||||
ScopedFastFlag sff{FFlag::LuauEagerGeneralization2, true};
|
||||
|
||||
TableType tt;
|
||||
tt.indexer = TableIndexer{builtinTypes.numberType, builtinTypes.numberType};
|
||||
|
@ -260,7 +261,7 @@ TEST_CASE_FIXTURE(GeneralizationFixture, "(('a <: number | string)) -> string?")
|
|||
|
||||
TEST_CASE_FIXTURE(GeneralizationFixture, "(('a <: {'b})) -> ()")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::LuauEagerGeneralization, true};
|
||||
ScopedFastFlag sff{FFlag::LuauEagerGeneralization2, true};
|
||||
|
||||
auto [aTy, aFree] = freshType();
|
||||
auto [bTy, bFree] = freshType();
|
||||
|
@ -341,10 +342,7 @@ end
|
|||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "generalization_should_not_leak_free_type")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::DebugLuauForbidInternalTypes, true},
|
||||
{FFlag::LuauTrackInferredFunctionTypeFromCall, true}
|
||||
};
|
||||
ScopedFastFlag sffs[] = {{FFlag::DebugLuauForbidInternalTypes, true}, {FFlag::LuauTrackInferredFunctionTypeFromCall, true}};
|
||||
|
||||
// This test case should just not assert
|
||||
CheckResult result = check(R"(
|
||||
|
|
|
@ -8,13 +8,13 @@
|
|||
|
||||
using namespace Luau;
|
||||
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization);
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2);
|
||||
|
||||
TEST_SUITE_BEGIN("InferPolarity");
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "T where T = { m: <a>(a) -> T }")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::LuauEagerGeneralization, true};
|
||||
ScopedFastFlag sff{FFlag::LuauEagerGeneralization2, true};
|
||||
|
||||
TypeArena arena;
|
||||
ScopePtr globalScope = std::make_shared<Scope>(builtinTypes->anyTypePack);
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
LUAU_FASTFLAG(LuauSolverV2);
|
||||
LUAU_FASTFLAG(LintRedundantNativeAttribute);
|
||||
LUAU_FASTFLAG(LuauDeprecatedAttribute);
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization);
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2);
|
||||
|
||||
using namespace Luau;
|
||||
|
||||
|
@ -1942,7 +1942,7 @@ print(foo:bar(2.0))
|
|||
TEST_CASE_FIXTURE(BuiltinsFixture, "TableOperations")
|
||||
{
|
||||
// FIXME: For now this flag causes a stack overflow on Windows.
|
||||
ScopedFastFlag _{FFlag::LuauEagerGeneralization, false};
|
||||
ScopedFastFlag _{FFlag::LuauEagerGeneralization2, false};
|
||||
|
||||
LintResult result = lint(R"(
|
||||
local t = {}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <iostream>
|
||||
|
||||
LUAU_FASTFLAG(LuauNewNonStrictVisitTypes2)
|
||||
LUAU_FASTFLAG(LuauNewNonStrictFixGenericTypePacks)
|
||||
|
||||
using namespace Luau;
|
||||
|
||||
|
@ -560,6 +561,18 @@ optionalArgsAtTheEnd1("a", nil, 3)
|
|||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "generic_type_packs_in_non_strict")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::LuauNewNonStrictFixGenericTypePacks, true};
|
||||
|
||||
CheckResult result = checkNonStrict(R"(
|
||||
--!nonstrict
|
||||
local test: <T...>(T...) -> () -- TypeError: Unknown type 'T'
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "optionals_in_checked_function_in_middle_cannot_be_omitted")
|
||||
{
|
||||
CheckResult result = checkNonStrict(R"(
|
||||
|
|
|
@ -14,7 +14,7 @@ LUAU_FASTFLAG(LuauSolverV2)
|
|||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||
LUAU_FASTINT(LuauNormalizeIntersectionLimit)
|
||||
LUAU_FASTINT(LuauNormalizeUnionLimit)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||
LUAU_FASTFLAG(LuauRefineWaitForBlockedTypesInTarget)
|
||||
LUAU_FASTFLAG(LuauSimplifyOutOfLine)
|
||||
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
||||
|
@ -1206,7 +1206,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_union_type_pack_cycle")
|
|||
{FFlag::LuauNoMoreInjectiveTypeFunctions, true},
|
||||
{FFlag::LuauOptimizeFalsyAndTruthyIntersect, true},
|
||||
{FFlag::LuauClipVariadicAnysFromArgsToGenericFuncs2, true},
|
||||
{FFlag::LuauEagerGeneralization, true}
|
||||
{FFlag::LuauEagerGeneralization2, true}
|
||||
};
|
||||
ScopedFastInt sfi{FInt::LuauTypeInferRecursionLimit, 0};
|
||||
|
||||
|
@ -1221,6 +1221,6 @@ _[_] ^= _(_(_))
|
|||
InternalCompilerError
|
||||
);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
|
|
@ -2086,7 +2086,8 @@ TEST_CASE_FIXTURE(Fixture, "parse_extern_type_declarations")
|
|||
declare extern type Bar extends Foo with
|
||||
prop2: string
|
||||
end
|
||||
)").root;
|
||||
)")
|
||||
.root;
|
||||
|
||||
REQUIRE_EQ(stat->body.size, 2);
|
||||
|
||||
|
@ -2194,7 +2195,8 @@ TEST_CASE_FIXTURE(Fixture, "parse_extern_type_declarations")
|
|||
declare extern type Bar extends Foo with
|
||||
prop2: string
|
||||
end
|
||||
)").root;
|
||||
)")
|
||||
.root;
|
||||
|
||||
REQUIRE_EQ(stat->body.size, 2);
|
||||
|
||||
|
@ -2355,9 +2357,7 @@ TEST_CASE_FIXTURE(Fixture, "class_indexer")
|
|||
[number]: number
|
||||
end
|
||||
)",
|
||||
(FFlag::LuauDeclareExternType)
|
||||
? "Cannot have more than one indexer on an extern type"
|
||||
: "Cannot have more than one class indexer"
|
||||
(FFlag::LuauDeclareExternType) ? "Cannot have more than one indexer on an extern type" : "Cannot have more than one class indexer"
|
||||
);
|
||||
|
||||
REQUIRE_EQ(1, p1.root->body.size);
|
||||
|
@ -2904,10 +2904,13 @@ TEST_CASE_FIXTURE(Fixture, "for_loop_with_single_var_has_comma_positions_of_size
|
|||
ParseOptions parseOptions;
|
||||
parseOptions.storeCstData = true;
|
||||
|
||||
ParseResult result = parseEx(R"(
|
||||
ParseResult result = parseEx(
|
||||
R"(
|
||||
for value in tbl do
|
||||
end
|
||||
)", parseOptions);
|
||||
)",
|
||||
parseOptions
|
||||
);
|
||||
REQUIRE(result.root);
|
||||
REQUIRE_EQ(1, result.root->body.size);
|
||||
|
||||
|
@ -4188,7 +4191,7 @@ TEST_CASE_FIXTURE(Fixture, "grouped_function_type")
|
|||
CHECK_EQ(unionTy->types.size, 2);
|
||||
auto groupTy = unionTy->types.data[0]->as<AstTypeGroup>(); // (() -> ())
|
||||
REQUIRE(groupTy);
|
||||
CHECK(groupTy->type->is<AstTypeFunction>()); // () -> ()
|
||||
CHECK(groupTy->type->is<AstTypeFunction>()); // () -> ()
|
||||
CHECK(unionTy->types.data[1]->is<AstTypeOptional>()); // ?
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauSubtypeGenericsAndNegations)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||
|
||||
using namespace Luau;
|
||||
|
||||
|
@ -1629,19 +1629,13 @@ TEST_CASE_FIXTURE(SubtypeFixture, "substitute_a_generic_for_a_negation")
|
|||
TypeId bTy = arena.addType(GenericType{"B"});
|
||||
getMutable<GenericType>(bTy)->scope = moduleScope.get();
|
||||
|
||||
TypeId genericFunctionTy = arena.addType(FunctionType{
|
||||
{aTy, bTy},
|
||||
{},
|
||||
arena.addTypePack({aTy, bTy}),
|
||||
arena.addTypePack({join(meet(aTy, builtinTypes->truthyType), bTy)})
|
||||
});
|
||||
TypeId genericFunctionTy =
|
||||
arena.addType(FunctionType{{aTy, bTy}, {}, arena.addTypePack({aTy, bTy}), arena.addTypePack({join(meet(aTy, builtinTypes->truthyType), bTy)})}
|
||||
);
|
||||
|
||||
const TypeId truthyTy = builtinTypes->truthyType;
|
||||
|
||||
TypeId actualFunctionTy = fn(
|
||||
{truthyTy, truthyTy},
|
||||
{join(meet(truthyTy, builtinTypes->truthyType), truthyTy)}
|
||||
);
|
||||
TypeId actualFunctionTy = fn({truthyTy, truthyTy}, {join(meet(truthyTy, builtinTypes->truthyType), truthyTy)});
|
||||
|
||||
SubtypingResult result = isSubtype(genericFunctionTy, actualFunctionTy);
|
||||
|
||||
|
@ -1650,7 +1644,7 @@ TEST_CASE_FIXTURE(SubtypeFixture, "substitute_a_generic_for_a_negation")
|
|||
|
||||
TEST_CASE_FIXTURE(SubtypeFixture, "free_types_might_be_subtypes")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::LuauEagerGeneralization, true};
|
||||
ScopedFastFlag sff{FFlag::LuauEagerGeneralization2, true};
|
||||
|
||||
TypeId argTy = arena.freshType(builtinTypes, moduleScope.get());
|
||||
FreeType* freeArg = getMutable<FreeType>(argTy);
|
||||
|
|
|
@ -878,7 +878,7 @@ TEST_CASE_FIXTURE(Fixture, "tostring_unsee_ttv_if_array")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "tostring_error_mismatch")
|
||||
{
|
||||
ScopedFastFlag _ {FFlag::LuauTableLiteralSubtypeSpecificCheck, true};
|
||||
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
|
@ -889,26 +889,26 @@ TEST_CASE_FIXTURE(Fixture, "tostring_error_mismatch")
|
|||
|
||||
std::string expected;
|
||||
if (FFlag::LuauSolverV2)
|
||||
expected = "Type\n\t"
|
||||
"'{ a: number, b: string, c: { d: string } }'\n"
|
||||
"could not be converted into\n\t"
|
||||
"'{ a: number, b: string, c: { d: number } }'; \n"
|
||||
"this is because accessing `c.d` results in `string` in the former type and `number` in the latter "
|
||||
"type, and `string` is not exactly `number`";
|
||||
expected = "Type\n\t"
|
||||
"'{ a: number, b: string, c: { d: string } }'\n"
|
||||
"could not be converted into\n\t"
|
||||
"'{ a: number, b: string, c: { d: number } }'; \n"
|
||||
"this is because accessing `c.d` results in `string` in the former type and `number` in the latter "
|
||||
"type, and `string` is not exactly `number`";
|
||||
else
|
||||
expected = "Type\n\t"
|
||||
"'{| a: number, b: string, c: {| d: string |} |}'\n"
|
||||
"could not be converted into\n\t"
|
||||
"'{| a: number, b: string, c: {| d: number |} |}'\n"
|
||||
"caused by:\n "
|
||||
"Property 'c' is not compatible.\n"
|
||||
"Type\n\t"
|
||||
"'{| d: string |}'\n"
|
||||
"could not be converted into\n\t"
|
||||
"'{| d: number |}'\n"
|
||||
"caused by:\n "
|
||||
"Property 'd' is not compatible.\n"
|
||||
"Type 'string' could not be converted into 'number' in an invariant context";
|
||||
expected = "Type\n\t"
|
||||
"'{| a: number, b: string, c: {| d: string |} |}'\n"
|
||||
"could not be converted into\n\t"
|
||||
"'{| a: number, b: string, c: {| d: number |} |}'\n"
|
||||
"caused by:\n "
|
||||
"Property 'c' is not compatible.\n"
|
||||
"Type\n\t"
|
||||
"'{| d: string |}'\n"
|
||||
"could not be converted into\n\t"
|
||||
"'{| d: number |}'\n"
|
||||
"caused by:\n "
|
||||
"Property 'd' is not compatible.\n"
|
||||
"Type 'string' could not be converted into 'number' in an invariant context";
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
std::string actual = toString(result.errors[0]);
|
||||
|
|
|
@ -14,7 +14,7 @@ using namespace Luau;
|
|||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_DYNAMIC_FASTINT(LuauTypeFamilyApplicationCartesianProductLimit)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||
LUAU_FASTFLAG(LuauHasPropProperBlock)
|
||||
LUAU_FASTFLAG(LuauSimplifyOutOfLine)
|
||||
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
|
||||
|
@ -1725,7 +1725,7 @@ struct TFFixture
|
|||
TypeFunctionRuntime runtime{NotNull{&ice}, NotNull{&limits}};
|
||||
|
||||
const ScopedFastFlag sff[1] = {
|
||||
{FFlag::LuauEagerGeneralization, true},
|
||||
{FFlag::LuauEagerGeneralization2, true},
|
||||
};
|
||||
|
||||
BuiltinTypeFunctions builtinTypeFunctions;
|
||||
|
@ -1746,9 +1746,7 @@ TEST_CASE_FIXTURE(TFFixture, "refine<G, ~(false?)>")
|
|||
{
|
||||
TypeId g = arena->addType(GenericType{globalScope.get(), Polarity::Negative});
|
||||
|
||||
TypeId refineTy = arena->addType(TypeFunctionInstanceType{
|
||||
builtinTypeFunctions.refineFunc, {g, builtinTypes->truthyType}
|
||||
});
|
||||
TypeId refineTy = arena->addType(TypeFunctionInstanceType{builtinTypeFunctions.refineFunc, {g, builtinTypes->truthyType}});
|
||||
|
||||
FunctionGraphReductionResult res = reduceTypeFunctions(refineTy, Location{}, tfc);
|
||||
|
||||
|
@ -1764,9 +1762,7 @@ TEST_CASE_FIXTURE(TFFixture, "or<'a, 'b>")
|
|||
TypeId aType = arena->freshType(builtinTypes, globalScope.get());
|
||||
TypeId bType = arena->freshType(builtinTypes, globalScope.get());
|
||||
|
||||
TypeId orType = arena->addType(TypeFunctionInstanceType{
|
||||
builtinTypeFunctions.orFunc, {aType, bType}
|
||||
});
|
||||
TypeId orType = arena->addType(TypeFunctionInstanceType{builtinTypeFunctions.orFunc, {aType, bType}});
|
||||
|
||||
FunctionGraphReductionResult res = reduceTypeFunctions(orType, Location{}, tfc);
|
||||
|
||||
|
|
|
@ -9,8 +9,9 @@ using namespace Luau;
|
|||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
|
||||
LUAU_FASTFLAG(LuauUserTypeFunctionAliases)
|
||||
|
||||
TEST_SUITE_BEGIN("UserDefinedTypeFunctionTests");
|
||||
|
||||
|
@ -1987,7 +1988,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_singleton_equality_bool")
|
|||
{
|
||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
// FIXME: CLI-151985
|
||||
// This test breaks because we can't see that eq<type?, b> is already fully reduced.
|
||||
|
@ -2010,7 +2011,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_singleton_equality_string")
|
|||
{
|
||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
// FIXME: CLI-151985
|
||||
// This test breaks because we can't see that eq<type?, b> is already fully reduced.
|
||||
|
@ -2225,4 +2226,203 @@ TEST_CASE_FIXTURE(Fixture, "typeof_is_not_a_valid_type_function_name")
|
|||
CHECK("typeof cannot be used as an identifier for a type function or alias" == toString(result.errors[0]));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "type_alias_call")
|
||||
{
|
||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||
ScopedFastFlag luauUserTypeFunctionAliases{FFlag::LuauUserTypeFunctionAliases, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Test<T> = T?
|
||||
|
||||
type function foo(t)
|
||||
return Test(t)
|
||||
end
|
||||
|
||||
local x: foo<{a: number}> = { a = 2 }
|
||||
local y: foo<{b: number}> = { b = 2 }
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
CHECK(toString(requireType("x"), ToStringOptions{true}) == "{ a: number }?");
|
||||
CHECK(toString(requireType("y"), ToStringOptions{true}) == "{ b: number }?");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "type_alias_values")
|
||||
{
|
||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||
ScopedFastFlag luauUserTypeFunctionAliases{FFlag::LuauUserTypeFunctionAliases, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Test = { a: number }
|
||||
|
||||
type function foo(t)
|
||||
return types.unionof(Test, t)
|
||||
end
|
||||
|
||||
local x: foo<nil> = { a = 2 }
|
||||
local y: foo<string> = "a"
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
CHECK(toString(requireType("x"), ToStringOptions{true}) == "{ a: number }?");
|
||||
CHECK(toString(requireType("y"), ToStringOptions{true}) == "string | { a: number }");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "type_alias_call_with_reduction")
|
||||
{
|
||||
if (!FFlag::LuauSolverV2)
|
||||
return;
|
||||
|
||||
ScopedFastFlag luauUserTypeFunctionAliases{FFlag::LuauUserTypeFunctionAliases, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Test<T> = rawget<T, "a">
|
||||
|
||||
type function foo(t)
|
||||
return Test(t)
|
||||
end
|
||||
|
||||
local x: foo<{ a: number }> = 2
|
||||
local y: foo<{ a: string }> = "x"
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
CHECK(toString(requireType("x"), ToStringOptions{true}) == "number");
|
||||
CHECK(toString(requireType("y"), ToStringOptions{true}) == "string");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "type_alias_implicit_export")
|
||||
{
|
||||
if (!FFlag::LuauSolverV2)
|
||||
return;
|
||||
|
||||
ScopedFastFlag luauUserTypeFunctionAliases{FFlag::LuauUserTypeFunctionAliases, true};
|
||||
|
||||
fileResolver.source["game/A"] = R"(
|
||||
type Test<T> = rawget<T, "a">
|
||||
|
||||
export type function foo(t)
|
||||
return Test(t)
|
||||
end
|
||||
local x: foo<{ a: number }> = 2
|
||||
return {}
|
||||
)";
|
||||
|
||||
CheckResult aResult = frontend.check("game/A");
|
||||
LUAU_REQUIRE_NO_ERRORS(aResult);
|
||||
|
||||
CHECK(toString(requireType("game/A", "x")) == R"(number)");
|
||||
|
||||
CheckResult bResult = check(R"(
|
||||
local Test = require(game.A);
|
||||
local y: Test.foo<{ a: string }> = "x"
|
||||
)");
|
||||
LUAU_REQUIRE_NO_ERRORS(bResult);
|
||||
|
||||
CHECK(toString(requireType("y")) == R"(string)");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ExternTypeFixture, "type_alias_not_too_many_globals")
|
||||
{
|
||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||
ScopedFastFlag luauUserTypeFunctionAliases{FFlag::LuauUserTypeFunctionAliases, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type function get()
|
||||
return number
|
||||
end
|
||||
local function ok(idx: get<>): number return idx end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(5, result);
|
||||
CHECK(toString(result.errors[0]) == R"(Unknown global 'number')");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ExternTypeFixture, "type_alias_not_enough_arguments")
|
||||
{
|
||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||
ScopedFastFlag luauUserTypeFunctionAliases{FFlag::LuauUserTypeFunctionAliases, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Test<A, B> = (a: A, b: B) -> A
|
||||
|
||||
type function get()
|
||||
return Test(types.number)
|
||||
end
|
||||
|
||||
local function ok(idx: get<>): number return idx end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
||||
CHECK(toString(result.errors[0]) == R"('get' type function errored at runtime: [string "get"]:5: not enough arguments to call)");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "type_alias_can_call_packs")
|
||||
{
|
||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||
ScopedFastFlag luauUserTypeFunctionAliases{FFlag::LuauUserTypeFunctionAliases, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Test<T, U...> = (U...) -> T
|
||||
|
||||
type function foo(t)
|
||||
return Test(types.number, types.string, t)
|
||||
end
|
||||
|
||||
local x: foo<boolean>
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
CHECK(toString(requireType("x"), ToStringOptions{true}) == "(string, boolean) -> number");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ExternTypeFixture, "type_alias_reduction_errors")
|
||||
{
|
||||
if (!FFlag::LuauSolverV2)
|
||||
return;
|
||||
|
||||
ScopedFastFlag luauUserTypeFunctionAliases{FFlag::LuauUserTypeFunctionAliases, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Test<T, U> = setmetatable<T, U>
|
||||
|
||||
type function get()
|
||||
return Test(types.number, types.string)
|
||||
end
|
||||
|
||||
local function ok(idx: get<>): number return idx end
|
||||
)");
|
||||
|
||||
// TODO: type solving fails to complete in this test because of the blocked NameConstraint on the 'Test' alias
|
||||
LUAU_REQUIRE_ERROR_COUNT(5, result);
|
||||
CHECK(
|
||||
toString(result.errors[1]) ==
|
||||
R"('get' type function errored at runtime: [string "get"]:5: failed to reduce type function with: Type function instance setmetatable<number, string> is uninhabited)"
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "type_alias_unreferenced_do_not_block")
|
||||
{
|
||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||
ScopedFastFlag luauUserTypeFunctionAliases{FFlag::LuauUserTypeFunctionAliases, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type function foo(t)
|
||||
return types.unionof(types.number, t)
|
||||
end
|
||||
|
||||
type Test = foo<string>
|
||||
|
||||
local x: foo<boolean>
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
CHECK(toString(requireType("x"), ToStringOptions{true}) == "boolean | number");
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
using namespace Luau;
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauRetainDefinitionAliasLocations)
|
||||
LUAU_FASTFLAG(LuauNewNonStrictVisitTypes2)
|
||||
LUAU_FASTFLAG(LuauGuardAgainstMalformedTypeAliasExpansion2)
|
||||
LUAU_FASTFLAG(LuauSkipMalformedTypeAliases)
|
||||
|
@ -1217,8 +1216,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "gh1632_no_infinite_recursion_in_normalizatio
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "exported_alias_location_is_accessible_on_module")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::LuauRetainDefinitionAliasLocations, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
export type Value = string
|
||||
)");
|
||||
|
@ -1235,7 +1232,6 @@ TEST_CASE_FIXTURE(Fixture, "exported_type_function_location_is_accessible_on_mod
|
|||
{
|
||||
ScopedFastFlag flags[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauRetainDefinitionAliasLocations, true},
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
|
|
|
@ -356,7 +356,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "replace_every_free_type_when_unifying_a_comp
|
|||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
CHECK_EQ("any", toString(requireType("b")));
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "call_to_any_yields_any")
|
||||
|
|
|
@ -11,7 +11,7 @@ using namespace Luau;
|
|||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauTableCloneClonesType3)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||
LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments)
|
||||
|
||||
TEST_SUITE_BEGIN("BuiltinTests");
|
||||
|
@ -146,20 +146,19 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "sort_with_bad_predicate")
|
|||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'(number, number) -> boolean'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'((string, string) -> boolean)?'"
|
||||
"\ncaused by:\n"
|
||||
" None of the union options are compatible. For example:\n"
|
||||
"Type\n\t"
|
||||
"'(number, number) -> boolean'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(string, string) -> boolean'"
|
||||
"\ncaused by:\n"
|
||||
" Argument #1 type is not compatible.\n"
|
||||
"Type 'string' could not be converted into 'number'";
|
||||
const std::string expected = "Type\n\t"
|
||||
"'(number, number) -> boolean'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'((string, string) -> boolean)?'"
|
||||
"\ncaused by:\n"
|
||||
" None of the union options are compatible. For example:\n"
|
||||
"Type\n\t"
|
||||
"'(number, number) -> boolean'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(string, string) -> boolean'"
|
||||
"\ncaused by:\n"
|
||||
" Argument #1 type is not compatible.\n"
|
||||
"Type 'string' could not be converted into 'number'";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
|
@ -460,7 +459,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_pack_reduce")
|
|||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauEagerGeneralization2)
|
||||
CHECK("{ [number]: string | string | string, n: number }" == toString(requireType("t")));
|
||||
else if (FFlag::LuauSolverV2)
|
||||
CHECK_EQ("{ [number]: string, n: number }", toString(requireType("t")));
|
||||
|
|
|
@ -822,7 +822,8 @@ TEST_CASE_FIXTURE(ExternTypeFixture, "cannot_index_a_class_with_no_indexer")
|
|||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
CHECK_MESSAGE(
|
||||
get<DynamicPropertyLookupOnExternTypesUnsafe>(result.errors[0]), "Expected DynamicPropertyLookupOnExternTypesUnsafe but got " << result.errors[0]
|
||||
get<DynamicPropertyLookupOnExternTypesUnsafe>(result.errors[0]),
|
||||
"Expected DynamicPropertyLookupOnExternTypesUnsafe but got " << result.errors[0]
|
||||
);
|
||||
|
||||
CHECK(builtinTypes->errorType == requireType("c"));
|
||||
|
|
|
@ -22,7 +22,7 @@ LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
|||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTINT(LuauTarjanChildLimit)
|
||||
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||
LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments)
|
||||
LUAU_FASTFLAG(LuauHasPropProperBlock)
|
||||
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
||||
|
@ -1519,13 +1519,12 @@ local b: B = a
|
|||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'(number, number) -> string'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number) -> string'"
|
||||
"\ncaused by:\n"
|
||||
" Argument count mismatch. Function expects 2 arguments, but only 1 is specified";
|
||||
const std::string expected = "Type\n\t"
|
||||
"'(number, number) -> string'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number) -> string'"
|
||||
"\ncaused by:\n"
|
||||
" Argument count mismatch. Function expects 2 arguments, but only 1 is specified";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
|
@ -1543,14 +1542,13 @@ local b: B = a
|
|||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'(number, number) -> string'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number, string) -> string'"
|
||||
"\ncaused by:\n"
|
||||
" Argument #2 type is not compatible.\n"
|
||||
"Type 'string' could not be converted into 'number'";
|
||||
const std::string expected = "Type\n\t"
|
||||
"'(number, number) -> string'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number, string) -> string'"
|
||||
"\ncaused by:\n"
|
||||
" Argument #2 type is not compatible.\n"
|
||||
"Type 'string' could not be converted into 'number'";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
|
@ -1568,13 +1566,12 @@ local b: B = a
|
|||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'(number, number) -> number'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number, number) -> (number, boolean)'"
|
||||
"\ncaused by:\n"
|
||||
" Function only returns 1 value, but 2 are required here";
|
||||
const std::string expected = "Type\n\t"
|
||||
"'(number, number) -> number'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number, number) -> (number, boolean)'"
|
||||
"\ncaused by:\n"
|
||||
" Function only returns 1 value, but 2 are required here";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
|
@ -1592,14 +1589,13 @@ local b: B = a
|
|||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'(number, number) -> string'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number, number) -> number'"
|
||||
"\ncaused by:\n"
|
||||
" Return type is not compatible.\n"
|
||||
"Type 'string' could not be converted into 'number'";
|
||||
const std::string expected = "Type\n\t"
|
||||
"'(number, number) -> string'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number, number) -> number'"
|
||||
"\ncaused by:\n"
|
||||
" Return type is not compatible.\n"
|
||||
"Type 'string' could not be converted into 'number'";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
|
@ -1617,14 +1613,13 @@ local b: B = a
|
|||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'(number, number) -> (number, string)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number, number) -> (number, boolean)'"
|
||||
"\ncaused by:\n"
|
||||
" Return #2 type is not compatible.\n"
|
||||
"Type 'string' could not be converted into 'boolean'";
|
||||
const std::string expected = "Type\n\t"
|
||||
"'(number, number) -> (number, string)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number, number) -> (number, boolean)'"
|
||||
"\ncaused by:\n"
|
||||
" Return #2 type is not compatible.\n"
|
||||
"Type 'string' could not be converted into 'boolean'";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
|
@ -1688,7 +1683,7 @@ t.f = function(x)
|
|||
end
|
||||
)");
|
||||
|
||||
if (FFlag::LuauEagerGeneralization && FFlag::LuauSolverV2)
|
||||
if (FFlag::LuauEagerGeneralization2 && FFlag::LuauSolverV2)
|
||||
{
|
||||
// FIXME CLI-151985
|
||||
LUAU_CHECK_ERROR_COUNT(3, result);
|
||||
|
@ -1773,7 +1768,7 @@ t.f = function(x)
|
|||
end
|
||||
)");
|
||||
|
||||
if (FFlag::LuauEagerGeneralization && FFlag::LuauSolverV2)
|
||||
if (FFlag::LuauEagerGeneralization2 && FFlag::LuauSolverV2)
|
||||
{
|
||||
// FIXME CLI-151985
|
||||
LUAU_CHECK_ERROR_COUNT(2, result);
|
||||
|
@ -1950,10 +1945,7 @@ TEST_CASE_FIXTURE(Fixture, "free_is_not_bound_to_unknown")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "dont_infer_parameter_types_for_functions_from_their_call_site")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauHasPropProperBlock, true},
|
||||
{FFlag::LuauOptimizeFalsyAndTruthyIntersect, true}
|
||||
};
|
||||
ScopedFastFlag sffs[] = {{FFlag::LuauHasPropProperBlock, true}, {FFlag::LuauOptimizeFalsyAndTruthyIntersect, true}};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local t = {}
|
||||
|
@ -1975,7 +1967,7 @@ TEST_CASE_FIXTURE(Fixture, "dont_infer_parameter_types_for_functions_from_their_
|
|||
|
||||
CHECK_EQ("<a>(a) -> a", toString(requireType("f")));
|
||||
|
||||
if (FFlag::LuauEagerGeneralization && FFlag::LuauSolverV2)
|
||||
if (FFlag::LuauEagerGeneralization2 && FFlag::LuauSolverV2)
|
||||
{
|
||||
LUAU_CHECK_NO_ERRORS(result);
|
||||
CHECK("<a>({ read p: { read q: a } }) -> (a & ~(false?))?" == toString(requireType("g")));
|
||||
|
@ -2940,7 +2932,7 @@ TEST_CASE_FIXTURE(Fixture, "unifier_should_not_bind_free_types")
|
|||
{
|
||||
// The new solver should ideally be able to do better here, but this is no worse than the old solver.
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
auto tm1 = get<TypeMismatch>(result.errors[0]);
|
||||
|
|
|
@ -357,13 +357,12 @@ TEST_CASE_FIXTURE(Fixture, "table_intersection_write_sealed_indirect")
|
|||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
||||
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'(string, number) -> string'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(string) -> string'\n"
|
||||
"caused by:\n"
|
||||
" Argument count mismatch. Function expects 2 arguments, but only 1 is specified";
|
||||
const std::string expected = "Type\n\t"
|
||||
"'(string, number) -> string'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(string) -> string'\n"
|
||||
"caused by:\n"
|
||||
" Argument count mismatch. Function expects 2 arguments, but only 1 is specified";
|
||||
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
CHECK_EQ(toString(result.errors[1]), "Cannot add property 'z' to table 'X & Y'");
|
||||
|
@ -389,13 +388,12 @@ TEST_CASE_FIXTURE(Fixture, "table_write_sealed_indirect")
|
|||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'(string, number) -> string'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(string) -> string'\n"
|
||||
"caused by:\n"
|
||||
" Argument count mismatch. Function expects 2 arguments, but only 1 is specified";
|
||||
const std::string expected = "Type\n\t"
|
||||
"'(string, number) -> string'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(string) -> string'\n"
|
||||
"caused by:\n"
|
||||
" Argument count mismatch. Function expects 2 arguments, but only 1 is specified";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
|
||||
CHECK_EQ(toString(result.errors[1]), "Cannot add property 'z' to table 'XY'");
|
||||
|
@ -428,15 +426,14 @@ local a: XYZ = 3
|
|||
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
const std::string expected =
|
||||
"Type "
|
||||
"'number'"
|
||||
" could not be converted into "
|
||||
"'X & Y & Z'; \n"
|
||||
"this is because \n\t"
|
||||
" * the 1st component of the intersection is `X`, and `number` is not a subtype of `X`\n\t"
|
||||
" * the 2nd component of the intersection is `Y`, and `number` is not a subtype of `Y`\n\t"
|
||||
" * the 3rd component of the intersection is `Z`, and `number` is not a subtype of `Z`";
|
||||
const std::string expected = "Type "
|
||||
"'number'"
|
||||
" could not be converted into "
|
||||
"'X & Y & Z'; \n"
|
||||
"this is because \n\t"
|
||||
" * the 1st component of the intersection is `X`, and `number` is not a subtype of `X`\n\t"
|
||||
" * the 2nd component of the intersection is `Y`, and `number` is not a subtype of `Y`\n\t"
|
||||
" * the 3rd component of the intersection is `Z`, and `number` is not a subtype of `Z`";
|
||||
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
@ -527,14 +524,13 @@ TEST_CASE_FIXTURE(Fixture, "intersect_bool_and_false")
|
|||
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
const std::string expected =
|
||||
"Type "
|
||||
"'boolean & false'"
|
||||
" could not be converted into "
|
||||
"'true'; \n"
|
||||
"this is because \n\t"
|
||||
" * the 1st component of the intersection is `boolean`, which is not a subtype of `true`\n\t"
|
||||
" * the 2nd component of the intersection is `false`, which is not a subtype of `true`";
|
||||
const std::string expected = "Type "
|
||||
"'boolean & false'"
|
||||
" could not be converted into "
|
||||
"'true'; \n"
|
||||
"this is because \n\t"
|
||||
" * the 1st component of the intersection is `boolean`, which is not a subtype of `true`\n\t"
|
||||
" * the 2nd component of the intersection is `false`, which is not a subtype of `true`";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
|
@ -557,15 +553,14 @@ TEST_CASE_FIXTURE(Fixture, "intersect_false_and_bool_and_false")
|
|||
// TODO: odd stringification of `false & (boolean & false)`.)
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
const std::string expected =
|
||||
"Type "
|
||||
"'boolean & false & false'"
|
||||
" could not be converted into "
|
||||
"'true'; \n"
|
||||
"this is because \n\t"
|
||||
" * the 1st component of the intersection is `false`, which is not a subtype of `true`\n\t"
|
||||
" * the 2nd component of the intersection is `boolean`, which is not a subtype of `true`\n\t"
|
||||
" * the 3rd component of the intersection is `false`, which is not a subtype of `true`";
|
||||
const std::string expected = "Type "
|
||||
"'boolean & false & false'"
|
||||
" could not be converted into "
|
||||
"'true'; \n"
|
||||
"this is because \n\t"
|
||||
" * the 1st component of the intersection is `false`, which is not a subtype of `true`\n\t"
|
||||
" * the 2nd component of the intersection is `boolean`, which is not a subtype of `true`\n\t"
|
||||
" * the 3rd component of the intersection is `false`, which is not a subtype of `true`";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
|
@ -640,13 +635,11 @@ TEST_CASE_FIXTURE(Fixture, "union_saturate_overloaded_functions")
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'((number) -> number) & ((string) -> string)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(boolean | number) -> boolean | number'; none of the intersection parts are compatible";
|
||||
const std::string expected = "Type\n\t"
|
||||
"'((number) -> number) & ((string) -> string)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(boolean | number) -> boolean | number'; none of the intersection parts are compatible";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "intersection_of_tables")
|
||||
|
@ -662,16 +655,15 @@ TEST_CASE_FIXTURE(Fixture, "intersection_of_tables")
|
|||
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
const std::string expected =
|
||||
"Type "
|
||||
"'{ p: number?, q: number?, r: number? } & { p: number?, q: string? }'"
|
||||
" could not be converted into "
|
||||
"'{ p: nil }'; \n"
|
||||
"this is because \n\t"
|
||||
" * in the 1st component of the intersection, accessing `p` has the 1st component of the union as `number` and "
|
||||
"accessing `p` results in `nil`, and `number` is not exactly `nil`\n\t"
|
||||
" * in the 2nd component of the intersection, accessing `p` has the 1st component of the union as `number` and "
|
||||
"accessing `p` results in `nil`, and `number` is not exactly `nil`";
|
||||
const std::string expected = "Type "
|
||||
"'{ p: number?, q: number?, r: number? } & { p: number?, q: string? }'"
|
||||
" could not be converted into "
|
||||
"'{ p: nil }'; \n"
|
||||
"this is because \n\t"
|
||||
" * in the 1st component of the intersection, accessing `p` has the 1st component of the union as `number` and "
|
||||
"accessing `p` results in `nil`, and `number` is not exactly `nil`\n\t"
|
||||
" * in the 2nd component of the intersection, accessing `p` has the 1st component of the union as `number` and "
|
||||
"accessing `p` results in `nil`, and `number` is not exactly `nil`";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
|
@ -696,24 +688,23 @@ TEST_CASE_FIXTURE(Fixture, "intersection_of_tables_with_top_properties")
|
|||
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'{ p: number?, q: any } & { p: unknown, q: string? }'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'{ p: string?, q: number? }'; \n"
|
||||
"this is because \n\t"
|
||||
" * in the 1st component of the intersection, accessing `p` has the 1st component of the union as `number` and "
|
||||
"accessing `p` results in `string?`, and `number` is not exactly `string?`\n\t"
|
||||
" * in the 1st component of the intersection, accessing `p` results in `number?` and accessing `p` has the 1st "
|
||||
"component of the union as `string`, and `number?` is not exactly `string`\n\t"
|
||||
" * in the 1st component of the intersection, accessing `q` results in `any` and accessing `q` results in "
|
||||
"`number?`, and `any` is not exactly `number?`\n\t"
|
||||
" * in the 2nd component of the intersection, accessing `p` results in `unknown` and accessing `p` results in "
|
||||
"`string?`, and `unknown` is not exactly `string?`\n\t"
|
||||
" * in the 2nd component of the intersection, accessing `q` has the 1st component of the union as `string` and "
|
||||
"accessing `q` results in `number?`, and `string` is not exactly `number?`\n\t"
|
||||
" * in the 2nd component of the intersection, accessing `q` results in `string?` and accessing `q` has the 1st "
|
||||
"component of the union as `number`, and `string?` is not exactly `number`";
|
||||
const std::string expected = "Type\n\t"
|
||||
"'{ p: number?, q: any } & { p: unknown, q: string? }'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'{ p: string?, q: number? }'; \n"
|
||||
"this is because \n\t"
|
||||
" * in the 1st component of the intersection, accessing `p` has the 1st component of the union as `number` and "
|
||||
"accessing `p` results in `string?`, and `number` is not exactly `string?`\n\t"
|
||||
" * in the 1st component of the intersection, accessing `p` results in `number?` and accessing `p` has the 1st "
|
||||
"component of the union as `string`, and `number?` is not exactly `string`\n\t"
|
||||
" * in the 1st component of the intersection, accessing `q` results in `any` and accessing `q` results in "
|
||||
"`number?`, and `any` is not exactly `number?`\n\t"
|
||||
" * in the 2nd component of the intersection, accessing `p` results in `unknown` and accessing `p` results in "
|
||||
"`string?`, and `unknown` is not exactly `string?`\n\t"
|
||||
" * in the 2nd component of the intersection, accessing `q` has the 1st component of the union as `string` and "
|
||||
"accessing `q` results in `number?`, and `string` is not exactly `number?`\n\t"
|
||||
" * in the 2nd component of the intersection, accessing `q` results in `string?` and accessing `q` has the 1st "
|
||||
"component of the union as `number`, and `string?` is not exactly `number`";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
|
@ -924,9 +915,9 @@ TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_unknown_result")
|
|||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
const std::string expected = "Type\n\t"
|
||||
"'((nil) -> unknown) & ((number) -> number)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number?) -> number?'; none of the intersection parts are compatible";
|
||||
"'((nil) -> unknown) & ((number) -> number)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number?) -> number?'; none of the intersection parts are compatible";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
|
@ -946,11 +937,10 @@ TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_unknown_arguments")
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'((number) -> number?) & ((unknown) -> string?)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number?) -> nil'; none of the intersection parts are compatible";
|
||||
const std::string expected = "Type\n\t"
|
||||
"'((number) -> number?) & ((unknown) -> string?)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number?) -> nil'; none of the intersection parts are compatible";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
|
@ -1075,9 +1065,9 @@ TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_overlapping_results_and_
|
|||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
const std::string expected = "Type\n\t"
|
||||
"'((number?) -> (...number)) & ((string?) -> number | string)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number | string) -> (number, number?)'; none of the intersection parts are compatible";
|
||||
"'((number?) -> (...number)) & ((string?) -> number | string)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number | string) -> (number, number?)'; none of the intersection parts are compatible";
|
||||
CHECK(expected == toString(result.errors[0]));
|
||||
}
|
||||
|
||||
|
@ -1172,16 +1162,15 @@ TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_weird_typepacks_4")
|
|||
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'((a...) -> ()) & ((number, a...) -> number)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'((a...) -> ()) & ((number, a...) -> number)'; \n"
|
||||
"this is because \n\t"
|
||||
" * in the 1st component of the intersection, the function returns is `()` in the former type and `number` in "
|
||||
"the latter type, and `()` is not a subtype of `number`\n\t"
|
||||
" * in the 2nd component of the intersection, the function takes a tail of `a...` and in the 1st component of "
|
||||
"the intersection, the function takes a tail of `a...`, and `a...` is not a supertype of `a...`";
|
||||
const std::string expected = "Type\n\t"
|
||||
"'((a...) -> ()) & ((number, a...) -> number)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'((a...) -> ()) & ((number, a...) -> number)'; \n"
|
||||
"this is because \n\t"
|
||||
" * in the 1st component of the intersection, the function returns is `()` in the former type and `number` in "
|
||||
"the latter type, and `()` is not a subtype of `number`\n\t"
|
||||
" * in the 2nd component of the intersection, the function takes a tail of `a...` and in the 1st component of "
|
||||
"the intersection, the function takes a tail of `a...`, and `a...` is not a supertype of `a...`";
|
||||
CHECK(expected == toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
|
|
|
@ -1519,7 +1519,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "repeat_is_linearish")
|
|||
)"));
|
||||
|
||||
CHECK_EQ("nil", toString(requireType("y")));
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "ensure_local_in_loop_does_not_escape")
|
||||
|
@ -1539,7 +1538,6 @@ TEST_CASE_FIXTURE(Fixture, "ensure_local_in_loop_does_not_escape")
|
|||
)"));
|
||||
|
||||
CHECK_EQ("number", toString(requireType("y")));
|
||||
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
||||
LUAU_FASTFLAG(LuauClipVariadicAnysFromArgsToGenericFuncs2)
|
||||
|
||||
|
@ -467,10 +467,9 @@ local b: B.T = a
|
|||
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
const std::string expected =
|
||||
"Type 'T' from 'game/A' could not be converted into 'T' from 'game/B'; \n"
|
||||
"this is because accessing `x` results in `number` in the former type and `string` in the latter type, and "
|
||||
"`number` is not exactly `string`";
|
||||
const std::string expected = "Type 'T' from 'game/A' could not be converted into 'T' from 'game/B'; \n"
|
||||
"this is because accessing `x` results in `number` in the former type and `string` in the latter type, and "
|
||||
"`number` is not exactly `string`";
|
||||
CHECK(expected == toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
|
@ -514,10 +513,9 @@ local b: B.T = a
|
|||
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
const std::string expected =
|
||||
"Type 'T' from 'game/B' could not be converted into 'T' from 'game/C'; \n"
|
||||
"this is because accessing `x` results in `number` in the former type and `string` in the latter type, and "
|
||||
"`number` is not exactly `string`";
|
||||
const std::string expected = "Type 'T' from 'game/B' could not be converted into 'T' from 'game/C'; \n"
|
||||
"this is because accessing `x` results in `number` in the former type and `string` in the latter type, and "
|
||||
"`number` is not exactly `string`";
|
||||
CHECK(expected == toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
|
@ -787,7 +785,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "leaky_generics")
|
|||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
CHECK_EQ("(unknown) -> unknown", toString(requireTypeAtPosition({13, 23})));
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
using namespace Luau;
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||
|
||||
TEST_SUITE_BEGIN("TypeInferOperators");
|
||||
|
||||
|
@ -29,7 +29,7 @@ TEST_CASE_FIXTURE(Fixture, "or_joins_types")
|
|||
)");
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
// FIXME: Regression
|
||||
CHECK("(string & ~(false?)) | number" == toString(*requireType("s")));
|
||||
|
@ -51,7 +51,7 @@ TEST_CASE_FIXTURE(Fixture, "or_joins_types_with_no_extras")
|
|||
)");
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
// FIXME: Regression.
|
||||
CHECK("(string & ~(false?)) | number" == toString(*requireType("s")));
|
||||
|
@ -72,7 +72,7 @@ TEST_CASE_FIXTURE(Fixture, "or_joins_types_with_no_superfluous_union")
|
|||
)");
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
// FIXME: Regression
|
||||
CHECK("(string & ~(false?)) | string" == toString(requireType("s")));
|
||||
|
@ -634,7 +634,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "typecheck_unary_minus_error")
|
|||
local a = -foo
|
||||
)");
|
||||
|
||||
if (FFlag::LuauEagerGeneralization && FFlag::LuauSolverV2)
|
||||
if (FFlag::LuauEagerGeneralization2 && FFlag::LuauSolverV2)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||
LUAU_FASTFLAG(LuauFunctionCallsAreNotNilable)
|
||||
LUAU_FASTFLAG(LuauWeakNilRefinementType)
|
||||
LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions)
|
||||
|
@ -770,12 +770,12 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "nonoptional_type_can_narrow_to_nil_if_sense_
|
|||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
CHECK("nil & string & unknown & unknown" == toString(requireTypeAtPosition({4, 24}))); // type(v) == "nil"
|
||||
CHECK("nil & string & unknown & unknown" == toString(requireTypeAtPosition({4, 24}))); // type(v) == "nil"
|
||||
CHECK("string & unknown & unknown & ~nil" == toString(requireTypeAtPosition({6, 24}))); // type(v) ~= "nil"
|
||||
|
||||
CHECK("nil & string & unknown & unknown" == toString(requireTypeAtPosition({10, 24}))); // equivalent to type(v) == "nil"
|
||||
CHECK("nil & string & unknown & unknown" == toString(requireTypeAtPosition({10, 24}))); // equivalent to type(v) == "nil"
|
||||
CHECK("string & unknown & unknown & ~nil" == toString(requireTypeAtPosition({12, 24}))); // equivalent to type(v) ~= "nil"
|
||||
}
|
||||
else
|
||||
|
@ -1273,7 +1273,10 @@ TEST_CASE_FIXTURE(Fixture, "discriminate_from_truthiness_of_x")
|
|||
// time, sometimes this is due to hitting the simplifier rather than
|
||||
// normalization.
|
||||
CHECK("{ tag: \"exists\", x: string } & { x: ~(false?) }" == toString(requireTypeAtPosition({5, 28})));
|
||||
CHECK(R"(({ tag: "exists", x: string } & { x: false? }) | ({ tag: "missing", x: nil } & { x: false? }))" == toString(requireTypeAtPosition({7, 28})));
|
||||
CHECK(
|
||||
R"(({ tag: "exists", x: string } & { x: false? }) | ({ tag: "missing", x: nil } & { x: false? }))" ==
|
||||
toString(requireTypeAtPosition({7, 28}))
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2514,7 +2517,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "remove_recursive_upper_bound_when_generalizi
|
|||
end
|
||||
)"));
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
CHECK_EQ("nil & string & unknown", toString(requireTypeAtPosition({4, 24})));
|
||||
else
|
||||
CHECK_EQ("nil", toString(requireTypeAtPosition({4, 24})));
|
||||
|
@ -2636,10 +2639,7 @@ TEST_CASE_FIXTURE(Fixture, "oss_1687_equality_shouldnt_leak_nil")
|
|||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1451")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauWeakNilRefinementType, true}
|
||||
};
|
||||
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauWeakNilRefinementType, true}};
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||
type Part = {
|
||||
|
@ -2682,10 +2682,17 @@ TEST_CASE_FIXTURE(RefinementExternTypeFixture, "cannot_call_a_function")
|
|||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
|
||||
CHECK_EQ(toString(result.errors[0]), "Key 'Disconnect' is missing from 't2 where t1 = ExternScriptConnection | t2 | { Disconnect: (t1) -> (...any) } ; t2 = { disconnect: (t1) -> (...any) }' in the type 't1 where t1 = ExternScriptConnection | { Disconnect: (t1) -> (...any) } | { disconnect: (t1) -> (...any) }'");
|
||||
CHECK_EQ(
|
||||
toString(result.errors[0]),
|
||||
"Key 'Disconnect' is missing from 't2 where t1 = ExternScriptConnection | t2 | { Disconnect: (t1) -> (...any) } ; t2 = { disconnect: "
|
||||
"(t1) -> (...any) }' in the type 't1 where t1 = ExternScriptConnection | { Disconnect: (t1) -> (...any) } | { disconnect: (t1) -> "
|
||||
"(...any) }'"
|
||||
);
|
||||
|
||||
if (FFlag::LuauBetterCannotCallFunctionPrimitive)
|
||||
CHECK_EQ(toString(result.errors[1]), "The type function is not precise enough for us to determine the appropriate result type of this call.");
|
||||
CHECK_EQ(
|
||||
toString(result.errors[1]), "The type function is not precise enough for us to determine the appropriate result type of this call."
|
||||
);
|
||||
else
|
||||
CHECK_EQ(toString(result.errors[1]), "Cannot call a value of type function");
|
||||
}
|
||||
|
@ -2694,7 +2701,9 @@ TEST_CASE_FIXTURE(RefinementExternTypeFixture, "cannot_call_a_function")
|
|||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::LuauBetterCannotCallFunctionPrimitive)
|
||||
CHECK_EQ(toString(result.errors[0]), "The type function is not precise enough for us to determine the appropriate result type of this call.");
|
||||
CHECK_EQ(
|
||||
toString(result.errors[0]), "The type function is not precise enough for us to determine the appropriate result type of this call."
|
||||
);
|
||||
else
|
||||
CHECK_EQ(toString(result.errors[0]), "Cannot call a value of type function");
|
||||
}
|
||||
|
|
|
@ -389,7 +389,8 @@ TEST_CASE_FIXTURE(Fixture, "table_properties_type_error_escapes")
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
const std::string expected = R"(Table type '{ ["\n"]: number }' not compatible with type '{ ["<>"]: number }' because the former is missing field '<>')";
|
||||
const std::string expected =
|
||||
R"(Table type '{ ["\n"]: number }' not compatible with type '{ ["<>"]: number }' because the former is missing field '<>')";
|
||||
CHECK(expected == toString(result.errors[0]));
|
||||
}
|
||||
|
||||
|
@ -460,11 +461,10 @@ TEST_CASE_FIXTURE(Fixture, "parametric_tagged_union_alias")
|
|||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
const std::string expectedError = "Type\n\t"
|
||||
"'{ result: string, success: boolean }'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'Err<number> | Ok<string>'";
|
||||
"'{ result: string, success: boolean }'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'Err<number> | Ok<string>'";
|
||||
CHECK(toString(result.errors[0]) == expectedError);
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "if_then_else_expression_singleton_options")
|
||||
|
|
|
@ -21,8 +21,8 @@ LUAU_FASTFLAG(LuauSolverV2)
|
|||
|
||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||
LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||
LUAU_FASTFLAG(DebugLuauAssertOnForcedConstraint)
|
||||
LUAU_FASTFLAG(LuauBidirectionalInferenceElideAssert)
|
||||
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
||||
|
@ -702,7 +702,7 @@ TEST_CASE_FIXTURE(Fixture, "indexers_get_quantified_too")
|
|||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauEagerGeneralization2)
|
||||
CHECK("<a>({a}) -> ()" == toString(requireType("swap")));
|
||||
else if (FFlag::LuauSolverV2)
|
||||
CHECK("({unknown}) -> ()" == toString(requireType("swap")));
|
||||
|
@ -925,9 +925,9 @@ TEST_CASE_FIXTURE(Fixture, "sealed_table_indexers_must_unify")
|
|||
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
std::string expected = "Type '{number}' could not be converted into '{string}'; \n"
|
||||
"this is because the result of indexing is `number` in the former type and `string` in the latter type, "
|
||||
"and `number` is not exactly `string`";
|
||||
std::string expected = "Type '{number}' could not be converted into '{string}'; \n"
|
||||
"this is because the result of indexing is `number` in the former type and `string` in the latter type, "
|
||||
"and `number` is not exactly `string`";
|
||||
auto actual = toString(result.errors[0]);
|
||||
CHECK_EQ(expected, actual);
|
||||
}
|
||||
|
@ -2379,7 +2379,7 @@ TEST_CASE_FIXTURE(Fixture, "invariant_table_properties_means_instantiating_table
|
|||
local c : string = t.m("hi")
|
||||
)");
|
||||
|
||||
if (FFlag::LuauEagerGeneralization && FFlag::LuauSolverV2)
|
||||
if (FFlag::LuauEagerGeneralization2 && FFlag::LuauSolverV2)
|
||||
{
|
||||
// FIXME CLI-151985
|
||||
LUAU_CHECK_ERROR_COUNT(2, result);
|
||||
|
@ -3749,7 +3749,7 @@ TEST_CASE_FIXTURE(Fixture, "scalar_is_not_a_subtype_of_a_compatible_polymorphic_
|
|||
{
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::LuauReportSubtypingErrors, true},
|
||||
{FFlag::LuauEagerGeneralization, true},
|
||||
{FFlag::LuauEagerGeneralization2, true},
|
||||
{FFlag::LuauSubtypeGenericsAndNegations, true},
|
||||
{FFlag::LuauNoMoreInjectiveTypeFunctions, true}
|
||||
};
|
||||
|
@ -4298,7 +4298,7 @@ TEST_CASE_FIXTURE(Fixture, "cli_84607_missing_prop_in_array_or_dict")
|
|||
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
for (const auto& err: result.errors)
|
||||
for (const auto& err : result.errors)
|
||||
{
|
||||
const auto* error = get<MissingProperties>(err);
|
||||
REQUIRE(error);
|
||||
|
@ -4644,9 +4644,7 @@ TEST_CASE_FIXTURE(Fixture, "table_writes_introduce_write_properties")
|
|||
return;
|
||||
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::LuauEagerGeneralization, true},
|
||||
{FFlag::LuauSubtypeGenericsAndNegations, true},
|
||||
{FFlag::LuauNoMoreInjectiveTypeFunctions, true}
|
||||
{FFlag::LuauEagerGeneralization2, true}, {FFlag::LuauSubtypeGenericsAndNegations, true}, {FFlag::LuauNoMoreInjectiveTypeFunctions, true}
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
|
@ -4698,7 +4696,7 @@ TEST_CASE_FIXTURE(Fixture, "refined_thing_can_be_an_array")
|
|||
end
|
||||
)");
|
||||
|
||||
if (FFlag::LuauSolverV2 && !FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauSolverV2 && !FFlag::LuauEagerGeneralization2)
|
||||
{
|
||||
LUAU_CHECK_ERROR_COUNT(1, result);
|
||||
LUAU_CHECK_ERROR(result, NotATable);
|
||||
|
@ -4746,7 +4744,7 @@ TEST_CASE_FIXTURE(Fixture, "parameter_was_set_an_indexer_and_bounded_by_another_
|
|||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
// FIXME CLI-114134. We need to simplify types more consistently.
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
CHECK("({number} & {number}, unknown) -> ()" == toString(requireType("f")));
|
||||
else
|
||||
CHECK_EQ("(unknown & {number} & {number}, unknown) -> ()", toString(requireType("f")));
|
||||
|
@ -5088,8 +5086,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "subtyping_with_a_metatable_table_path")
|
|||
if (!FFlag::LuauSolverV2)
|
||||
return;
|
||||
|
||||
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type self = {} & {}
|
||||
type Class = typeof(setmetatable())
|
||||
|
@ -5098,18 +5094,20 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "subtyping_with_a_metatable_table_path")
|
|||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
LUAU_REQUIRE_ERROR_COUNT(3, result);
|
||||
|
||||
// We shouldn't allow `setmetatable()` to type check
|
||||
CHECK_EQ(result.errors[0].location, Location{{2, 21}, {2, 43}});
|
||||
CHECK_EQ("Type function instance setmetatable<nil, nil> is uninhabited", toString(result.errors[0]));
|
||||
|
||||
CHECK_EQ(result.errors[1].location, Location{{3, 8}, {5, 11}});
|
||||
CHECK_EQ("Type function instance setmetatable<nil, nil> is uninhabited", toString(result.errors[1]));
|
||||
|
||||
CHECK_EQ(
|
||||
"Type pack '{ @metatable { }, { } & { } }' could not be converted into 'Class'; \n"
|
||||
"this is because \n\t"
|
||||
" * in the 1st entry in the type pack, the metatable portion is `{ }` in the former type and `nil` in the latter type, and `{ }` "
|
||||
"is not a subtype of `nil`\n\t"
|
||||
" * in the 1st entry in the type pack, the table portion has the 1st component of the intersection as `{ }` and in the 1st entry "
|
||||
"in the type pack, the table portion is `nil`, and `{ }` is not a subtype of `nil`\n\t"
|
||||
" * in the 1st entry in the type pack, the table portion has the 2nd component of the intersection as `{ }` and in the 1st entry "
|
||||
"in the type pack, the table portion is `nil`, and `{ }` is not a subtype of `nil`",
|
||||
toString(result.errors[0])
|
||||
"Type pack '{ @metatable { }, { } & { } }' could not be converted into 'setmetatable<nil, nil>'; \n"
|
||||
"this is because the 1st entry in the type pack is `{ @metatable { }, { } & { } }` and in the 1st entry in the type packreduces to "
|
||||
"`never`, and `{ @metatable { }, { } & { } }` is not a subtype of `never`",
|
||||
toString(result.errors[2])
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -5399,10 +5397,7 @@ TEST_CASE_FIXTURE(Fixture, "returning_mismatched_optional_in_table")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "optional_function_in_table")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}
|
||||
};
|
||||
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauTableLiteralSubtypeSpecificCheck, true}};
|
||||
|
||||
LUAU_CHECK_NO_ERRORS(check(R"(
|
||||
local t: { (() -> ())? } = {
|
||||
|
@ -5551,10 +5546,7 @@ TEST_CASE_FIXTURE(Fixture, "deeply_nested_classish_inference")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "bigger_nested_table_causes_big_type_error")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}
|
||||
};
|
||||
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauTableLiteralSubtypeSpecificCheck, true}};
|
||||
|
||||
auto result = check(R"(
|
||||
type File = {
|
||||
|
@ -5794,7 +5786,7 @@ TEST_CASE_FIXTURE(Fixture, "large_table_inference_does_not_bleed")
|
|||
local otherWords: { Word } = {"foo"}
|
||||
)");
|
||||
LUAU_REQUIRE_ERROR_COUNT(3, result);
|
||||
for (const auto& err: result.errors)
|
||||
for (const auto& err : result.errors)
|
||||
// Check that all of the errors are localized to `words`, not `otherWords`
|
||||
CHECK(err.location.begin.line == 2);
|
||||
}
|
||||
|
@ -5809,10 +5801,7 @@ TEST_CASE_FIXTURE(Fixture, "extremely_large_table" * doctest::timeout(2.0))
|
|||
{FFlag::LuauDisablePrimitiveInferenceInLargeTables, true},
|
||||
};
|
||||
|
||||
const std::string source =
|
||||
"local res = {\n" +
|
||||
rep("\"foo\",\n", 100'000) +
|
||||
"}";
|
||||
const std::string source = "local res = {\n" + rep("\"foo\",\n", 100'000) + "}";
|
||||
LUAU_REQUIRE_NO_ERRORS(check(source));
|
||||
CHECK_EQ("{string}", toString(requireType("res"), {true}));
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ LUAU_FASTFLAG(LuauTypeCheckerAcceptNumberConcats)
|
|||
LUAU_FASTFLAG(LuauPreprocessTypestatedArgument)
|
||||
LUAU_FASTFLAG(LuauMagicFreezeCheckBlocked2)
|
||||
LUAU_FASTFLAG(LuauNoMoreInjectiveTypeFunctions)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
||||
LUAU_FASTFLAG(LuauHasPropProperBlock)
|
||||
LUAU_FASTFLAG(LuauStringPartLengthLimit)
|
||||
|
@ -447,7 +447,7 @@ TEST_CASE_FIXTURE(Fixture, "check_expr_recursion_limit")
|
|||
#endif
|
||||
ScopedFastInt luauRecursionLimit{FInt::LuauRecursionLimit, limit + 100};
|
||||
ScopedFastInt luauCheckRecursionLimit{FInt::LuauCheckRecursionLimit, limit - 100};
|
||||
ScopedFastFlag _{FFlag::LuauEagerGeneralization, false};
|
||||
ScopedFastFlag _{FFlag::LuauEagerGeneralization2, false};
|
||||
|
||||
CheckResult result = check(R"(("foo"))" + rep(":lower()", limit));
|
||||
|
||||
|
@ -1990,10 +1990,7 @@ TEST_CASE_FIXTURE(Fixture, "assert_allows_singleton_union_or_intersection")
|
|||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "assert_table_freeze_constraint_solving")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauMagicFreezeCheckBlocked2, true}
|
||||
};
|
||||
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauMagicFreezeCheckBlocked2, true}};
|
||||
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||
local f = table.freeze
|
||||
f(table)
|
||||
|
@ -2002,10 +1999,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "assert_table_freeze_constraint_solving")
|
|||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_assert_table_freeze_constraint_solving")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauMagicFreezeCheckBlocked2, true}
|
||||
};
|
||||
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauMagicFreezeCheckBlocked2, true}};
|
||||
// This is the original fuzzer version of the above issue.
|
||||
CheckResult results = check(R"(
|
||||
local function l0()
|
||||
|
@ -2041,7 +2035,7 @@ TEST_CASE_FIXTURE(Fixture, "fuzz_generalize_one_remove_type_assert")
|
|||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauHasPropProperBlock, true},
|
||||
{FFlag::LuauEagerGeneralization, true},
|
||||
{FFlag::LuauEagerGeneralization2, true},
|
||||
{FFlag::LuauOptimizeFalsyAndTruthyIntersect, true},
|
||||
{FFlag::LuauSubtypeGenericsAndNegations, true},
|
||||
{FFlag::LuauNoMoreInjectiveTypeFunctions, true},
|
||||
|
@ -2079,7 +2073,7 @@ TEST_CASE_FIXTURE(Fixture, "fuzz_generalize_one_remove_type_assert_2")
|
|||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauEagerGeneralization, true},
|
||||
{FFlag::LuauEagerGeneralization2, true},
|
||||
{FFlag::LuauOptimizeFalsyAndTruthyIntersect, true},
|
||||
{FFlag::LuauSubtypeGenericsAndNegations, true},
|
||||
{FFlag::LuauNoMoreInjectiveTypeFunctions, true},
|
||||
|
@ -2116,7 +2110,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_simplify_combinatorial_explosion")
|
|||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauHasPropProperBlock, true},
|
||||
{FFlag::LuauEagerGeneralization, true},
|
||||
{FFlag::LuauEagerGeneralization2, true},
|
||||
{FFlag::LuauOptimizeFalsyAndTruthyIntersect, true},
|
||||
{FFlag::LuauStringPartLengthLimit, true},
|
||||
{FFlag::LuauSimplificationRecheckAssumption, true},
|
||||
|
|
|
@ -12,7 +12,7 @@ using namespace Luau;
|
|||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
|
||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||
LUAU_FASTFLAG(LuauReportSubtypingErrors)
|
||||
LUAU_FASTFLAG(LuauTrackInferredFunctionTypeFromCall)
|
||||
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
|
||||
|
@ -100,7 +100,7 @@ TEST_CASE_FIXTURE(Fixture, "higher_order_function")
|
|||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
CHECK_EQ("<a, b..., c...>((c...) -> (b...), (a) -> (c...), a) -> (b...)", toString(requireType("apply")));
|
||||
else
|
||||
CHECK_EQ("<a, b..., c...>((b...) -> (c...), (a) -> (b...), a) -> (c...)", toString(requireType("apply")));
|
||||
|
@ -966,13 +966,12 @@ a = b
|
|||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'() -> (number, ...boolean)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'() -> (number, ...string)'; \n"
|
||||
"this is because it returns a tail of the variadic `boolean` in the former type and `string` in the latter "
|
||||
"type, and `boolean` is not a subtype of `string`";
|
||||
const std::string expected = "Type\n\t"
|
||||
"'() -> (number, ...boolean)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'() -> (number, ...string)'; \n"
|
||||
"this is because it returns a tail of the variadic `boolean` in the former type and `string` in the latter "
|
||||
"type, and `boolean` is not a subtype of `string`";
|
||||
|
||||
CHECK(expected == toString(result.errors[0]));
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ LUAU_FASTFLAG(LuauRefineWaitForBlockedTypesInTarget)
|
|||
LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType)
|
||||
LUAU_FASTFLAG(LuauDfgIfBlocksShouldRespectControlFlow)
|
||||
LUAU_FASTFLAG(LuauReportSubtypingErrors)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||
LUAU_FASTFLAG(LuauPreprocessTypestatedArgument)
|
||||
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
|
||||
LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops)
|
||||
|
@ -418,7 +418,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "prototyped_recursive_functions_but_has_futur
|
|||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauReportSubtypingErrors, true},
|
||||
{FFlag::LuauEagerGeneralization, true},
|
||||
{FFlag::LuauEagerGeneralization2, true},
|
||||
{FFlag::LuauSubtypeGenericsAndNegations, true},
|
||||
{FFlag::LuauNoMoreInjectiveTypeFunctions, true},
|
||||
};
|
||||
|
@ -763,9 +763,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "refinement_through_erroring")
|
|||
TEST_CASE_FIXTURE(BuiltinsFixture, "refinement_through_erroring_in_loop")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauDfgIfBlocksShouldRespectControlFlow, true},
|
||||
{FFlag::LuauDfgAllowUpdatesInLoops,true}
|
||||
{FFlag::LuauSolverV2, true}, {FFlag::LuauDfgIfBlocksShouldRespectControlFlow, true}, {FFlag::LuauDfgAllowUpdatesInLoops, true}
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
|
|
|
@ -10,7 +10,7 @@ using namespace Luau;
|
|||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
||||
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
|
||||
|
||||
TEST_SUITE_BEGIN("UnionTypes");
|
||||
|
@ -675,11 +675,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_union_write_indirect")
|
|||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
// NOTE: union normalization will improve this message
|
||||
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'(string) -> number'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'((number) -> string) | ((number) -> string)'; none of the union options are compatible";
|
||||
const std::string expected = "Type\n\t"
|
||||
"'(string) -> number'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'((number) -> string) | ((number) -> string)'; none of the union options are compatible";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
|
@ -767,11 +766,10 @@ TEST_CASE_FIXTURE(Fixture, "union_of_functions_mentioning_generic_typepacks")
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'(number, a...) -> (number?, a...)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'((number) -> number) | ((number?, a...) -> (number?, a...))'; none of the union options are compatible";
|
||||
const std::string expected = "Type\n\t"
|
||||
"'(number, a...) -> (number?, a...)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'((number) -> number) | ((number?, a...) -> (number?, a...))'; none of the union options are compatible";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
|
@ -788,11 +786,10 @@ TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_arg_arities")
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'(number) -> number?'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'((number) -> nil) | ((number, string?) -> number)'; none of the union options are compatible";
|
||||
const std::string expected = "Type\n\t"
|
||||
"'(number) -> number?'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'((number) -> nil) | ((number, string?) -> number)'; none of the union options are compatible";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
|
@ -809,11 +806,10 @@ TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_result_arities")
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'() -> number | string'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(() -> (string, string)) | (() -> number)'; none of the union options are compatible";
|
||||
const std::string expected = "Type\n\t"
|
||||
"'() -> number | string'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(() -> (string, string)) | (() -> number)'; none of the union options are compatible";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
|
@ -830,11 +826,10 @@ TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_variadics")
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'(...nil) -> (...number?)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'((...string?) -> (...number)) | ((...string?) -> nil)'; none of the union options are compatible";
|
||||
const std::string expected = "Type\n\t"
|
||||
"'(...nil) -> (...number?)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'((...string?) -> (...number)) | ((...string?) -> nil)'; none of the union options are compatible";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
|
@ -850,11 +845,10 @@ TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_arg_variadics")
|
|||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'(number) -> ()'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'((...number?) -> ()) | ((number?) -> ())'";
|
||||
const std::string expected = "Type\n\t"
|
||||
"'(number) -> ()'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'((...number?) -> ()) | ((number?) -> ())'";
|
||||
CHECK(expected == toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
|
@ -880,11 +874,10 @@ TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_result_variadics
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'() -> (number?, ...number)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(() -> (...number)) | (() -> number)'; none of the union options are compatible";
|
||||
const std::string expected = "Type\n\t"
|
||||
"'() -> (number?, ...number)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(() -> (...number)) | (() -> number)'; none of the union options are compatible";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
|
@ -902,13 +895,14 @@ TEST_CASE_FIXTURE(Fixture, "less_greedy_unification_with_union_types")
|
|||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
if (FFlag::LuauEagerGeneralization)
|
||||
if (FFlag::LuauEagerGeneralization2)
|
||||
CHECK_EQ(
|
||||
"<a>(({ read x: a } & { x: number }) | ({ read x: a } & { x: string })) -> { x: number } | { x: string }", toString(requireType("f"))
|
||||
);
|
||||
else
|
||||
CHECK_EQ(
|
||||
"(({ read x: unknown } & { x: number }) | ({ read x: unknown } & { x: string })) -> { x: number } | { x: string }", toString(requireType("f"))
|
||||
"(({ read x: unknown } & { x: number }) | ({ read x: unknown } & { x: string })) -> { x: number } | { x: string }",
|
||||
toString(requireType("f"))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue