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/InsertionOrderedMap.h"
|
||||||
#include "Luau/Module.h"
|
#include "Luau/Module.h"
|
||||||
#include "Luau/ModuleResolver.h"
|
#include "Luau/ModuleResolver.h"
|
||||||
#include "Luau/Normalize.h"
|
|
||||||
#include "Luau/NotNull.h"
|
#include "Luau/NotNull.h"
|
||||||
#include "Luau/Polarity.h"
|
#include "Luau/Polarity.h"
|
||||||
#include "Luau/Refinement.h"
|
#include "Luau/Refinement.h"
|
||||||
#include "Luau/Symbol.h"
|
#include "Luau/Symbol.h"
|
||||||
#include "Luau/TypeFwd.h"
|
#include "Luau/TypeFwd.h"
|
||||||
|
#include "Luau/TypeIds.h"
|
||||||
#include "Luau/TypeUtils.h"
|
#include "Luau/TypeUtils.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
@ -93,7 +93,7 @@ struct ConstraintGenerator
|
||||||
std::vector<ConstraintPtr> constraints;
|
std::vector<ConstraintPtr> constraints;
|
||||||
|
|
||||||
// The set of all free types introduced during constraint generation.
|
// 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.
|
// Map a function's signature scope back to its signature type.
|
||||||
DenseHashMap<Scope*, TypeId> scopeToFunction{nullptr};
|
DenseHashMap<Scope*, TypeId> scopeToFunction{nullptr};
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Luau/Constraint.h"
|
#include "Luau/Constraint.h"
|
||||||
#include "Luau/DenseHash.h"
|
#include "Luau/TypeIds.h"
|
||||||
#include "Luau/Error.h"
|
#include "Luau/Error.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -18,7 +18,7 @@ struct ConstraintSet
|
||||||
std::vector<ConstraintPtr> constraints;
|
std::vector<ConstraintPtr> constraints;
|
||||||
|
|
||||||
// The set of all free types created during constraint generation
|
// 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
|
// 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,
|
// dispatched all of the constraints pertaining to a particular free type,
|
||||||
|
@ -29,4 +29,4 @@ struct ConstraintSet
|
||||||
std::vector<TypeError> errors;
|
std::vector<TypeError> errors;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
} // namespace Luau
|
||||||
|
|
|
@ -456,6 +456,14 @@ public:
|
||||||
*/
|
*/
|
||||||
std::vector<NotNull<Constraint>> borrowConstraints(const std::vector<ConstraintPtr>& constraints);
|
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);
|
void dump(NotNull<Scope> rootScope, struct ToStringOptions& opts);
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -464,7 +464,10 @@ struct ReservedIdentifier
|
||||||
|
|
||||||
struct UnexpectedArrayLikeTableItem
|
struct UnexpectedArrayLikeTableItem
|
||||||
{
|
{
|
||||||
bool operator==(const UnexpectedArrayLikeTableItem&) const { return true; }
|
bool operator==(const UnexpectedArrayLikeTableItem&) const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
using TypeErrorData = Variant<
|
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.
|
// we need a sentinel value for the map.
|
||||||
DenseHashMap<const AstNode*, Scope*> astScopes{nullptr};
|
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;
|
std::unordered_map<Name, TypeId> declaredGlobals;
|
||||||
ErrorVec errors;
|
ErrorVec errors;
|
||||||
LintResult lintResult;
|
LintResult lintResult;
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
#include "Luau/NotNull.h"
|
#include "Luau/NotNull.h"
|
||||||
#include "Luau/Set.h"
|
#include "Luau/Set.h"
|
||||||
#include "Luau/TypeFwd.h"
|
#include "Luau/TypeFwd.h"
|
||||||
|
#include "Luau/TypeIds.h"
|
||||||
#include "Luau/UnifierSharedState.h"
|
#include "Luau/UnifierSharedState.h"
|
||||||
|
|
||||||
#include <initializer_list>
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
@ -39,57 +39,6 @@ bool isSubtype(
|
||||||
InternalErrorReporter& ice
|
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
|
} // namespace Luau
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -302,7 +251,7 @@ struct NormalizedType
|
||||||
// we'd be reusing bad, stale data.
|
// we'd be reusing bad, stale data.
|
||||||
bool isCacheable = true;
|
bool isCacheable = true;
|
||||||
|
|
||||||
NormalizedType(NotNull<BuiltinTypes> builtinTypes);
|
explicit NormalizedType(NotNull<BuiltinTypes> builtinTypes);
|
||||||
|
|
||||||
NormalizedType() = delete;
|
NormalizedType() = delete;
|
||||||
~NormalizedType() = default;
|
~NormalizedType() = default;
|
||||||
|
|
|
@ -101,6 +101,8 @@ struct Scope
|
||||||
|
|
||||||
std::optional<std::vector<TypeId>> interiorFreeTypes;
|
std::optional<std::vector<TypeId>> interiorFreeTypes;
|
||||||
std::optional<std::vector<TypePackId>> interiorFreeTypePacks;
|
std::optional<std::vector<TypePackId>> interiorFreeTypePacks;
|
||||||
|
|
||||||
|
NotNull<Scope> findNarrowestScopeContaining(Location);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Returns true iff the left scope encloses the right scope. A Scope* equal to
|
// Returns true iff the left scope encloses the right scope. A Scope* equal to
|
||||||
|
|
|
@ -234,8 +234,14 @@ private:
|
||||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TableType* subTable, const TableType* superTable, NotNull<Scope> scope);
|
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TableType* subTable, const TableType* superTable, NotNull<Scope> scope);
|
||||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const MetatableType* superMt, NotNull<Scope> scope);
|
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const MetatableType* superMt, NotNull<Scope> scope);
|
||||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const TableType* superTable, 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(
|
||||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const ExternType* subExternType, TypeId superTy, const TableType* superTable, NotNull<Scope>);
|
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(
|
SubtypingResult isCovariantWith(
|
||||||
SubtypingEnvironment& env,
|
SubtypingEnvironment& env,
|
||||||
const FunctionType* subFunction,
|
const FunctionType* subFunction,
|
||||||
|
@ -267,7 +273,12 @@ private:
|
||||||
const NormalizedExternType& superExternType,
|
const NormalizedExternType& superExternType,
|
||||||
NotNull<Scope> scope
|
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(
|
SubtypingResult isCovariantWith(
|
||||||
SubtypingEnvironment& env,
|
SubtypingEnvironment& env,
|
||||||
const NormalizedStringType& subString,
|
const NormalizedStringType& subString,
|
||||||
|
|
|
@ -34,6 +34,7 @@ using ScopePtr = std::shared_ptr<Scope>;
|
||||||
struct Module;
|
struct Module;
|
||||||
|
|
||||||
struct TypeFunction;
|
struct TypeFunction;
|
||||||
|
struct TypeFun;
|
||||||
struct Constraint;
|
struct Constraint;
|
||||||
struct Subtyping;
|
struct Subtyping;
|
||||||
struct TypeChecker2;
|
struct TypeChecker2;
|
||||||
|
@ -608,7 +609,8 @@ struct UserDefinedFunctionData
|
||||||
// References to AST elements are owned by the Module allocator which also stores this type
|
// References to AST elements are owned by the Module allocator which also stores this type
|
||||||
AstStatTypeFunction* definition = nullptr;
|
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 TypeArena;
|
||||||
struct TxnLog;
|
struct TxnLog;
|
||||||
struct ConstraintSolver;
|
struct ConstraintSolver;
|
||||||
|
struct TypeFunctionRuntimeBuilderState;
|
||||||
|
struct TypeFunctionContext;
|
||||||
class Normalizer;
|
class Normalizer;
|
||||||
|
|
||||||
using StateRef = std::unique_ptr<lua_State, void (*)(lua_State*)>;
|
using StateRef = std::unique_ptr<lua_State, void (*)(lua_State*)>;
|
||||||
|
@ -54,6 +56,9 @@ struct TypeFunctionRuntime
|
||||||
// Output created by 'print' function
|
// Output created by 'print' function
|
||||||
std::vector<std::string> messages;
|
std::vector<std::string> messages;
|
||||||
|
|
||||||
|
// Type builder, valid for the duration of a single evaluation
|
||||||
|
TypeFunctionRuntimeBuilderState* runtimeBuilder = nullptr;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void prepareState();
|
void prepareState();
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,6 +15,8 @@ using lua_State = struct lua_State;
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
struct TypeFunctionRuntime;
|
||||||
|
|
||||||
void* typeFunctionAlloc(void* ud, void* ptr, size_t osize, size_t nsize);
|
void* typeFunctionAlloc(void* ud, void* ptr, size_t osize, size_t nsize);
|
||||||
|
|
||||||
// Replica of types from Type.h
|
// 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);
|
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);
|
TypeFunctionType* allocateTypeFunctionType(lua_State* L, TypeFunctionTypeVariant type);
|
||||||
TypeFunctionTypePackVar* allocateTypeFunctionTypePack(lua_State* L, TypeFunctionTypePackVariant 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);
|
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
|
} // namespace Luau
|
||||||
|
|
|
@ -173,12 +173,7 @@ static bool checkTypeMatch(
|
||||||
unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit;
|
unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit;
|
||||||
|
|
||||||
Subtyping subtyping{
|
Subtyping subtyping{
|
||||||
builtinTypes,
|
builtinTypes, NotNull{typeArena}, NotNull{simplifier.get()}, NotNull{&normalizer}, NotNull{&typeFunctionRuntime}, NotNull{&iceReporter}
|
||||||
NotNull{typeArena},
|
|
||||||
NotNull{simplifier.get()},
|
|
||||||
NotNull{&normalizer},
|
|
||||||
NotNull{&typeFunctionRuntime},
|
|
||||||
NotNull{&iceReporter}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return subtyping.isSubtype(subTy, superTy, scope).isSubtype;
|
return subtyping.isSubtype(subTy, superTy, scope).isSubtype;
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
#include "Luau/NotNull.h"
|
#include "Luau/NotNull.h"
|
||||||
#include "Luau/Subtyping.h"
|
#include "Luau/Subtyping.h"
|
||||||
#include "Luau/Symbol.h"
|
#include "Luau/Symbol.h"
|
||||||
#include "Luau/ToString.h"
|
|
||||||
#include "Luau/Type.h"
|
#include "Luau/Type.h"
|
||||||
#include "Luau/TypeChecker2.h"
|
#include "Luau/TypeChecker2.h"
|
||||||
#include "Luau/TypeFunction.h"
|
#include "Luau/TypeFunction.h"
|
||||||
|
@ -30,10 +29,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3)
|
LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauMagicFreezeCheckBlocked2)
|
LUAU_FASTFLAGVARIABLE(LuauMagicFreezeCheckBlocked2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFormatUseLastPosition)
|
LUAU_FASTFLAGVARIABLE(LuauFormatUseLastPosition)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauUpdateSetMetatableTypeSignature)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -310,8 +310,8 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
|
||||||
|
|
||||||
TypeArena& arena = globals.globalTypes;
|
TypeArena& arena = globals.globalTypes;
|
||||||
NotNull<BuiltinTypes> builtinTypes = globals.builtinTypes;
|
NotNull<BuiltinTypes> builtinTypes = globals.builtinTypes;
|
||||||
Scope* globalScope = nullptr; // NotNull<Scope> when removing FFlag::LuauEagerGeneralization
|
Scope* globalScope = nullptr; // NotNull<Scope> when removing FFlag::LuauEagerGeneralization2
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
globalScope = globals.globalScope.get();
|
globalScope = globals.globalScope.get();
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
|
@ -386,6 +386,16 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
|
||||||
TypeId genericT = arena.addType(GenericType{globalScope, "T"});
|
TypeId genericT = arena.addType(GenericType{globalScope, "T"});
|
||||||
TypeId tMetaMT = arena.addType(MetatableType{genericT, genericMT});
|
TypeId tMetaMT = arena.addType(MetatableType{genericT, genericMT});
|
||||||
|
|
||||||
|
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
|
// clang-format off
|
||||||
// setmetatable<T: {}, MT>(T, MT) -> { @metatable MT, T }
|
// setmetatable<T: {}, MT>(T, MT) -> { @metatable MT, T }
|
||||||
addGlobalBinding(globals, "setmetatable",
|
addGlobalBinding(globals, "setmetatable",
|
||||||
|
@ -400,6 +410,7 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
|
||||||
);
|
);
|
||||||
// clang-format on
|
// clang-format on
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
@ -701,7 +712,8 @@ bool MagicFormat::typeCheck(const MagicFunctionTypeCheckContext& context)
|
||||||
{
|
{
|
||||||
TypeId actualTy = params[i + paramOffset];
|
TypeId actualTy = params[i + paramOffset];
|
||||||
TypeId expectedTy = expected[i];
|
TypeId expectedTy = expected[i];
|
||||||
Location location = FFlag::LuauFormatUseLastPosition
|
Location location =
|
||||||
|
FFlag::LuauFormatUseLastPosition
|
||||||
? context.callSite->args.data[std::min(context.callSite->args.size - 1, i + (calledWithSelf ? 0 : paramOffset))]->location
|
? 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;
|
: context.callSite->args.data[i + (calledWithSelf ? 0 : paramOffset)]->location;
|
||||||
// use subtyping instead here
|
// use subtyping instead here
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#include "Luau/Constraint.h"
|
#include "Luau/Constraint.h"
|
||||||
#include "Luau/VisitType.h"
|
#include "Luau/VisitType.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -51,7 +51,7 @@ struct ReferenceCountInitializer : TypeOnceVisitor
|
||||||
|
|
||||||
bool visit(TypeId, const TypeFunctionInstanceType&) override
|
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);
|
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.traverseIntoTypeFunctions = false;
|
||||||
rci.traverse(fcc->fn);
|
rci.traverse(fcc->fn);
|
||||||
|
@ -118,12 +118,12 @@ DenseHashSet<TypeId> Constraint::getMaybeMutatedFreeTypes() const
|
||||||
else if (auto hpc = get<HasPropConstraint>(*this))
|
else if (auto hpc = get<HasPropConstraint>(*this))
|
||||||
{
|
{
|
||||||
rci.traverse(hpc->resultType);
|
rci.traverse(hpc->resultType);
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
rci.traverse(hpc->subjectType);
|
rci.traverse(hpc->subjectType);
|
||||||
}
|
}
|
||||||
else if (auto hic = get<HasIndexerConstraint>(*this))
|
else if (auto hic = get<HasIndexerConstraint>(*this))
|
||||||
{
|
{
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
rci.traverse(hic->subjectType);
|
rci.traverse(hic->subjectType);
|
||||||
rci.traverse(hic->resultType);
|
rci.traverse(hic->resultType);
|
||||||
// `HasIndexerConstraint` should not mutate `indexType`.
|
// `HasIndexerConstraint` should not mutate `indexType`.
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "Luau/DenseHash.h"
|
#include "Luau/DenseHash.h"
|
||||||
#include "Luau/InferPolarity.h"
|
#include "Luau/InferPolarity.h"
|
||||||
#include "Luau/ModuleResolver.h"
|
#include "Luau/ModuleResolver.h"
|
||||||
|
#include "Luau/Normalize.h"
|
||||||
#include "Luau/NotNull.h"
|
#include "Luau/NotNull.h"
|
||||||
#include "Luau/RecursionCounter.h"
|
#include "Luau/RecursionCounter.h"
|
||||||
#include "Luau/Refinement.h"
|
#include "Luau/Refinement.h"
|
||||||
|
@ -33,11 +34,8 @@
|
||||||
LUAU_FASTINT(LuauCheckRecursionLimit)
|
LUAU_FASTINT(LuauCheckRecursionLimit)
|
||||||
LUAU_FASTFLAG(DebugLuauLogSolverToJson)
|
LUAU_FASTFLAG(DebugLuauLogSolverToJson)
|
||||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauRetainDefinitionAliasLocations)
|
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauWeakNilRefinementType)
|
LUAU_FASTFLAGVARIABLE(LuauWeakNilRefinementType)
|
||||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation)
|
LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation)
|
||||||
|
@ -51,6 +49,7 @@ LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
|
||||||
LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops)
|
LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauDisablePrimitiveInferenceInLargeTables)
|
LUAU_FASTFLAGVARIABLE(LuauDisablePrimitiveInferenceInLargeTables)
|
||||||
LUAU_FASTINTVARIABLE(LuauPrimitiveInferenceInTableLimit, 500)
|
LUAU_FASTINTVARIABLE(LuauPrimitiveInferenceInTableLimit, 500)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunctionAliases)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -180,6 +179,22 @@ bool hasFreeType(TypeId ty)
|
||||||
return hft.result;
|
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
|
} // namespace
|
||||||
|
|
||||||
ConstraintGenerator::ConstraintGenerator(
|
ConstraintGenerator::ConstraintGenerator(
|
||||||
|
@ -220,26 +235,14 @@ ConstraintSet ConstraintGenerator::run(AstStatBlock* block)
|
||||||
{
|
{
|
||||||
visitModuleRoot(block);
|
visitModuleRoot(block);
|
||||||
|
|
||||||
return ConstraintSet{
|
return ConstraintSet{NotNull{rootScope}, std::move(constraints), std::move(freeTypes), std::move(scopeToFunction), std::move(errors)};
|
||||||
NotNull{rootScope},
|
|
||||||
std::move(constraints),
|
|
||||||
std::move(freeTypes),
|
|
||||||
std::move(scopeToFunction),
|
|
||||||
std::move(errors)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ConstraintSet ConstraintGenerator::runOnFragment(const ScopePtr& resumeScope, AstStatBlock* block)
|
ConstraintSet ConstraintGenerator::runOnFragment(const ScopePtr& resumeScope, AstStatBlock* block)
|
||||||
{
|
{
|
||||||
visitFragmentRoot(resumeScope, block);
|
visitFragmentRoot(resumeScope, block);
|
||||||
|
|
||||||
return ConstraintSet{
|
return ConstraintSet{NotNull{rootScope}, std::move(constraints), std::move(freeTypes), std::move(scopeToFunction), std::move(errors)};
|
||||||
NotNull{rootScope},
|
|
||||||
std::move(constraints),
|
|
||||||
std::move(freeTypes),
|
|
||||||
std::move(scopeToFunction),
|
|
||||||
std::move(errors)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
|
void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
|
||||||
|
@ -254,7 +257,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
|
||||||
rootScope->location = block->location;
|
rootScope->location = block->location;
|
||||||
module->astScopes[block] = NotNull{scope.get()};
|
module->astScopes[block] = NotNull{scope.get()};
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
interiorFreeTypes.emplace_back();
|
interiorFreeTypes.emplace_back();
|
||||||
else
|
else
|
||||||
DEPRECATED_interiorTypes.emplace_back();
|
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->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
|
||||||
scope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks);
|
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();
|
interiorFreeTypes.pop_back();
|
||||||
else
|
else
|
||||||
DEPRECATED_interiorTypes.pop_back();
|
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
|
// We prepopulate global data in the resumeScope to avoid writing data into the old modules scopes
|
||||||
prepopulateGlobalScopeForFragmentTypecheck(globalScope, resumeScope, block);
|
prepopulateGlobalScopeForFragmentTypecheck(globalScope, resumeScope, block);
|
||||||
// Pre
|
// Pre
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
interiorFreeTypes.emplace_back();
|
interiorFreeTypes.emplace_back();
|
||||||
else
|
else
|
||||||
DEPRECATED_interiorTypes.emplace_back();
|
DEPRECATED_interiorTypes.emplace_back();
|
||||||
visitBlockWithoutChildScope(resumeScope, block);
|
visitBlockWithoutChildScope(resumeScope, block);
|
||||||
// Post
|
// Post
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
interiorFreeTypes.pop_back();
|
interiorFreeTypes.pop_back();
|
||||||
else
|
else
|
||||||
DEPRECATED_interiorTypes.pop_back();
|
DEPRECATED_interiorTypes.pop_back();
|
||||||
|
@ -383,12 +386,12 @@ void ConstraintGenerator::visitFragmentRoot(const ScopePtr& resumeScope, AstStat
|
||||||
|
|
||||||
TypeId ConstraintGenerator::freshType(const ScopePtr& scope, Polarity polarity)
|
TypeId ConstraintGenerator::freshType(const ScopePtr& scope, Polarity polarity)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
auto ft = Luau::freshType(arena, builtinTypes, scope.get(), polarity);
|
auto ft = Luau::freshType(arena, builtinTypes, scope.get(), polarity);
|
||||||
interiorFreeTypes.back().types.push_back(ft);
|
interiorFreeTypes.back().types.push_back(ft);
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
freeTypes.insert(ft);
|
freeTypes.insert(ft);
|
||||||
|
|
||||||
return ft;
|
return ft;
|
||||||
|
@ -405,7 +408,7 @@ TypePackId ConstraintGenerator::freshTypePack(const ScopePtr& scope, Polarity po
|
||||||
{
|
{
|
||||||
FreeTypePack f{scope.get(), polarity};
|
FreeTypePack f{scope.get(), polarity};
|
||||||
TypePackId result = arena->addTypePack(TypePackVar{std::move(f)});
|
TypePackId result = arena->addTypePack(TypePackVar{std::move(f)});
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
interiorFreeTypes.back().typePacks.push_back(result);
|
interiorFreeTypes.back().typePacks.push_back(result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -818,7 +821,6 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc
|
||||||
initialFun.typePackParams.push_back(genPack);
|
initialFun.typePackParams.push_back(genPack);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauRetainDefinitionAliasLocations)
|
|
||||||
initialFun.definitionLocation = alias->location;
|
initialFun.definitionLocation = alias->location;
|
||||||
|
|
||||||
if (alias->exported)
|
if (alias->exported)
|
||||||
|
@ -873,7 +875,6 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc
|
||||||
|
|
||||||
TypeFun typeFunction{std::move(quantifiedTypeParams), typeFunctionTy};
|
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
|
// Set type bindings and definition locations for this user-defined type function
|
||||||
|
@ -903,7 +904,6 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc
|
||||||
|
|
||||||
TypeId initialType = arena->addType(BlockedType{});
|
TypeId initialType = arena->addType(BlockedType{});
|
||||||
TypeFun initialFun{initialType};
|
TypeFun initialFun{initialType};
|
||||||
if (FFlag::LuauRetainDefinitionAliasLocations)
|
|
||||||
initialFun.definitionLocation = classDeclaration->location;
|
initialFun.definitionLocation = classDeclaration->location;
|
||||||
scope->exportedTypeBindings[classDeclaration->name.value] = std::move(initialFun);
|
scope->exportedTypeBindings[classDeclaration->name.value] = std::move(initialFun);
|
||||||
|
|
||||||
|
@ -936,20 +936,24 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc
|
||||||
mainTypeFun = getMutable<TypeFunctionInstanceType>(it->second.type);
|
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)
|
if (mainTypeFun)
|
||||||
{
|
{
|
||||||
|
GlobalNameCollector globalNameCollector;
|
||||||
|
stat->visit(&globalNameCollector);
|
||||||
|
|
||||||
UserDefinedFunctionData& userFuncData = mainTypeFun->userFuncData;
|
UserDefinedFunctionData& userFuncData = mainTypeFun->userFuncData;
|
||||||
size_t level = 0;
|
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))
|
if (auto ty = get<TypeFunctionInstanceType>(tf.type); ty && ty->userFuncData.definition)
|
||||||
|
{
|
||||||
|
if (userFuncData.environmentFunction.find(name))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (auto ty = get<TypeFunctionInstanceType>(type); ty && ty->userFuncData.definition)
|
userFuncData.environmentFunction[name] = std::make_pair(ty->userFuncData.definition, level);
|
||||||
{
|
|
||||||
userFuncData.environment[name] = std::make_pair(ty->userFuncData.definition, level);
|
|
||||||
|
|
||||||
if (auto it = astTypeFunctionEnvironmentScopes.find(ty->userFuncData.definition))
|
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};
|
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)
|
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)
|
for (auto& [name, tf] : curr->exportedTypeBindings)
|
||||||
addToEnvironment(userFuncData, typeFunctionEnvScope, name, tf.type, level);
|
addToEnvironment(userFuncData, typeFunctionEnvScope, name, tf, level);
|
||||||
|
|
||||||
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);
|
FunctionSignature sig = checkFunctionSignature(scope, function->func, /* expectedType */ std::nullopt, function->name->location);
|
||||||
sig.bodyScope->bindings[function->name] = Binding{sig.signature, 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)
|
if (sigFullyDefined)
|
||||||
emplaceType<BoundType>(asMutable(functionType), sig.signature);
|
emplaceType<BoundType>(asMutable(functionType), sig.signature);
|
||||||
|
|
||||||
|
@ -1456,7 +1480,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatFunction* f
|
||||||
|
|
||||||
Checkpoint start = checkpoint(this);
|
Checkpoint start = checkpoint(this);
|
||||||
FunctionSignature sig = checkFunctionSignature(scope, function->func, /* expectedType */ std::nullopt, function->name->location);
|
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);
|
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
|
// Place this function as a child of the non-type function scope
|
||||||
scope->children.push_back(NotNull{sig.signatureScope.get()});
|
scope->children.push_back(NotNull{sig.signatureScope.get()});
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
interiorFreeTypes.emplace_back();
|
interiorFreeTypes.emplace_back();
|
||||||
else
|
else
|
||||||
DEPRECATED_interiorTypes.push_back(std::vector<TypeId>{});
|
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->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
|
||||||
sig.signatureScope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks);
|
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());
|
sig.signatureScope->interiorFreeTypes = std::move(DEPRECATED_interiorTypes.back());
|
||||||
|
|
||||||
getMutable<BlockedType>(generalizedTy)->setOwner(gc);
|
getMutable<BlockedType>(generalizedTy)->setOwner(gc);
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
interiorFreeTypes.pop_back();
|
interiorFreeTypes.pop_back();
|
||||||
else
|
else
|
||||||
DEPRECATED_interiorTypes.pop_back();
|
DEPRECATED_interiorTypes.pop_back();
|
||||||
|
@ -1884,7 +1908,8 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareExte
|
||||||
{
|
{
|
||||||
reportError(
|
reportError(
|
||||||
declaredExternType->location,
|
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;
|
return ControlFlow::None;
|
||||||
|
@ -2466,7 +2491,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantStrin
|
||||||
return Inference{builtinTypes->stringType};
|
return Inference{builtinTypes->stringType};
|
||||||
|
|
||||||
TypeId freeTy = nullptr;
|
TypeId freeTy = nullptr;
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
freeTy = freshType(scope, Polarity::Positive);
|
freeTy = freshType(scope, Polarity::Positive);
|
||||||
FreeType* ft = getMutable<FreeType>(freeTy);
|
FreeType* ft = getMutable<FreeType>(freeTy);
|
||||||
|
@ -2507,7 +2532,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantBool*
|
||||||
return Inference{builtinTypes->booleanType};
|
return Inference{builtinTypes->booleanType};
|
||||||
|
|
||||||
TypeId freeTy = nullptr;
|
TypeId freeTy = nullptr;
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
freeTy = freshType(scope, Polarity::Positive);
|
freeTy = freshType(scope, Polarity::Positive);
|
||||||
FreeType* ft = getMutable<FreeType>(freeTy);
|
FreeType* ft = getMutable<FreeType>(freeTy);
|
||||||
|
@ -2605,9 +2630,7 @@ Inference ConstraintGenerator::checkIndexName(
|
||||||
{
|
{
|
||||||
result = arena->addType(BlockedType{});
|
result = arena->addType(BlockedType{});
|
||||||
|
|
||||||
auto c = addConstraint(
|
auto c = addConstraint(scope, indexee->location, HasPropConstraint{result, obj, index, ValueContext::RValue, inConditional(typeContext)});
|
||||||
scope, indexee->location, HasPropConstraint{result, obj, std::move(index), ValueContext::RValue, inConditional(typeContext)}
|
|
||||||
);
|
|
||||||
getMutable<BlockedType>(result)->setOwner(c);
|
getMutable<BlockedType>(result)->setOwner(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2668,7 +2691,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprFunction* fun
|
||||||
Checkpoint startCheckpoint = checkpoint(this);
|
Checkpoint startCheckpoint = checkpoint(this);
|
||||||
FunctionSignature sig = checkFunctionSignature(scope, func, expectedType);
|
FunctionSignature sig = checkFunctionSignature(scope, func, expectedType);
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
interiorFreeTypes.emplace_back();
|
interiorFreeTypes.emplace_back();
|
||||||
else
|
else
|
||||||
DEPRECATED_interiorTypes.push_back(std::vector<TypeId>{});
|
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->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
|
||||||
sig.signatureScope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks);
|
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->definitionLocation = expr->location;
|
||||||
ttv->scope = scope.get();
|
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++;
|
largeTableDepth++;
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
interiorFreeTypes.back().types.push_back(ty);
|
interiorFreeTypes.back().types.push_back(ty);
|
||||||
else
|
else
|
||||||
DEPRECATED_interiorTypes.back().push_back(ty);
|
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--;
|
largeTableDepth--;
|
||||||
|
|
||||||
return Inference{ty};
|
return Inference{ty};
|
||||||
|
@ -3453,7 +3478,7 @@ ConstraintGenerator::FunctionSignature ConstraintGenerator::checkFunctionSignatu
|
||||||
|
|
||||||
LUAU_ASSERT(nullptr != varargPack);
|
LUAU_ASSERT(nullptr != varargPack);
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
// Some of the types in argTypes will eventually be generics, and some
|
// Some of the types in argTypes will eventually be generics, and some
|
||||||
// will not. The ones that are not generic will be pruned when
|
// 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))
|
if (expectedType && get<FreeType>(*expectedType))
|
||||||
bindFreeType(*expectedType, actualFunctionType);
|
bindFreeType(*expectedType, actualFunctionType);
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
scopeToFunction[signatureScope.get()] = actualFunctionType;
|
scopeToFunction[signatureScope.get()] = actualFunctionType;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -33,9 +33,8 @@ LUAU_FASTFLAGVARIABLE(DebugLuauLogBindings)
|
||||||
LUAU_FASTINTVARIABLE(LuauSolverRecursionLimit, 500)
|
LUAU_FASTINTVARIABLE(LuauSolverRecursionLimit, 500)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification)
|
LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauHasPropProperBlock)
|
LUAU_FASTFLAGVARIABLE(LuauHasPropProperBlock)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||||
LUAU_FASTFLAG(LuauDeprecatedAttribute)
|
LUAU_FASTFLAG(LuauDeprecatedAttribute)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTrackInferredFunctionTypeFromCall)
|
LUAU_FASTFLAGVARIABLE(LuauTrackInferredFunctionTypeFromCall)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAddCallConstraintForIterableFunctions)
|
LUAU_FASTFLAGVARIABLE(LuauAddCallConstraintForIterableFunctions)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauGuardAgainstMalformedTypeAliasExpansion2)
|
LUAU_FASTFLAGVARIABLE(LuauGuardAgainstMalformedTypeAliasExpansion2)
|
||||||
|
@ -43,6 +42,7 @@ LUAU_FASTFLAGVARIABLE(LuauInsertErrorTypesIntoIndexerResult)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauClipVariadicAnysFromArgsToGenericFuncs2)
|
LUAU_FASTFLAGVARIABLE(LuauClipVariadicAnysFromArgsToGenericFuncs2)
|
||||||
LUAU_FASTFLAG(LuauSubtypeGenericsAndNegations)
|
LUAU_FASTFLAG(LuauSubtypeGenericsAndNegations)
|
||||||
LUAU_FASTFLAG(LuauNoMoreInjectiveTypeFunctions)
|
LUAU_FASTFLAG(LuauNoMoreInjectiveTypeFunctions)
|
||||||
|
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -103,7 +103,7 @@ size_t HashBlockedConstraintId::operator()(const BlockedConstraintId& bci) const
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::pair<std::vector<TypeId>, std::vector<TypePackId>> saturateArguments(
|
std::pair<std::vector<TypeId>, std::vector<TypePackId>> saturateArguments(
|
||||||
TypeArena* arena,
|
TypeArena* arena,
|
||||||
NotNull<BuiltinTypes> builtinTypes,
|
NotNull<BuiltinTypes> builtinTypes,
|
||||||
const TypeFun& fn,
|
const TypeFun& fn,
|
||||||
|
@ -418,7 +418,7 @@ void ConstraintSolver::run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free types that have no constraints at all can be generalized right away.
|
// Free types that have no constraints at all can be generalized right away.
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
for (TypeId ty : constraintSet.freeTypes)
|
for (TypeId ty : constraintSet.freeTypes)
|
||||||
{
|
{
|
||||||
|
@ -479,7 +479,7 @@ void ConstraintSolver::run()
|
||||||
// expansion types, etc, so we need to follow it.
|
// expansion types, etc, so we need to follow it.
|
||||||
ty = follow(ty);
|
ty = follow(ty);
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
if (seen.contains(ty))
|
if (seen.contains(ty))
|
||||||
continue;
|
continue;
|
||||||
|
@ -498,7 +498,7 @@ void ConstraintSolver::run()
|
||||||
if (refCount <= 1)
|
if (refCount <= 1)
|
||||||
unblock(ty, Location{});
|
unblock(ty, Location{});
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization && refCount == 0)
|
if (FFlag::LuauEagerGeneralization2 && refCount == 0)
|
||||||
generalizeOneType(ty);
|
generalizeOneType(ty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -676,7 +676,7 @@ void ConstraintSolver::initFreeTypeTracking()
|
||||||
auto [refCount, _] = unresolvedConstraints.try_insert(ty, 0);
|
auto [refCount, _] = unresolvedConstraints.try_insert(ty, 0);
|
||||||
refCount += 1;
|
refCount += 1;
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
auto [it, fresh] = mutatedFreeTypeToConstraint.try_emplace(ty, nullptr);
|
auto [it, fresh] = mutatedFreeTypeToConstraint.try_emplace(ty, nullptr);
|
||||||
it->second.insert(c.get());
|
it->second.insert(c.get());
|
||||||
|
@ -691,7 +691,7 @@ void ConstraintSolver::initFreeTypeTracking()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also check flag integrity while we're here
|
// Also check flag integrity while we're here
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauSubtypeGenericsAndNegations);
|
LUAU_ASSERT(FFlag::LuauSubtypeGenericsAndNegations);
|
||||||
LUAU_ASSERT(FFlag::LuauNoMoreInjectiveTypeFunctions);
|
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
|
constraint, ty, constraint->scope, builtinTypes->neverType, builtinTypes->unknownType, Polarity::Mixed
|
||||||
); // FIXME? Is this the right polarity?
|
); // FIXME? Is this the right polarity?
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
trackInteriorFreeType(constraint->scope, ty);
|
trackInteriorFreeType(constraint->scope, ty);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -900,7 +900,7 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<co
|
||||||
{
|
{
|
||||||
for (TypeId ty : *constraint->scope->interiorFreeTypes) // NOLINT(bugprone-unchecked-optional-access)
|
for (TypeId ty : *constraint->scope->interiorFreeTypes) // NOLINT(bugprone-unchecked-optional-access)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
ty = follow(ty);
|
ty = follow(ty);
|
||||||
if (auto freeTy = get<FreeType>(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)
|
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)
|
if (c.noGenerics)
|
||||||
{
|
{
|
||||||
|
@ -1378,7 +1378,6 @@ void ConstraintSolver::fillInDiscriminantTypes(NotNull<const Constraint> constra
|
||||||
// We also need to unconditionally unblock these types, otherwise
|
// We also need to unconditionally unblock these types, otherwise
|
||||||
// you end up with funky looking "Blocked on *no-refine*."
|
// you end up with funky looking "Blocked on *no-refine*."
|
||||||
unblock(*ty, constraint->location);
|
unblock(*ty, constraint->location);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1388,7 +1387,7 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
|
||||||
TypePackId argsPack = follow(c.argsPack);
|
TypePackId argsPack = follow(c.argsPack);
|
||||||
TypePackId result = follow(c.result);
|
TypePackId result = follow(c.result);
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
if (isBlocked(fn))
|
if (isBlocked(fn))
|
||||||
return block(c.fn, constraint);
|
return block(c.fn, constraint);
|
||||||
|
@ -1528,7 +1527,7 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
|
||||||
|
|
||||||
const bool occursCheckPassed = u2.unify(overloadToUse, inferredTy);
|
const bool occursCheckPassed = u2.unify(overloadToUse, inferredTy);
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
for (TypeId freeTy : u2.newFreshTypes)
|
for (TypeId freeTy : u2.newFreshTypes)
|
||||||
trackInteriorFreeType(constraint->scope, freeTy);
|
trackInteriorFreeType(constraint->scope, freeTy);
|
||||||
|
@ -1942,7 +1941,7 @@ bool ConstraintSolver::tryDispatchHasIndexer(
|
||||||
TypeId upperBound =
|
TypeId upperBound =
|
||||||
arena->addType(TableType{/* props */ {}, TableIndexer{indexType, resultType}, TypeLevel{}, ft->scope, TableState::Unsealed});
|
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));
|
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};
|
FreeType freeResult{tt->scope, builtinTypes->neverType, builtinTypes->unknownType, Polarity::Mixed};
|
||||||
emplace<FreeType>(constraint, resultType, freeResult);
|
emplace<FreeType>(constraint, resultType, freeResult);
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
trackInteriorFreeType(constraint->scope, resultType);
|
trackInteriorFreeType(constraint->scope, resultType);
|
||||||
|
|
||||||
tt->indexer = TableIndexer{indexType, resultType};
|
tt->indexer = TableIndexer{indexType, resultType};
|
||||||
|
@ -2056,7 +2055,6 @@ bool ConstraintSolver::tryDispatchHasIndexer(
|
||||||
r = follow(r);
|
r = follow(r);
|
||||||
if (FFlag::LuauInsertErrorTypesIntoIndexerResult || !get<ErrorType>(r))
|
if (FFlag::LuauInsertErrorTypesIntoIndexerResult || !get<ErrorType>(r))
|
||||||
results.insert(r);
|
results.insert(r);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (0 == results.size())
|
if (0 == results.size())
|
||||||
|
@ -2163,7 +2161,7 @@ bool ConstraintSolver::tryDispatch(const AssignPropConstraint& c, NotNull<const
|
||||||
{
|
{
|
||||||
auto lhsFreeUpperBound = follow(lhsFree->upperBound);
|
auto lhsFreeUpperBound = follow(lhsFree->upperBound);
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
const auto [blocked, maybeTy, isIndex] = lookupTableProp(constraint, lhsType, propName, ValueContext::LValue);
|
const auto [blocked, maybeTy, isIndex] = lookupTableProp(constraint, lhsType, propName, ValueContext::LValue);
|
||||||
if (!blocked.empty())
|
if (!blocked.empty())
|
||||||
|
@ -3064,7 +3062,7 @@ TablePropLookupResult ConstraintSolver::lookupTableProp(
|
||||||
{
|
{
|
||||||
const TypeId upperBound = follow(ft->upperBound);
|
const TypeId upperBound = follow(ft->upperBound);
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
if (get<TableType>(upperBound) || get<PrimitiveType>(upperBound))
|
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
|
// Any constraint that might have mutated source may now mutate target
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
auto it = mutatedFreeTypeToConstraint.find(source);
|
auto it = mutatedFreeTypeToConstraint.find(source);
|
||||||
if (it != mutatedFreeTypeToConstraint.end())
|
if (it != mutatedFreeTypeToConstraint.end())
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
LUAU_FASTINTVARIABLE(LuauIndentTypeMismatchMaxTypeLength, 10)
|
LUAU_FASTINTVARIABLE(LuauIndentTypeMismatchMaxTypeLength, 10)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauBetterCannotCallFunctionPrimitive)
|
LUAU_FASTFLAGVARIABLE(LuauBetterCannotCallFunctionPrimitive)
|
||||||
|
|
||||||
|
@ -663,7 +663,7 @@ struct ErrorConverter
|
||||||
}
|
}
|
||||||
|
|
||||||
// binary operators
|
// 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())
|
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 ";
|
std::string result = "Operator '" + std::string(binaryString->second) + "' could not be applied to operands of types ";
|
||||||
|
@ -718,7 +718,7 @@ 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" +
|
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";
|
||||||
|
|
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/Autocomplete.h"
|
||||||
#include "Luau/Common.h"
|
#include "Luau/Common.h"
|
||||||
#include "Luau/EqSatSimplification.h"
|
#include "Luau/EqSatSimplification.h"
|
||||||
|
#include "Luau/ExpectedTypeVisitor.h"
|
||||||
#include "Luau/ModuleResolver.h"
|
#include "Luau/ModuleResolver.h"
|
||||||
#include "Luau/Parser.h"
|
#include "Luau/Parser.h"
|
||||||
#include "Luau/ParseOptions.h"
|
#include "Luau/ParseOptions.h"
|
||||||
|
@ -35,6 +36,8 @@ LUAU_FASTFLAGVARIABLE(LuauFragmentAcMemoryLeak)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauGlobalVariableModuleIsolation)
|
LUAU_FASTFLAGVARIABLE(LuauGlobalVariableModuleIsolation)
|
||||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFragmentAutocompleteIfRecommendations)
|
LUAU_FASTFLAGVARIABLE(LuauFragmentAutocompleteIfRecommendations)
|
||||||
|
LUAU_FASTFLAG(LuauExpectedTypeVisitor)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauPopulateRefinedTypesInFragmentFromOldSolver)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -669,6 +672,7 @@ void cloneTypesFromFragment(
|
||||||
destScope->lvalueTypes[d] = Luau::cloneIncremental(*lValue, *destArena, cloneState, destScope);
|
destScope->lvalueTypes[d] = Luau::cloneIncremental(*lValue, *destArena, cloneState, destScope);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& [d, loc] : f.localBindingsReferenced)
|
for (const auto& [d, loc] : f.localBindingsReferenced)
|
||||||
{
|
{
|
||||||
if (std::optional<std::pair<Symbol, Binding>> pair = staleScope->linearSearchForBindingPair(loc->name.value, true))
|
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.
|
// 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
|
// If the actual type alias appears in the fragment on the lhs as a definition (in declaredAliases), it will be processed during typechecking
|
||||||
// anyway
|
// anyway
|
||||||
|
@ -1191,6 +1210,19 @@ FragmentTypeCheckResult typecheckFragment_(
|
||||||
|
|
||||||
reportWaypoint(reporter, FragmentAutocompleteWaypoint::ConstraintSolverEnd);
|
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
|
// In frontend we would forbid internal types
|
||||||
// because this is just for autocomplete, we don't actually care
|
// 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
|
// 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);
|
FrontendOptions frontendOptions = opts.value_or(frontend.options);
|
||||||
const ScopePtr& closestScope = FFlag::LuauBetterScopeSelection ? findClosestScope(module, parseResult.scopePos)
|
const ScopePtr& closestScope = FFlag::LuauBetterScopeSelection ? findClosestScope(module, parseResult.scopePos)
|
||||||
: findClosestScope_DEPRECATED(module, parseResult.nearestStatement);
|
: 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);
|
result.ancestry = std::move(parseResult.ancestry);
|
||||||
reportFragmentString(reporter, tryParse->fragmentToParse);
|
reportFragmentString(reporter, tryParse->fragmentToParse);
|
||||||
return {FragmentTypeCheckStatus::Success, result};
|
return {FragmentTypeCheckStatus::Success, result};
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "Luau/DataFlowGraph.h"
|
#include "Luau/DataFlowGraph.h"
|
||||||
#include "Luau/DcrLogger.h"
|
#include "Luau/DcrLogger.h"
|
||||||
#include "Luau/EqSatSimplification.h"
|
#include "Luau/EqSatSimplification.h"
|
||||||
|
#include "Luau/ExpectedTypeVisitor.h"
|
||||||
#include "Luau/FileResolver.h"
|
#include "Luau/FileResolver.h"
|
||||||
#include "Luau/NonStrictTypeChecker.h"
|
#include "Luau/NonStrictTypeChecker.h"
|
||||||
#include "Luau/NotNull.h"
|
#include "Luau/NotNull.h"
|
||||||
|
@ -39,13 +40,14 @@ LUAU_FASTINT(LuauTarjanChildLimit)
|
||||||
LUAU_FASTFLAG(LuauInferInNoCheckMode)
|
LUAU_FASTFLAG(LuauInferInNoCheckMode)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3)
|
LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3)
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson)
|
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJsonFile)
|
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJsonFile)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauForbidInternalTypes)
|
LUAU_FASTFLAGVARIABLE(DebugLuauForbidInternalTypes)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauForceStrictMode)
|
LUAU_FASTFLAGVARIABLE(DebugLuauForceStrictMode)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauForceNonStrictMode)
|
LUAU_FASTFLAGVARIABLE(DebugLuauForceNonStrictMode)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNewSolverTypecheckCatchTimeouts)
|
LUAU_FASTFLAGVARIABLE(LuauNewSolverTypecheckCatchTimeouts)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauExpectedTypeVisitor)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -1436,13 +1438,13 @@ ModulePtr check(
|
||||||
requireCycles
|
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
|
// This optional<> only exists so that we can run one constructor when the flag
|
||||||
// is set, and another when it is unset.
|
// is set, and another when it is unset.
|
||||||
std::optional<ConstraintSolver> cs;
|
std::optional<ConstraintSolver> cs;
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
ConstraintSet constraintSet = cg.run(sourceModule.root);
|
ConstraintSet constraintSet = cg.run(sourceModule.root);
|
||||||
result->errors = std::move(constraintSet.errors);
|
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);
|
unfreeze(result->interfaceTypes);
|
||||||
result->clonePublicInterface(builtinTypes, *iceHandler);
|
result->clonePublicInterface(builtinTypes, *iceHandler);
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
|
LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauEagerGeneralization)
|
LUAU_FASTFLAGVARIABLE(LuauEagerGeneralization2)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -469,7 +469,7 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
|
|
||||||
bool visit(TypeId ty, const FreeType& ft) override
|
bool visit(TypeId ty, const FreeType& ft) override
|
||||||
{
|
{
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
if (!subsumes(scope, ft.scope))
|
if (!subsumes(scope, ft.scope))
|
||||||
return true;
|
return true;
|
||||||
|
@ -520,7 +520,7 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
|
|
||||||
if ((tt.state == TableState::Free || tt.state == TableState::Unsealed) && subsumes(scope, tt.scope))
|
if ((tt.state == TableState::Free || tt.state == TableState::Unsealed) && subsumes(scope, tt.scope))
|
||||||
{
|
{
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
unsealedTables.insert(ty);
|
unsealedTables.insert(ty);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -574,7 +574,6 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
traverse(*prop.writeTy);
|
traverse(*prop.writeTy);
|
||||||
polarity = p;
|
polarity = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -594,7 +593,7 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
|
|
||||||
if (tt.indexer)
|
if (tt.indexer)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
// {[K]: V} is equivalent to three functions: get, set, and iterate
|
// {[K]: V} is equivalent to three functions: get, set, and iterate
|
||||||
//
|
//
|
||||||
|
@ -652,7 +651,7 @@ struct FreeTypeSearcher : TypeVisitor
|
||||||
if (!subsumes(scope, ftp.scope))
|
if (!subsumes(scope, ftp.scope))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
GeneralizationParams<TypePackId>& params = typePacks[tp];
|
GeneralizationParams<TypePackId>& params = typePacks[tp];
|
||||||
++params.useCount;
|
++params.useCount;
|
||||||
|
@ -1247,8 +1246,7 @@ struct RemoveType : Substitution // NOLINT
|
||||||
* @param needle The type to be removed.
|
* @param needle The type to be removed.
|
||||||
*/
|
*/
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static std::optional<
|
static std::optional<TypeId> removeType(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, TypeId haystack, TypeId needle)
|
||||||
TypeId> removeType(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, TypeId haystack, TypeId needle)
|
|
||||||
{
|
{
|
||||||
RemoveType rt{builtinTypes, arena, needle};
|
RemoveType rt{builtinTypes, arena, needle};
|
||||||
return rt.substitute(haystack);
|
return rt.substitute(haystack);
|
||||||
|
@ -1276,7 +1274,7 @@ GeneralizationResult<TypeId> generalizeType(
|
||||||
|
|
||||||
if (!hasLowerBound && !hasUpperBound)
|
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);
|
emplaceType<BoundType>(asMutable(freeTy), builtinTypes->unknownType);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1308,7 +1306,7 @@ GeneralizationResult<TypeId> generalizeType(
|
||||||
|
|
||||||
if (follow(lb) != freeTy)
|
if (follow(lb) != freeTy)
|
||||||
emplaceType<BoundType>(asMutable(freeTy), lb);
|
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);
|
emplaceType<BoundType>(asMutable(freeTy), builtinTypes->unknownType);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1425,7 +1423,7 @@ std::optional<TypeId> generalize(
|
||||||
FreeTypeSearcher fts{scope, cachedTypes};
|
FreeTypeSearcher fts{scope, cachedTypes};
|
||||||
fts.traverse(ty);
|
fts.traverse(ty);
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
FunctionType* functionTy = getMutable<FunctionType>(ty);
|
FunctionType* functionTy = getMutable<FunctionType>(ty);
|
||||||
auto pushGeneric = [&](TypeId t)
|
auto pushGeneric = [&](TypeId t)
|
||||||
|
@ -1586,7 +1584,6 @@ struct GenericCounter : TypeVisitor
|
||||||
traverse(*prop.writeTy);
|
traverse(*prop.writeTy);
|
||||||
polarity = p;
|
polarity = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1653,7 +1650,7 @@ void pruneUnnecessaryGenerics(
|
||||||
TypeId ty
|
TypeId ty
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (!FFlag::LuauEagerGeneralization)
|
if (!FFlag::LuauEagerGeneralization2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ty = follow(ty);
|
ty = follow(ty);
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#include "Luau/Scope.h"
|
#include "Luau/Scope.h"
|
||||||
#include "Luau/VisitType.h"
|
#include "Luau/VisitType.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -133,7 +133,7 @@ struct InferPolarity : TypeVisitor
|
||||||
template<typename TID>
|
template<typename TID>
|
||||||
static void inferGenericPolarities_(NotNull<TypeArena> arena, NotNull<Scope> scope, TID ty)
|
static void inferGenericPolarities_(NotNull<TypeArena> arena, NotNull<Scope> scope, TID ty)
|
||||||
{
|
{
|
||||||
if (!FFlag::LuauEagerGeneralization)
|
if (!FFlag::LuauEagerGeneralization2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
InferPolarity infer{arena, scope};
|
InferPolarity infer{arena, scope};
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2);
|
LUAU_FASTFLAG(LuauSolverV2);
|
||||||
LUAU_FASTFLAG(LuauRetainDefinitionAliasLocations)
|
LUAU_FASTFLAG(LuauUserTypeFunctionAliases)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -265,10 +265,7 @@ struct ClonePublicInterface : Substitution
|
||||||
|
|
||||||
TypeId type = cloneType(tf.type);
|
TypeId type = cloneType(tf.type);
|
||||||
|
|
||||||
if (FFlag::LuauRetainDefinitionAliasLocations)
|
|
||||||
return TypeFun{typeParams, typePackParams, type, tf.definitionLocation};
|
return TypeFun{typeParams, typePackParams, type, tf.definitionLocation};
|
||||||
else
|
|
||||||
return TypeFun{typeParams, typePackParams, type};
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -309,6 +306,14 @@ void Module::clonePublicInterface(NotNull<BuiltinTypes> builtinTypes, InternalEr
|
||||||
ty = clonePublicInterface.cloneType(ty);
|
ty = clonePublicInterface.cloneType(ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FFlag::LuauUserTypeFunctionAliases)
|
||||||
|
{
|
||||||
|
for (auto& tf : typeFunctionAliases)
|
||||||
|
{
|
||||||
|
*tf = clonePublicInterface.cloneTypeFun(*tf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Copy external stuff over to Module itself
|
// Copy external stuff over to Module itself
|
||||||
this->returnType = moduleScope->returnType;
|
this->returnType = moduleScope->returnType;
|
||||||
this->exportedTypeBindings = moduleScope->exportedTypeBindings;
|
this->exportedTypeBindings = moduleScope->exportedTypeBindings;
|
||||||
|
|
|
@ -24,6 +24,7 @@ LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictVisitTypes2)
|
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictVisitTypes2)
|
||||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictFixGenericTypePacks)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -1091,6 +1092,24 @@ struct NonStrictTypeChecker
|
||||||
Scope* scope = findInnermostScope(tp->location);
|
Scope* scope = findInnermostScope(tp->location);
|
||||||
LUAU_ASSERT(scope);
|
LUAU_ASSERT(scope);
|
||||||
|
|
||||||
|
if (FFlag::LuauNewNonStrictFixGenericTypePacks)
|
||||||
|
{
|
||||||
|
if (std::optional<TypePackId> alias = scope->lookupPack(tp->genericName.value))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (scope->lookupType(tp->genericName.value))
|
||||||
|
return reportError(
|
||||||
|
SwappedGenericTypeParameter{
|
||||||
|
tp->genericName.value,
|
||||||
|
SwappedGenericTypeParameter::Kind::Pack,
|
||||||
|
},
|
||||||
|
tp->location
|
||||||
|
);
|
||||||
|
|
||||||
|
reportError(UnknownSymbol{tp->genericName.value, UnknownSymbol::Context::Type}, tp->location);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
std::optional<TypePackId> alias = scope->lookupPack(tp->genericName.value);
|
std::optional<TypePackId> alias = scope->lookupPack(tp->genericName.value);
|
||||||
if (!alias.has_value())
|
if (!alias.has_value())
|
||||||
{
|
{
|
||||||
|
@ -1110,6 +1129,7 @@ struct NonStrictTypeChecker
|
||||||
reportError(UnknownSymbol{tp->genericName.value, UnknownSymbol::Context::Type}, tp->location);
|
reportError(UnknownSymbol{tp->genericName.value, UnknownSymbol::Context::Type}, tp->location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void visitGenerics(AstArray<AstGenericType*> generics, AstArray<AstGenericTypePack*> genericPacks)
|
void visitGenerics(AstArray<AstGenericType*> generics, AstArray<AstGenericTypePack*> genericPacks)
|
||||||
{
|
{
|
||||||
|
|
|
@ -33,150 +33,6 @@ static bool shouldEarlyExit(NormalizationResult res)
|
||||||
return false;
|
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() {}
|
||||||
|
|
||||||
NormalizedStringType::NormalizedStringType(bool isCofinite, std::map<std::string, TypeId> singletons)
|
NormalizedStringType::NormalizedStringType(bool isCofinite, std::map<std::string, TypeId> singletons)
|
||||||
|
|
|
@ -255,6 +255,29 @@ bool Scope::shouldWarnGlobal(std::string name) const
|
||||||
return false;
|
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)
|
bool subsumesStrict(Scope* left, Scope* right)
|
||||||
{
|
{
|
||||||
while (right)
|
while (right)
|
||||||
|
|
|
@ -21,7 +21,7 @@ LUAU_FASTFLAGVARIABLE(DebugLuauSubtypingCheckPathValidity)
|
||||||
LUAU_FASTINTVARIABLE(LuauSubtypingReasoningLimit, 100)
|
LUAU_FASTINTVARIABLE(LuauSubtypingReasoningLimit, 100)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSubtypeGenericsAndNegations)
|
LUAU_FASTFLAGVARIABLE(LuauSubtypeGenericsAndNegations)
|
||||||
LUAU_FASTFLAG(LuauClipVariadicAnysFromArgsToGenericFuncs2)
|
LUAU_FASTFLAG(LuauClipVariadicAnysFromArgsToGenericFuncs2)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -675,20 +675,21 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
||||||
result.isSubtype = ok;
|
result.isSubtype = ok;
|
||||||
result.isCacheable = false;
|
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);
|
bool ok = bindGeneric(env, subTy, superTy);
|
||||||
result.isSubtype = ok;
|
result.isSubtype = ok;
|
||||||
result.isCacheable = false;
|
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
|
// Any two free types are potentially subtypes of one another because
|
||||||
// both of them could be narrowed to never.
|
// both of them could be narrowed to never.
|
||||||
result = {true};
|
result = {true};
|
||||||
result.assumedConstraints.emplace_back(SubtypeConstraint{subTy, superTy});
|
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)
|
// Given SubTy <: (LB <: SuperTy <: UB)
|
||||||
//
|
//
|
||||||
|
@ -703,7 +704,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
||||||
if (result.isSubtype)
|
if (result.isSubtype)
|
||||||
result.assumedConstraints.emplace_back(SubtypeConstraint{subTy, superTy});
|
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
|
// Given (LB <: SubTy <: UB) <: SuperTy
|
||||||
//
|
//
|
||||||
|
@ -767,7 +768,8 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
||||||
result.isSubtype = ok;
|
result.isSubtype = ok;
|
||||||
result.isCacheable = false;
|
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);
|
bool ok = bindGeneric(env, subTy, superTy);
|
||||||
result.isSubtype = ok;
|
result.isSubtype = ok;
|
||||||
|
@ -1450,7 +1452,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Tabl
|
||||||
{
|
{
|
||||||
SubtypingResult result{true};
|
SubtypingResult result{true};
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
if (subTable->props.empty() && !subTable->indexer && subTable->state == TableState::Sealed && superTable->indexer)
|
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)
|
if (subTable->indexer)
|
||||||
result.andAlso(isInvariantWith(env, *subTable->indexer, *superTable->indexer, scope));
|
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
|
// As above, we assume that {| |} <: {T} because the unsealed table
|
||||||
// on the left will eventually gain the necessary indexer.
|
// 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)};
|
return {isSubclass(subExternType, superExternType)};
|
||||||
}
|
}
|
||||||
|
@ -1737,9 +1744,8 @@ SubtypingResult Subtyping::isCovariantWith(
|
||||||
|
|
||||||
SubtypingResult result = isCovariantWith(env, subNorm->tops, superNorm->tops, scope);
|
SubtypingResult result = isCovariantWith(env, subNorm->tops, superNorm->tops, scope);
|
||||||
result.andAlso(isCovariantWith(env, subNorm->booleans, superNorm->booleans, scope));
|
result.andAlso(isCovariantWith(env, subNorm->booleans, superNorm->booleans, scope));
|
||||||
result.andAlso(
|
result.andAlso(isCovariantWith(env, subNorm->externTypes, superNorm->externTypes, scope)
|
||||||
isCovariantWith(env, subNorm->externTypes, superNorm->externTypes, scope).orElse(isCovariantWith(env, subNorm->externTypes, superNorm->tables, scope))
|
.orElse(isCovariantWith(env, subNorm->externTypes, superNorm->tables, scope)));
|
||||||
);
|
|
||||||
result.andAlso(isCovariantWith(env, subNorm->errors, superNorm->errors, scope));
|
result.andAlso(isCovariantWith(env, subNorm->errors, superNorm->errors, scope));
|
||||||
result.andAlso(isCovariantWith(env, subNorm->nils, superNorm->nils, scope));
|
result.andAlso(isCovariantWith(env, subNorm->nils, superNorm->nils, scope));
|
||||||
result.andAlso(isCovariantWith(env, subNorm->numbers, superNorm->numbers, scope));
|
result.andAlso(isCovariantWith(env, subNorm->numbers, superNorm->numbers, scope));
|
||||||
|
|
|
@ -19,16 +19,6 @@ LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
|
||||||
namespace Luau
|
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(
|
TypeId matchLiteralType(
|
||||||
NotNull<DenseHashMap<const AstExpr*, TypeId>> astTypes,
|
NotNull<DenseHashMap<const AstExpr*, TypeId>> astTypes,
|
||||||
NotNull<DenseHashMap<const AstExpr*, TypeId>> astExpectedTypes,
|
NotNull<DenseHashMap<const AstExpr*, TypeId>> astExpectedTypes,
|
||||||
|
|
|
@ -1381,7 +1381,11 @@ struct Printer
|
||||||
LUAU_ASSERT(!forVarArg);
|
LUAU_ASSERT(!forVarArg);
|
||||||
if (const auto cstNode = lookupCstNode<CstTypePackExplicit>(explicitTp))
|
if (const auto cstNode = lookupCstNode<CstTypePackExplicit>(explicitTp))
|
||||||
visualizeTypeList(
|
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
|
else
|
||||||
visualizeTypeList(explicitTp->typeList, unconditionallyParenthesize);
|
visualizeTypeList(explicitTp->typeList, unconditionallyParenthesize);
|
||||||
|
@ -1986,7 +1990,7 @@ struct Printer
|
||||||
if (FFlag::LuauStoreLocalAnnotationColonPositions)
|
if (FFlag::LuauStoreLocalAnnotationColonPositions)
|
||||||
visualize(*a->var, cstNode ? cstNode->annotationColonPosition : Position{0, 0});
|
visualize(*a->var, cstNode ? cstNode->annotationColonPosition : Position{0, 0});
|
||||||
else
|
else
|
||||||
visualize(*a->var, Position{0,0});
|
visualize(*a->var, Position{0, 0});
|
||||||
|
|
||||||
if (cstNode)
|
if (cstNode)
|
||||||
advance(cstNode->equalsPosition);
|
advance(cstNode->equalsPosition);
|
||||||
|
|
|
@ -1079,7 +1079,7 @@ void persist(TypeId ty)
|
||||||
queue.push_back(ttv->indexer->indexResultType);
|
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)
|
for (const auto& [_name, prop] : etv->props)
|
||||||
queue.push_back(prop.type());
|
queue.push_back(prop.type());
|
||||||
|
|
|
@ -34,6 +34,7 @@ LUAU_FASTFLAGVARIABLE(LuauTypeCheckerAcceptNumberConcats)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTypeCheckerStricterIndexCheck)
|
LUAU_FASTFLAGVARIABLE(LuauTypeCheckerStricterIndexCheck)
|
||||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
|
LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
|
||||||
|
LUAU_FASTFLAG(LuauNewNonStrictFixGenericTypePacks)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauReportSubtypingErrors)
|
LUAU_FASTFLAGVARIABLE(LuauReportSubtypingErrors)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSkipMalformedTypeAliases)
|
LUAU_FASTFLAGVARIABLE(LuauSkipMalformedTypeAliases)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTableLiteralSubtypeSpecificCheck)
|
LUAU_FASTFLAGVARIABLE(LuauTableLiteralSubtypeSpecificCheck)
|
||||||
|
@ -782,7 +783,6 @@ void TypeChecker2::visit(AstStatReturn* ret)
|
||||||
|
|
||||||
for (AstExpr* expr : ret->list)
|
for (AstExpr* expr : ret->list)
|
||||||
visit(expr, ValueContext::RValue);
|
visit(expr, ValueContext::RValue);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TypeChecker2::visit(AstStatExpr* expr)
|
void TypeChecker2::visit(AstStatExpr* expr)
|
||||||
|
@ -2796,6 +2796,24 @@ void TypeChecker2::visit(AstTypePackGeneric* tp)
|
||||||
Scope* scope = findInnermostScope(tp->location);
|
Scope* scope = findInnermostScope(tp->location);
|
||||||
LUAU_ASSERT(scope);
|
LUAU_ASSERT(scope);
|
||||||
|
|
||||||
|
if (FFlag::LuauNewNonStrictFixGenericTypePacks)
|
||||||
|
{
|
||||||
|
if (std::optional<TypePackId> alias = scope->lookupPack(tp->genericName.value))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (scope->lookupType(tp->genericName.value))
|
||||||
|
return reportError(
|
||||||
|
SwappedGenericTypeParameter{
|
||||||
|
tp->genericName.value,
|
||||||
|
SwappedGenericTypeParameter::Kind::Pack,
|
||||||
|
},
|
||||||
|
tp->location
|
||||||
|
);
|
||||||
|
|
||||||
|
reportError(UnknownSymbol{tp->genericName.value, UnknownSymbol::Context::Type}, tp->location);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
std::optional<TypePackId> alias = scope->lookupPack(tp->genericName.value);
|
std::optional<TypePackId> alias = scope->lookupPack(tp->genericName.value);
|
||||||
if (!alias.has_value())
|
if (!alias.has_value())
|
||||||
{
|
{
|
||||||
|
@ -2814,6 +2832,7 @@ void TypeChecker2::visit(AstTypePackGeneric* tp)
|
||||||
reportError(UnknownSymbol{tp->genericName.value, UnknownSymbol::Context::Type}, tp->location);
|
reportError(UnknownSymbol{tp->genericName.value, UnknownSymbol::Context::Type}, tp->location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename TID>
|
template<typename TID>
|
||||||
|
@ -2944,14 +2963,6 @@ void TypeChecker2::explainError(TypePackId subTy, TypePackId superTy, Location l
|
||||||
reportError(TypePackMismatch{superTy, subTy, reasonings.toString()}, location);
|
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)
|
bool TypeChecker2::testPotentialLiteralIsSubtype(AstExpr* expr, TypeId expectedType)
|
||||||
{
|
{
|
||||||
auto exprType = follow(lookupType(expr));
|
auto exprType = follow(lookupType(expr));
|
||||||
|
@ -2982,7 +2993,7 @@ bool TypeChecker2::testPotentialLiteralIsSubtype(AstExpr* expr, TypeId expectedT
|
||||||
return testIsSubtype(exprType, expectedType, expr->location);
|
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)
|
for (const auto& [name, prop] : expectedTableType->props)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauEnableWriteOnlyProperties)
|
if (FFlag::LuauEnableWriteOnlyProperties)
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "Luau/Unifier2.h"
|
#include "Luau/Unifier2.h"
|
||||||
#include "Luau/VecDeque.h"
|
#include "Luau/VecDeque.h"
|
||||||
#include "Luau/VisitType.h"
|
#include "Luau/VisitType.h"
|
||||||
|
#include "Luau/ApplyTypeFunction.h"
|
||||||
|
|
||||||
#include "lua.h"
|
#include "lua.h"
|
||||||
#include "lualib.h"
|
#include "lualib.h"
|
||||||
|
@ -47,8 +48,8 @@ LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyApplicationCartesianProductLimit, 5'0
|
||||||
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyUseGuesserDepth, -1);
|
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyUseGuesserDepth, -1);
|
||||||
|
|
||||||
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies)
|
LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies)
|
||||||
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
||||||
|
@ -56,6 +57,7 @@ LUAU_FASTFLAGVARIABLE(LuauNarrowIntersectionNevers)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauRefineWaitForBlockedTypesInTarget)
|
LUAU_FASTFLAGVARIABLE(LuauRefineWaitForBlockedTypesInTarget)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNoMoreInjectiveTypeFunctions)
|
LUAU_FASTFLAGVARIABLE(LuauNoMoreInjectiveTypeFunctions)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNotAllBinaryTypeFunsHaveDefaults)
|
LUAU_FASTFLAGVARIABLE(LuauNotAllBinaryTypeFunsHaveDefaults)
|
||||||
|
LUAU_FASTFLAG(LuauUserTypeFunctionAliases)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -283,7 +285,7 @@ struct TypeFunctionReducer
|
||||||
}
|
}
|
||||||
else if (is<GenericType>(ty))
|
else if (is<GenericType>(ty))
|
||||||
{
|
{
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
return SkipTestResult::Generic;
|
return SkipTestResult::Generic;
|
||||||
else
|
else
|
||||||
return SkipTestResult::Irreducible;
|
return SkipTestResult::Irreducible;
|
||||||
|
@ -305,7 +307,7 @@ struct TypeFunctionReducer
|
||||||
}
|
}
|
||||||
else if (is<GenericTypePack>(ty))
|
else if (is<GenericTypePack>(ty))
|
||||||
{
|
{
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
return SkipTestResult::Generic;
|
return SkipTestResult::Generic;
|
||||||
else
|
else
|
||||||
return SkipTestResult::Irreducible;
|
return SkipTestResult::Irreducible;
|
||||||
|
@ -569,6 +571,27 @@ struct LuauTempThreadPopper
|
||||||
lua_State* L = nullptr;
|
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(
|
static FunctionGraphReductionResult reduceFunctionsInternal(
|
||||||
VecDeque<TypeId> queuedTys,
|
VecDeque<TypeId> queuedTys,
|
||||||
VecDeque<TypePackId> queuedTps,
|
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(
|
TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
|
||||||
TypeId instance,
|
TypeId instance,
|
||||||
const std::vector<TypeId>& typeParams,
|
const std::vector<TypeId>& typeParams,
|
||||||
|
@ -819,11 +933,21 @@ TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
|
||||||
for (auto typeParam : typeParams)
|
for (auto typeParam : typeParams)
|
||||||
check.traverse(follow(typeParam));
|
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())
|
if (!check.blockingTypes.empty())
|
||||||
return {std::nullopt, Reduction::MaybeOk, check.blockingTypes, {}};
|
return {std::nullopt, Reduction::MaybeOk, check.blockingTypes, {}};
|
||||||
|
|
||||||
// Ensure that whole type function environment is registered
|
// 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
|
// Cannot evaluate if a potential dependency couldn't be parsed
|
||||||
if (definition.first->hasErrors)
|
if (definition.first->hasErrors)
|
||||||
|
@ -849,8 +973,13 @@ TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
|
||||||
lua_State* L = lua_newthread(global);
|
lua_State* L = lua_newthread(global);
|
||||||
LuauTempThreadPopper popper(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
|
// 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
|
// Environment table has to be filled only once in the current execution context
|
||||||
if (ctx->typeFunctionRuntime->initialized.find(curr.first))
|
if (ctx->typeFunctionRuntime->initialized.find(curr.first))
|
||||||
|
@ -870,7 +999,7 @@ TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
|
||||||
lua_getfenv(L, -1);
|
lua_getfenv(L, -1);
|
||||||
lua_setreadonly(L, -1, false);
|
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
|
// Filter visibility based on original scope depth
|
||||||
if (definition.second >= curr.second)
|
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_setreadonly(L, -1, true);
|
||||||
lua_pop(L, 2);
|
lua_pop(L, 2);
|
||||||
}
|
}
|
||||||
|
@ -901,8 +1063,6 @@ TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
|
||||||
|
|
||||||
resetTypeFunctionState(L);
|
resetTypeFunctionState(L);
|
||||||
|
|
||||||
std::unique_ptr<TypeFunctionRuntimeBuilderState> runtimeBuilder = std::make_unique<TypeFunctionRuntimeBuilderState>(ctx);
|
|
||||||
|
|
||||||
// Push serialized arguments onto the stack
|
// Push serialized arguments onto the stack
|
||||||
for (auto typeParam : typeParams)
|
for (auto typeParam : typeParams)
|
||||||
{
|
{
|
||||||
|
@ -1101,7 +1261,7 @@ TypeFunctionReductionResult<TypeId> unmTypeFunction(
|
||||||
if (isPending(operandTy, ctx->solver))
|
if (isPending(operandTy, ctx->solver))
|
||||||
return {std::nullopt, Reduction::MaybeOk, {operandTy}, {}};
|
return {std::nullopt, Reduction::MaybeOk, {operandTy}, {}};
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
operandTy = follow(operandTy);
|
operandTy = follow(operandTy);
|
||||||
|
|
||||||
std::shared_ptr<const NormalizedType> normTy = ctx->normalizer->normalize(operandTy);
|
std::shared_ptr<const NormalizedType> normTy = ctx->normalizer->normalize(operandTy);
|
||||||
|
@ -1698,7 +1858,7 @@ TypeFunctionReductionResult<TypeId> orTypeFunction(
|
||||||
return {rhsTy, Reduction::MaybeOk, {}, {}};
|
return {rhsTy, Reduction::MaybeOk, {}, {}};
|
||||||
|
|
||||||
// check to see if both operand types are resolved enough, and wait to reduce if not
|
// 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))
|
if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(lhsTy))
|
||||||
return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}};
|
return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}};
|
||||||
|
@ -1745,7 +1905,7 @@ static TypeFunctionReductionResult<TypeId> comparisonTypeFunction(
|
||||||
if (lhsTy == instance || rhsTy == instance)
|
if (lhsTy == instance || rhsTy == instance)
|
||||||
return {ctx->builtins->neverType, Reduction::MaybeOk, {}, {}};
|
return {ctx->builtins->neverType, Reduction::MaybeOk, {}, {}};
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(lhsTy))
|
if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(lhsTy))
|
||||||
return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}};
|
return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}};
|
||||||
|
@ -2099,7 +2259,7 @@ bool isSimpleDiscriminant(TypeId ty)
|
||||||
return isApproximateTruthy(ty) || isApproximateFalsy(ty);
|
return isApproximateTruthy(ty) || isApproximateFalsy(ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} // namespace
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
||||||
TypeId instance,
|
TypeId instance,
|
||||||
|
@ -2119,8 +2279,7 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
||||||
for (size_t i = 1; i < typeParams.size(); i++)
|
for (size_t i = 1; i < typeParams.size(); i++)
|
||||||
discriminantTypes.push_back(follow(typeParams.at(i)));
|
discriminantTypes.push_back(follow(typeParams.at(i)));
|
||||||
|
|
||||||
const bool targetIsPending = FFlag::LuauEagerGeneralization
|
const bool targetIsPending = FFlag::LuauEagerGeneralization2 ? is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(targetTy)
|
||||||
? is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(targetTy)
|
|
||||||
: isPending(targetTy, ctx->solver);
|
: isPending(targetTy, ctx->solver);
|
||||||
|
|
||||||
// check to see if both operand types are resolved enough, and wait to reduce if not
|
// check to see if both operand types are resolved enough, and wait to reduce if not
|
||||||
|
@ -2206,7 +2365,7 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
||||||
if (is<TableType>(target) || isSimpleDiscriminant(discriminant))
|
if (is<TableType>(target) || isSimpleDiscriminant(discriminant))
|
||||||
{
|
{
|
||||||
SimplifyResult result = simplifyIntersection(ctx->builtins, ctx->arena, target, discriminant);
|
SimplifyResult result = simplifyIntersection(ctx->builtins, ctx->arena, target, discriminant);
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
// Simplification considers free and generic types to be
|
// Simplification considers free and generic types to be
|
||||||
// 'blocking', but that's not suitable for refine<>.
|
// 'blocking', but that's not suitable for refine<>.
|
||||||
|
@ -2615,8 +2774,8 @@ TypeFunctionReductionResult<TypeId> keyofFunctionImpl(
|
||||||
if (!normTy)
|
if (!normTy)
|
||||||
return {std::nullopt, Reduction::MaybeOk, {}, {}};
|
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
|
// 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
|
||||||
// as well)
|
// types as well)
|
||||||
if (normTy->hasTables() == normTy->hasExternTypes())
|
if (normTy->hasTables() == normTy->hasExternTypes())
|
||||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||||
|
|
||||||
|
@ -2985,7 +3144,8 @@ TypeFunctionReductionResult<TypeId> indexFunctionImpl(
|
||||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||||
|
|
||||||
// at least one class is guaranteed to be in the iterator by .hasExternTypes()
|
// 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);
|
auto externTy = get<ExternType>(*externTypeIter);
|
||||||
if (!externTy)
|
if (!externTy)
|
||||||
|
@ -3335,7 +3495,7 @@ BuiltinTypeFunctions::BuiltinTypeFunctions()
|
||||||
, ltFunc{"lt", ltTypeFunction}
|
, ltFunc{"lt", ltTypeFunction}
|
||||||
, leFunc{"le", leTypeFunction}
|
, leFunc{"le", leTypeFunction}
|
||||||
, eqFunc{"eq", eqTypeFunction}
|
, eqFunc{"eq", eqTypeFunction}
|
||||||
, refineFunc{"refine", refineTypeFunction, /*canReduceGenerics*/ FFlag::LuauEagerGeneralization}
|
, refineFunc{"refine", refineTypeFunction, /*canReduceGenerics*/ FFlag::LuauEagerGeneralization2}
|
||||||
, singletonFunc{"singleton", singletonTypeFunction}
|
, singletonFunc{"singleton", singletonTypeFunction}
|
||||||
, unionFunc{"union", unionTypeFunction}
|
, unionFunc{"union", unionTypeFunction}
|
||||||
, intersectFunc{"intersect", intersectTypeFunction}
|
, 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)));
|
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
|
// 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
|
// class
|
||||||
target = typeFunctionRuntime->typeArena.allocate(
|
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionExternType{{}, std::nullopt, std::nullopt, std::nullopt, std::nullopt, ty});
|
||||||
TypeFunctionExternType{{}, std::nullopt, std::nullopt, std::nullopt, std::nullopt, ty}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
else if (auto g = get<GenericType>(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(LuauInstantiateInSubtyping)
|
||||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauRetainDefinitionAliasLocations)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauReduceCheckBinaryExprStackPressure)
|
LUAU_FASTFLAGVARIABLE(LuauReduceCheckBinaryExprStackPressure)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
|
@ -1664,10 +1663,7 @@ void TypeChecker::prototype(const ScopePtr& scope, const AstStatTypeAlias& typea
|
||||||
FreeType* ftv = getMutable<FreeType>(ty);
|
FreeType* ftv = getMutable<FreeType>(ty);
|
||||||
LUAU_ASSERT(ftv);
|
LUAU_ASSERT(ftv);
|
||||||
ftv->forwardedTypeAlias = true;
|
ftv->forwardedTypeAlias = true;
|
||||||
if (FFlag::LuauRetainDefinitionAliasLocations)
|
|
||||||
bindingsMap[name] = {std::move(generics), std::move(genericPacks), ty, typealias.location};
|
bindingsMap[name] = {std::move(generics), std::move(genericPacks), ty, typealias.location};
|
||||||
else
|
|
||||||
bindingsMap[name] = {std::move(generics), std::move(genericPacks), ty};
|
|
||||||
|
|
||||||
scope->typeAliasLocations[name] = typealias.location;
|
scope->typeAliasLocations[name] = typealias.location;
|
||||||
scope->typeAliasNameLocations[name] = typealias.nameLocation;
|
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});
|
TypeId metaTy = addType(TableType{TableState::Sealed, scope->level});
|
||||||
|
|
||||||
etv->metatable = metaTy;
|
etv->metatable = metaTy;
|
||||||
if (FFlag::LuauRetainDefinitionAliasLocations)
|
|
||||||
scope->exportedTypeBindings[className] = TypeFun{{}, classTy, declaredExternType.location};
|
scope->exportedTypeBindings[className] = TypeFun{{}, classTy, declaredExternType.location};
|
||||||
else
|
|
||||||
scope->exportedTypeBindings[className] = TypeFun{{}, classTy};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatDeclareExternType& declaredExternType)
|
ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatDeclareExternType& declaredExternType)
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauErrorSuppressionTypeFunctionArgs)
|
LUAU_FASTFLAGVARIABLE(LuauErrorSuppressionTypeFunctionArgs)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
|
@ -306,7 +306,7 @@ TypePack extendTypePack(
|
||||||
TypePack newPack;
|
TypePack newPack;
|
||||||
newPack.tail = arena.freshTypePack(ftp->scope, ftp->polarity);
|
newPack.tail = arena.freshTypePack(ftp->scope, ftp->polarity);
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
trackInteriorFreeTypePack(ftp->scope, *newPack.tail);
|
trackInteriorFreeTypePack(ftp->scope, *newPack.tail);
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
|
@ -588,7 +588,7 @@ void trackInteriorFreeType(Scope* scope, TypeId ty)
|
||||||
void trackInteriorFreeTypePack(Scope* scope, TypePackId tp)
|
void trackInteriorFreeTypePack(Scope* scope, TypePackId tp)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(tp);
|
LUAU_ASSERT(tp);
|
||||||
if (!FFlag::LuauEagerGeneralization)
|
if (!FFlag::LuauEagerGeneralization2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (; scope; scope = scope->parent.get())
|
for (; scope; scope = scope->parent.get())
|
||||||
|
@ -685,4 +685,15 @@ std::optional<TypeId> extractMatchingTableType(std::vector<TypeId>& tables, Type
|
||||||
return std::nullopt;
|
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
|
} // namespace Luau
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||||
LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
|
LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -329,12 +329,12 @@ bool Unifier2::unify(TypeId subTy, const FunctionType* superFn)
|
||||||
|
|
||||||
for (TypePackId genericPack : subFn->genericPacks)
|
for (TypePackId genericPack : subFn->genericPacks)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
genericPack = follow(genericPack);
|
genericPack = follow(genericPack);
|
||||||
|
|
||||||
// TODO: Clip this follow() with LuauEagerGeneralization
|
// TODO: Clip this follow() with LuauEagerGeneralization2
|
||||||
const GenericTypePack* gen = get<GenericTypePack>(follow(genericPack));
|
const GenericTypePack* gen = get<GenericTypePack>(follow(genericPack));
|
||||||
if (gen)
|
if (gen)
|
||||||
genericPackSubstitutions[genericPack] = freshTypePack(scope, gen->polarity);
|
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->indexType, superTable->indexer->indexType);
|
||||||
result &= unify(subTable->indexer->indexResultType, superTable->indexer->indexResultType);
|
result &= unify(subTable->indexer->indexResultType, superTable->indexer->indexResultType);
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
// FIXME: We can probably do something more efficient here.
|
// FIXME: We can probably do something more efficient here.
|
||||||
result &= unify(superTable->indexer->indexType, subTable->indexer->indexType);
|
result &= unify(superTable->indexer->indexType, subTable->indexer->indexType);
|
||||||
|
|
|
@ -456,7 +456,7 @@ private:
|
||||||
AstType* annotation;
|
AstType* annotation;
|
||||||
Position colonPosition;
|
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)
|
: name(name)
|
||||||
, annotation(annotation)
|
, annotation(annotation)
|
||||||
, colonPosition(colonPosition)
|
, 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())
|
: CstNode(CstClassIndex())
|
||||||
, varsAnnotationColonPositions(varsAnnotationColonPositions)
|
, varsAnnotationColonPositions(varsAnnotationColonPositions)
|
||||||
, varsCommaPositions(varsCommaPositions)
|
, 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())
|
: CstNode(CstClassIndex())
|
||||||
, annotationColonPosition(annotationColonPosition)
|
, annotationColonPosition(annotationColonPosition)
|
||||||
, equalsPosition(equalsPosition)
|
, 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())
|
: CstNode(CstClassIndex())
|
||||||
, varsAnnotationColonPositions(varsAnnotationColonPositions)
|
, varsAnnotationColonPositions(varsAnnotationColonPositions)
|
||||||
, varsCommaPositions(varsCommaPositions)
|
, varsCommaPositions(varsCommaPositions)
|
||||||
|
|
|
@ -709,7 +709,8 @@ AstStat* Parser::parseFor()
|
||||||
if (options.storeCstData)
|
if (options.storeCstData)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauStoreLocalAnnotationColonPositions)
|
if (FFlag::LuauStoreLocalAnnotationColonPositions)
|
||||||
cstNodeMap[node] = allocator.alloc<CstStatForIn>(extractAnnotationColonPositions(names), varsCommaPosition, copy(valuesCommaPositions));
|
cstNodeMap[node] =
|
||||||
|
allocator.alloc<CstStatForIn>(extractAnnotationColonPositions(names), varsCommaPosition, copy(valuesCommaPositions));
|
||||||
else
|
else
|
||||||
cstNodeMap[node] = allocator.alloc<CstStatForIn>(AstArray<Position>{}, varsCommaPosition, copy(valuesCommaPositions));
|
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 (options.storeCstData)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauStoreLocalAnnotationColonPositions)
|
if (FFlag::LuauStoreLocalAnnotationColonPositions)
|
||||||
cstNodeMap[node] = allocator.alloc<CstStatLocal>(extractAnnotationColonPositions(names), varsCommaPositions, copy(valuesCommaPositions));
|
cstNodeMap[node] =
|
||||||
|
allocator.alloc<CstStatLocal>(extractAnnotationColonPositions(names), varsCommaPositions, copy(valuesCommaPositions));
|
||||||
else
|
else
|
||||||
cstNodeMap[node] = allocator.alloc<CstStatLocal>(AstArray<Position>{}, varsCommaPositions, copy(valuesCommaPositions));
|
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;
|
foundExtern = true;
|
||||||
nextLexeme();
|
nextLexeme();
|
||||||
if (AstName(lexer.current().name) != "type")
|
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 (foundExtern)
|
||||||
{
|
{
|
||||||
if (AstName(lexer.current().name) != "with")
|
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
|
else
|
||||||
nextLexeme();
|
nextLexeme();
|
||||||
}
|
}
|
||||||
|
@ -1452,9 +1460,9 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
||||||
|
|
||||||
expectAndConsume(':', "property type annotation");
|
expectAndConsume(':', "property type annotation");
|
||||||
AstType* propType = parseType();
|
AstType* propType = parseType();
|
||||||
props.push_back(
|
props.push_back(AstDeclaredExternTypeProperty{
|
||||||
AstDeclaredExternTypeProperty{propName->name, propName->location, propType, false, Location(propStart, lexer.previousLocation())}
|
propName->name, propName->location, propType, false, Location(propStart, lexer.previousLocation())
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1533,9 +1541,9 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
||||||
|
|
||||||
expectAndConsume(':', "property type annotation");
|
expectAndConsume(':', "property type annotation");
|
||||||
AstType* propType = parseType();
|
AstType* propType = parseType();
|
||||||
props.push_back(
|
props.push_back(AstDeclaredExternTypeProperty{
|
||||||
AstDeclaredExternTypeProperty{propName->name, propName->location, propType, false, Location(propStart, lexer.previousLocation())}
|
propName->name, propName->location, propType, false, Location(propStart, lexer.previousLocation())
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,16 +14,11 @@ void requireConfigInit(luarequire_Configuration* config);
|
||||||
|
|
||||||
struct ReplRequirer
|
struct ReplRequirer
|
||||||
{
|
{
|
||||||
using CompileOptions = Luau::CompileOptions(*)();
|
using CompileOptions = Luau::CompileOptions (*)();
|
||||||
using BoolCheck = bool(*)();
|
using BoolCheck = bool (*)();
|
||||||
using Coverage = void(*)(lua_State*, int);
|
using Coverage = void (*)(lua_State*, int);
|
||||||
|
|
||||||
ReplRequirer(
|
ReplRequirer(CompileOptions copts, BoolCheck coverageActive, BoolCheck codegenEnabled, Coverage coverageTrack);
|
||||||
CompileOptions copts,
|
|
||||||
BoolCheck coverageActive,
|
|
||||||
BoolCheck codegenEnabled,
|
|
||||||
Coverage coverageTrack
|
|
||||||
);
|
|
||||||
|
|
||||||
CompileOptions copts;
|
CompileOptions copts;
|
||||||
BoolCheck coverageActive;
|
BoolCheck coverageActive;
|
||||||
|
|
|
@ -189,6 +189,7 @@ target_sources(Luau.Analysis PRIVATE
|
||||||
Analysis/include/Luau/Documentation.h
|
Analysis/include/Luau/Documentation.h
|
||||||
Analysis/include/Luau/Error.h
|
Analysis/include/Luau/Error.h
|
||||||
Analysis/include/Luau/EqSatSimplification.h
|
Analysis/include/Luau/EqSatSimplification.h
|
||||||
|
Analysis/include/Luau/ExpectedTypeVisitor.h
|
||||||
Analysis/include/Luau/FileResolver.h
|
Analysis/include/Luau/FileResolver.h
|
||||||
Analysis/include/Luau/FragmentAutocomplete.h
|
Analysis/include/Luau/FragmentAutocomplete.h
|
||||||
Analysis/include/Luau/Frontend.h
|
Analysis/include/Luau/Frontend.h
|
||||||
|
@ -237,6 +238,7 @@ target_sources(Luau.Analysis PRIVATE
|
||||||
Analysis/include/Luau/TypeFunctionRuntime.h
|
Analysis/include/Luau/TypeFunctionRuntime.h
|
||||||
Analysis/include/Luau/TypeFunctionRuntimeBuilder.h
|
Analysis/include/Luau/TypeFunctionRuntimeBuilder.h
|
||||||
Analysis/include/Luau/TypeFwd.h
|
Analysis/include/Luau/TypeFwd.h
|
||||||
|
Analysis/include/Luau/TypeIds.h
|
||||||
Analysis/include/Luau/TypeInfer.h
|
Analysis/include/Luau/TypeInfer.h
|
||||||
Analysis/include/Luau/TypeOrPack.h
|
Analysis/include/Luau/TypeOrPack.h
|
||||||
Analysis/include/Luau/TypePack.h
|
Analysis/include/Luau/TypePack.h
|
||||||
|
@ -267,6 +269,7 @@ target_sources(Luau.Analysis PRIVATE
|
||||||
Analysis/src/EmbeddedBuiltinDefinitions.cpp
|
Analysis/src/EmbeddedBuiltinDefinitions.cpp
|
||||||
Analysis/src/Error.cpp
|
Analysis/src/Error.cpp
|
||||||
Analysis/src/EqSatSimplification.cpp
|
Analysis/src/EqSatSimplification.cpp
|
||||||
|
Analysis/src/ExpectedTypeVisitor.cpp
|
||||||
Analysis/src/FileResolver.cpp
|
Analysis/src/FileResolver.cpp
|
||||||
Analysis/src/FragmentAutocomplete.cpp
|
Analysis/src/FragmentAutocomplete.cpp
|
||||||
Analysis/src/Frontend.cpp
|
Analysis/src/Frontend.cpp
|
||||||
|
@ -306,6 +309,7 @@ target_sources(Luau.Analysis PRIVATE
|
||||||
Analysis/src/TypeFunctionReductionGuesser.cpp
|
Analysis/src/TypeFunctionReductionGuesser.cpp
|
||||||
Analysis/src/TypeFunctionRuntime.cpp
|
Analysis/src/TypeFunctionRuntime.cpp
|
||||||
Analysis/src/TypeFunctionRuntimeBuilder.cpp
|
Analysis/src/TypeFunctionRuntimeBuilder.cpp
|
||||||
|
Analysis/src/TypeIds.cpp
|
||||||
Analysis/src/TypeInfer.cpp
|
Analysis/src/TypeInfer.cpp
|
||||||
Analysis/src/TypeOrPack.cpp
|
Analysis/src/TypeOrPack.cpp
|
||||||
Analysis/src/TypePack.cpp
|
Analysis/src/TypePack.cpp
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauUnrefExisting, false)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This file contains most implementations of core Lua APIs from lua.h.
|
* 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;
|
global_State* g = L->global;
|
||||||
LuaTable* reg = hvalue(registry(L));
|
LuaTable* reg = hvalue(registry(L));
|
||||||
|
|
||||||
|
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);
|
TValue* slot = luaH_setnum(L, reg, ref);
|
||||||
setnvalue(slot, g->registryfree); // NB: no barrier needed because value isn't collectable
|
setnvalue(slot, g->registryfree); // NB: no barrier needed because value isn't collectable
|
||||||
g->registryfree = ref;
|
g->registryfree = ref;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void lua_setuserdatatag(lua_State* L, int idx, int tag)
|
void lua_setuserdatatag(lua_State* L, int idx, int tag)
|
||||||
|
|
|
@ -642,4 +642,3 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstGenericTypePackWithDefault")
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,8 @@
|
||||||
LUAU_FASTFLAG(LuauTraceTypesInNonstrictMode2)
|
LUAU_FASTFLAG(LuauTraceTypesInNonstrictMode2)
|
||||||
LUAU_FASTFLAG(LuauSetMetatableDoesNotTimeTravel)
|
LUAU_FASTFLAG(LuauSetMetatableDoesNotTimeTravel)
|
||||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||||
|
LUAU_FASTFLAG(LuauExpectedTypeVisitor)
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
@ -41,6 +42,9 @@ struct ACFixtureImpl : BaseType
|
||||||
FrontendOptions opts;
|
FrontendOptions opts;
|
||||||
opts.forAutocomplete = true;
|
opts.forAutocomplete = true;
|
||||||
opts.retainFullTypeGraphs = 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);
|
this->frontend.check("MainModule", opts);
|
||||||
|
|
||||||
return Luau::autocomplete(this->frontend, "MainModule", Position{row, column}, nullCallback);
|
return Luau::autocomplete(this->frontend, "MainModule", Position{row, column}, nullCallback);
|
||||||
|
@ -51,6 +55,9 @@ struct ACFixtureImpl : BaseType
|
||||||
FrontendOptions opts;
|
FrontendOptions opts;
|
||||||
opts.forAutocomplete = true;
|
opts.forAutocomplete = true;
|
||||||
opts.retainFullTypeGraphs = 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);
|
this->frontend.check("MainModule", opts);
|
||||||
|
|
||||||
return Luau::autocomplete(this->frontend, "MainModule", getPosition(marker), callback);
|
return Luau::autocomplete(this->frontend, "MainModule", getPosition(marker), callback);
|
||||||
|
@ -61,6 +68,9 @@ struct ACFixtureImpl : BaseType
|
||||||
FrontendOptions opts;
|
FrontendOptions opts;
|
||||||
opts.forAutocomplete = true;
|
opts.forAutocomplete = true;
|
||||||
opts.retainFullTypeGraphs = 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);
|
this->frontend.check(name, opts);
|
||||||
|
|
||||||
return Luau::autocomplete(this->frontend, name, pos, callback);
|
return Luau::autocomplete(this->frontend, name, pos, callback);
|
||||||
|
@ -103,7 +113,9 @@ struct ACFixtureImpl : BaseType
|
||||||
}
|
}
|
||||||
LUAU_ASSERT("Digit expected after @ symbol" && prevChar != '@');
|
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)
|
LoadDefinitionFileResult loadDefinition(const std::string& source)
|
||||||
|
@ -2191,7 +2203,10 @@ local fp: @1= f
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
REQUIRE_EQ("({ x: number, y: number }) -> number", toString(requireType("f")));
|
REQUIRE_EQ("({ x: number, y: number }) -> number", toString(requireType("f")));
|
||||||
else
|
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"));
|
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")
|
TEST_CASE_FIXTURE(ACFixture, "string_singleton_as_table_key_iso")
|
||||||
{
|
{
|
||||||
|
|
||||||
check(R"(
|
check(R"(
|
||||||
type Direction = "up" | "down"
|
type Direction = "up" | "down"
|
||||||
local b: {[Direction]: boolean} = {["@2"] = true}
|
local b: {[Direction]: boolean} = {["@2"] = true}
|
||||||
|
@ -4454,9 +4468,9 @@ TEST_CASE_FIXTURE(ACExternTypeFixture, "ac_dont_overflow_on_recursive_union")
|
||||||
|
|
||||||
auto ac = autocomplete('1');
|
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
|
// sets some flags
|
||||||
CHECK(ac.entryMap.count("BaseMethod") > 0);
|
CHECK(ac.entryMap.count("BaseMethod") > 0);
|
||||||
CHECK(ac.entryMap.count("Method") > 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_.
|
// Otherwise, we don't infer anything for `value`, which is _fine_.
|
||||||
CHECK(ac.entryMap.empty());
|
CHECK(ac.entryMap.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ACBuiltinsFixture, "type_function_has_types_definitions")
|
TEST_CASE_FIXTURE(ACBuiltinsFixture, "type_function_has_types_definitions")
|
||||||
|
@ -4536,4 +4549,62 @@ end
|
||||||
CHECK_EQ(ac.entryMap.count("number"), 1);
|
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();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -56,7 +56,6 @@ struct DataFlowGraphFixture
|
||||||
CHECK(phi->operands.size() == operandSet.size());
|
CHECK(phi->operands.size() == operandSet.size());
|
||||||
for (auto o : phi->operands)
|
for (auto o : phi->operands)
|
||||||
CHECK(operandSet.contains(o.get()));
|
CHECK(operandSet.contains(o.get()));
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ LUAU_FASTFLAG(DebugLuauFreezeArena)
|
||||||
LUAU_FASTFLAG(DebugLuauForceAllNewSolverTests)
|
LUAU_FASTFLAG(DebugLuauForceAllNewSolverTests)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauTypeFunOptional)
|
LUAU_FASTFLAG(LuauTypeFunOptional)
|
||||||
|
LUAU_FASTFLAG(LuauUpdateSetMetatableTypeSignature)
|
||||||
|
|
||||||
#define DOES_NOT_PASS_NEW_SOLVER_GUARD_IMPL(line) ScopedFastFlag sff_##line{FFlag::LuauSolverV2, FFlag::DebugLuauForceAllNewSolverTests};
|
#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.
|
// Most often those are changes related to builtin type definitions.
|
||||||
// In that case, flag can be forced to 'true' using the example below:
|
// In that case, flag can be forced to 'true' using the example below:
|
||||||
// ScopedFastFlag sff_LuauExampleFlagDefinition{FFlag::LuauExampleFlagDefinition, true};
|
// 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.
|
// 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.
|
// This is useful for tracking down violations of Luau's memory model.
|
||||||
|
|
|
@ -31,6 +31,7 @@ LUAU_FASTFLAG(LuauBlockDiffFragmentSelection)
|
||||||
LUAU_FASTFLAG(LuauFragmentAcMemoryLeak)
|
LUAU_FASTFLAG(LuauFragmentAcMemoryLeak)
|
||||||
LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation)
|
LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation)
|
||||||
LUAU_FASTFLAG(LuauFragmentAutocompleteIfRecommendations)
|
LUAU_FASTFLAG(LuauFragmentAutocompleteIfRecommendations)
|
||||||
|
LUAU_FASTFLAG(LuauPopulateRefinedTypesInFragmentFromOldSolver)
|
||||||
|
|
||||||
static std::optional<AutocompleteEntryMap> nullCallback(std::string tag, std::optional<const ExternType*> ptr, std::optional<std::string> contents)
|
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 luauFragmentAcMemoryLeak{FFlag::LuauFragmentAcMemoryLeak, true};
|
||||||
ScopedFastFlag luauGlobalVariableModuleIsolation{FFlag::LuauGlobalVariableModuleIsolation, true};
|
ScopedFastFlag luauGlobalVariableModuleIsolation{FFlag::LuauGlobalVariableModuleIsolation, true};
|
||||||
ScopedFastFlag luauFragmentAutocompleteIfRecommendations{FFlag::LuauFragmentAutocompleteIfRecommendations, true};
|
ScopedFastFlag luauFragmentAutocompleteIfRecommendations{FFlag::LuauFragmentAutocompleteIfRecommendations, true};
|
||||||
|
ScopedFastFlag luauPopulateRefinedTypesInFragmentFromOldSolver{FFlag::LuauPopulateRefinedTypesInFragmentFromOldSolver, true};
|
||||||
|
|
||||||
FragmentAutocompleteFixtureImpl()
|
FragmentAutocompleteFixtureImpl()
|
||||||
: BaseType(true)
|
: BaseType(true)
|
||||||
|
@ -146,6 +148,37 @@ struct FragmentAutocompleteFixtureImpl : BaseType
|
||||||
return Luau::tryFragmentAutocomplete(this->frontend, "MainModule", cursorPos, context, nullCallback);
|
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(
|
void autocompleteFragmentInBothSolvers(
|
||||||
const std::string& document,
|
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)
|
// NOLINTEND(bugprone-unchecked-optional-access)
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -897,7 +897,8 @@ TEST_CASE_FIXTURE(FrontendFixture, "it_should_be_safe_to_stringify_errors_when_f
|
||||||
{
|
{
|
||||||
CHECK_EQ(
|
CHECK_EQ(
|
||||||
"Table type '{ count: string }' not compatible with type '{ Count: number }' because the former is missing field 'Count'",
|
"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
|
else
|
||||||
REQUIRE_EQ(
|
REQUIRE_EQ(
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||||
LUAU_FASTFLAG(DebugLuauForbidInternalTypes)
|
LUAU_FASTFLAG(DebugLuauForbidInternalTypes)
|
||||||
LUAU_FASTFLAG(LuauTrackInferredFunctionTypeFromCall)
|
LUAU_FASTFLAG(LuauTrackInferredFunctionTypeFromCall)
|
||||||
|
|
||||||
|
@ -115,7 +115,8 @@ TEST_CASE_FIXTURE(GeneralizationFixture, "dont_traverse_into_class_types_when_ge
|
||||||
{
|
{
|
||||||
auto [propTy, _] = freshType();
|
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);
|
auto genExternType = generalize(cursedExternType);
|
||||||
REQUIRE(genExternType);
|
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})")
|
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;
|
TableType tt;
|
||||||
tt.indexer = TableIndexer{builtinTypes.numberType, builtinTypes.numberType};
|
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})) -> ()")
|
TEST_CASE_FIXTURE(GeneralizationFixture, "(('a <: {'b})) -> ()")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauEagerGeneralization, true};
|
ScopedFastFlag sff{FFlag::LuauEagerGeneralization2, true};
|
||||||
|
|
||||||
auto [aTy, aFree] = freshType();
|
auto [aTy, aFree] = freshType();
|
||||||
auto [bTy, bFree] = freshType();
|
auto [bTy, bFree] = freshType();
|
||||||
|
@ -341,10 +342,7 @@ end
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "generalization_should_not_leak_free_type")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "generalization_should_not_leak_free_type")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sffs[] = {{FFlag::DebugLuauForbidInternalTypes, true}, {FFlag::LuauTrackInferredFunctionTypeFromCall, true}};
|
||||||
{FFlag::DebugLuauForbidInternalTypes, true},
|
|
||||||
{FFlag::LuauTrackInferredFunctionTypeFromCall, true}
|
|
||||||
};
|
|
||||||
|
|
||||||
// This test case should just not assert
|
// This test case should just not assert
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
|
|
|
@ -8,13 +8,13 @@
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization);
|
LUAU_FASTFLAG(LuauEagerGeneralization2);
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("InferPolarity");
|
TEST_SUITE_BEGIN("InferPolarity");
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "T where T = { m: <a>(a) -> T }")
|
TEST_CASE_FIXTURE(Fixture, "T where T = { m: <a>(a) -> T }")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauEagerGeneralization, true};
|
ScopedFastFlag sff{FFlag::LuauEagerGeneralization2, true};
|
||||||
|
|
||||||
TypeArena arena;
|
TypeArena arena;
|
||||||
ScopePtr globalScope = std::make_shared<Scope>(builtinTypes->anyTypePack);
|
ScopePtr globalScope = std::make_shared<Scope>(builtinTypes->anyTypePack);
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
LUAU_FASTFLAG(LuauSolverV2);
|
LUAU_FASTFLAG(LuauSolverV2);
|
||||||
LUAU_FASTFLAG(LintRedundantNativeAttribute);
|
LUAU_FASTFLAG(LintRedundantNativeAttribute);
|
||||||
LUAU_FASTFLAG(LuauDeprecatedAttribute);
|
LUAU_FASTFLAG(LuauDeprecatedAttribute);
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization);
|
LUAU_FASTFLAG(LuauEagerGeneralization2);
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
@ -1942,7 +1942,7 @@ print(foo:bar(2.0))
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "TableOperations")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "TableOperations")
|
||||||
{
|
{
|
||||||
// FIXME: For now this flag causes a stack overflow on Windows.
|
// FIXME: For now this flag causes a stack overflow on Windows.
|
||||||
ScopedFastFlag _{FFlag::LuauEagerGeneralization, false};
|
ScopedFastFlag _{FFlag::LuauEagerGeneralization2, false};
|
||||||
|
|
||||||
LintResult result = lint(R"(
|
LintResult result = lint(R"(
|
||||||
local t = {}
|
local t = {}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauNewNonStrictVisitTypes2)
|
LUAU_FASTFLAG(LuauNewNonStrictVisitTypes2)
|
||||||
|
LUAU_FASTFLAG(LuauNewNonStrictFixGenericTypePacks)
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
@ -560,6 +561,18 @@ optionalArgsAtTheEnd1("a", nil, 3)
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
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")
|
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "optionals_in_checked_function_in_middle_cannot_be_omitted")
|
||||||
{
|
{
|
||||||
CheckResult result = checkNonStrict(R"(
|
CheckResult result = checkNonStrict(R"(
|
||||||
|
|
|
@ -14,7 +14,7 @@ LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||||
LUAU_FASTINT(LuauNormalizeIntersectionLimit)
|
LUAU_FASTINT(LuauNormalizeIntersectionLimit)
|
||||||
LUAU_FASTINT(LuauNormalizeUnionLimit)
|
LUAU_FASTINT(LuauNormalizeUnionLimit)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||||
LUAU_FASTFLAG(LuauRefineWaitForBlockedTypesInTarget)
|
LUAU_FASTFLAG(LuauRefineWaitForBlockedTypesInTarget)
|
||||||
LUAU_FASTFLAG(LuauSimplifyOutOfLine)
|
LUAU_FASTFLAG(LuauSimplifyOutOfLine)
|
||||||
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
||||||
|
@ -1206,7 +1206,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_union_type_pack_cycle")
|
||||||
{FFlag::LuauNoMoreInjectiveTypeFunctions, true},
|
{FFlag::LuauNoMoreInjectiveTypeFunctions, true},
|
||||||
{FFlag::LuauOptimizeFalsyAndTruthyIntersect, true},
|
{FFlag::LuauOptimizeFalsyAndTruthyIntersect, true},
|
||||||
{FFlag::LuauClipVariadicAnysFromArgsToGenericFuncs2, true},
|
{FFlag::LuauClipVariadicAnysFromArgsToGenericFuncs2, true},
|
||||||
{FFlag::LuauEagerGeneralization, true}
|
{FFlag::LuauEagerGeneralization2, true}
|
||||||
};
|
};
|
||||||
ScopedFastInt sfi{FInt::LuauTypeInferRecursionLimit, 0};
|
ScopedFastInt sfi{FInt::LuauTypeInferRecursionLimit, 0};
|
||||||
|
|
||||||
|
|
|
@ -2086,7 +2086,8 @@ TEST_CASE_FIXTURE(Fixture, "parse_extern_type_declarations")
|
||||||
declare extern type Bar extends Foo with
|
declare extern type Bar extends Foo with
|
||||||
prop2: string
|
prop2: string
|
||||||
end
|
end
|
||||||
)").root;
|
)")
|
||||||
|
.root;
|
||||||
|
|
||||||
REQUIRE_EQ(stat->body.size, 2);
|
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
|
declare extern type Bar extends Foo with
|
||||||
prop2: string
|
prop2: string
|
||||||
end
|
end
|
||||||
)").root;
|
)")
|
||||||
|
.root;
|
||||||
|
|
||||||
REQUIRE_EQ(stat->body.size, 2);
|
REQUIRE_EQ(stat->body.size, 2);
|
||||||
|
|
||||||
|
@ -2355,9 +2357,7 @@ TEST_CASE_FIXTURE(Fixture, "class_indexer")
|
||||||
[number]: number
|
[number]: number
|
||||||
end
|
end
|
||||||
)",
|
)",
|
||||||
(FFlag::LuauDeclareExternType)
|
(FFlag::LuauDeclareExternType) ? "Cannot have more than one indexer on an extern type" : "Cannot have more than one class indexer"
|
||||||
? "Cannot have more than one indexer on an extern type"
|
|
||||||
: "Cannot have more than one class indexer"
|
|
||||||
);
|
);
|
||||||
|
|
||||||
REQUIRE_EQ(1, p1.root->body.size);
|
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 parseOptions;
|
||||||
parseOptions.storeCstData = true;
|
parseOptions.storeCstData = true;
|
||||||
|
|
||||||
ParseResult result = parseEx(R"(
|
ParseResult result = parseEx(
|
||||||
|
R"(
|
||||||
for value in tbl do
|
for value in tbl do
|
||||||
end
|
end
|
||||||
)", parseOptions);
|
)",
|
||||||
|
parseOptions
|
||||||
|
);
|
||||||
REQUIRE(result.root);
|
REQUIRE(result.root);
|
||||||
REQUIRE_EQ(1, result.root->body.size);
|
REQUIRE_EQ(1, result.root->body.size);
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauSubtypeGenericsAndNegations)
|
LUAU_FASTFLAG(LuauSubtypeGenericsAndNegations)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
@ -1629,19 +1629,13 @@ TEST_CASE_FIXTURE(SubtypeFixture, "substitute_a_generic_for_a_negation")
|
||||||
TypeId bTy = arena.addType(GenericType{"B"});
|
TypeId bTy = arena.addType(GenericType{"B"});
|
||||||
getMutable<GenericType>(bTy)->scope = moduleScope.get();
|
getMutable<GenericType>(bTy)->scope = moduleScope.get();
|
||||||
|
|
||||||
TypeId genericFunctionTy = arena.addType(FunctionType{
|
TypeId genericFunctionTy =
|
||||||
{aTy, bTy},
|
arena.addType(FunctionType{{aTy, bTy}, {}, arena.addTypePack({aTy, bTy}), arena.addTypePack({join(meet(aTy, builtinTypes->truthyType), bTy)})}
|
||||||
{},
|
);
|
||||||
arena.addTypePack({aTy, bTy}),
|
|
||||||
arena.addTypePack({join(meet(aTy, builtinTypes->truthyType), bTy)})
|
|
||||||
});
|
|
||||||
|
|
||||||
const TypeId truthyTy = builtinTypes->truthyType;
|
const TypeId truthyTy = builtinTypes->truthyType;
|
||||||
|
|
||||||
TypeId actualFunctionTy = fn(
|
TypeId actualFunctionTy = fn({truthyTy, truthyTy}, {join(meet(truthyTy, builtinTypes->truthyType), truthyTy)});
|
||||||
{truthyTy, truthyTy},
|
|
||||||
{join(meet(truthyTy, builtinTypes->truthyType), truthyTy)}
|
|
||||||
);
|
|
||||||
|
|
||||||
SubtypingResult result = isSubtype(genericFunctionTy, actualFunctionTy);
|
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")
|
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());
|
TypeId argTy = arena.freshType(builtinTypes, moduleScope.get());
|
||||||
FreeType* freeArg = getMutable<FreeType>(argTy);
|
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")
|
TEST_CASE_FIXTURE(Fixture, "tostring_error_mismatch")
|
||||||
{
|
{
|
||||||
ScopedFastFlag _ {FFlag::LuauTableLiteralSubtypeSpecificCheck, true};
|
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
|
|
|
@ -14,7 +14,7 @@ using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_DYNAMIC_FASTINT(LuauTypeFamilyApplicationCartesianProductLimit)
|
LUAU_DYNAMIC_FASTINT(LuauTypeFamilyApplicationCartesianProductLimit)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||||
LUAU_FASTFLAG(LuauHasPropProperBlock)
|
LUAU_FASTFLAG(LuauHasPropProperBlock)
|
||||||
LUAU_FASTFLAG(LuauSimplifyOutOfLine)
|
LUAU_FASTFLAG(LuauSimplifyOutOfLine)
|
||||||
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
|
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
|
||||||
|
@ -1725,7 +1725,7 @@ struct TFFixture
|
||||||
TypeFunctionRuntime runtime{NotNull{&ice}, NotNull{&limits}};
|
TypeFunctionRuntime runtime{NotNull{&ice}, NotNull{&limits}};
|
||||||
|
|
||||||
const ScopedFastFlag sff[1] = {
|
const ScopedFastFlag sff[1] = {
|
||||||
{FFlag::LuauEagerGeneralization, true},
|
{FFlag::LuauEagerGeneralization2, true},
|
||||||
};
|
};
|
||||||
|
|
||||||
BuiltinTypeFunctions builtinTypeFunctions;
|
BuiltinTypeFunctions builtinTypeFunctions;
|
||||||
|
@ -1746,9 +1746,7 @@ TEST_CASE_FIXTURE(TFFixture, "refine<G, ~(false?)>")
|
||||||
{
|
{
|
||||||
TypeId g = arena->addType(GenericType{globalScope.get(), Polarity::Negative});
|
TypeId g = arena->addType(GenericType{globalScope.get(), Polarity::Negative});
|
||||||
|
|
||||||
TypeId refineTy = arena->addType(TypeFunctionInstanceType{
|
TypeId refineTy = arena->addType(TypeFunctionInstanceType{builtinTypeFunctions.refineFunc, {g, builtinTypes->truthyType}});
|
||||||
builtinTypeFunctions.refineFunc, {g, builtinTypes->truthyType}
|
|
||||||
});
|
|
||||||
|
|
||||||
FunctionGraphReductionResult res = reduceTypeFunctions(refineTy, Location{}, tfc);
|
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 aType = arena->freshType(builtinTypes, globalScope.get());
|
||||||
TypeId bType = arena->freshType(builtinTypes, globalScope.get());
|
TypeId bType = arena->freshType(builtinTypes, globalScope.get());
|
||||||
|
|
||||||
TypeId orType = arena->addType(TypeFunctionInstanceType{
|
TypeId orType = arena->addType(TypeFunctionInstanceType{builtinTypeFunctions.orFunc, {aType, bType}});
|
||||||
builtinTypeFunctions.orFunc, {aType, bType}
|
|
||||||
});
|
|
||||||
|
|
||||||
FunctionGraphReductionResult res = reduceTypeFunctions(orType, Location{}, tfc);
|
FunctionGraphReductionResult res = reduceTypeFunctions(orType, Location{}, tfc);
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,9 @@ using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||||
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
|
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
|
||||||
|
LUAU_FASTFLAG(LuauUserTypeFunctionAliases)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("UserDefinedTypeFunctionTests");
|
TEST_SUITE_BEGIN("UserDefinedTypeFunctionTests");
|
||||||
|
|
||||||
|
@ -1987,7 +1988,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_singleton_equality_bool")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
// FIXME: CLI-151985
|
// FIXME: CLI-151985
|
||||||
// This test breaks because we can't see that eq<type?, b> is already fully reduced.
|
// 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};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
// FIXME: CLI-151985
|
// FIXME: CLI-151985
|
||||||
// This test breaks because we can't see that eq<type?, b> is already fully reduced.
|
// 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]));
|
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();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauRetainDefinitionAliasLocations)
|
|
||||||
LUAU_FASTFLAG(LuauNewNonStrictVisitTypes2)
|
LUAU_FASTFLAG(LuauNewNonStrictVisitTypes2)
|
||||||
LUAU_FASTFLAG(LuauGuardAgainstMalformedTypeAliasExpansion2)
|
LUAU_FASTFLAG(LuauGuardAgainstMalformedTypeAliasExpansion2)
|
||||||
LUAU_FASTFLAG(LuauSkipMalformedTypeAliases)
|
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")
|
TEST_CASE_FIXTURE(Fixture, "exported_alias_location_is_accessible_on_module")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauRetainDefinitionAliasLocations, true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
export type Value = string
|
export type Value = string
|
||||||
)");
|
)");
|
||||||
|
@ -1235,7 +1232,6 @@ TEST_CASE_FIXTURE(Fixture, "exported_type_function_location_is_accessible_on_mod
|
||||||
{
|
{
|
||||||
ScopedFastFlag flags[] = {
|
ScopedFastFlag flags[] = {
|
||||||
{FFlag::LuauSolverV2, true},
|
{FFlag::LuauSolverV2, true},
|
||||||
{FFlag::LuauRetainDefinitionAliasLocations, true},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
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);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
CHECK_EQ("any", toString(requireType("b")));
|
CHECK_EQ("any", toString(requireType("b")));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "call_to_any_yields_any")
|
TEST_CASE_FIXTURE(Fixture, "call_to_any_yields_any")
|
||||||
|
|
|
@ -11,7 +11,7 @@ using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauTableCloneClonesType3)
|
LUAU_FASTFLAG(LuauTableCloneClonesType3)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||||
LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments)
|
LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("BuiltinTests");
|
TEST_SUITE_BEGIN("BuiltinTests");
|
||||||
|
@ -146,8 +146,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "sort_with_bad_predicate")
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
const std::string expected =
|
const std::string expected = "Type\n\t"
|
||||||
"Type\n\t"
|
|
||||||
"'(number, number) -> boolean'"
|
"'(number, number) -> boolean'"
|
||||||
"\ncould not be converted into\n\t"
|
"\ncould not be converted into\n\t"
|
||||||
"'((string, string) -> boolean)?'"
|
"'((string, string) -> boolean)?'"
|
||||||
|
@ -460,7 +459,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_pack_reduce")
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
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")));
|
CHECK("{ [number]: string | string | string, n: number }" == toString(requireType("t")));
|
||||||
else if (FFlag::LuauSolverV2)
|
else if (FFlag::LuauSolverV2)
|
||||||
CHECK_EQ("{ [number]: string, n: number }", toString(requireType("t")));
|
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);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
|
||||||
CHECK_MESSAGE(
|
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"));
|
CHECK(builtinTypes->errorType == requireType("c"));
|
||||||
|
|
|
@ -22,7 +22,7 @@ LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTINT(LuauTarjanChildLimit)
|
LUAU_FASTINT(LuauTarjanChildLimit)
|
||||||
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||||
LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments)
|
LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments)
|
||||||
LUAU_FASTFLAG(LuauHasPropProperBlock)
|
LUAU_FASTFLAG(LuauHasPropProperBlock)
|
||||||
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
||||||
|
@ -1519,8 +1519,7 @@ local b: B = a
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
const std::string expected =
|
const std::string expected = "Type\n\t"
|
||||||
"Type\n\t"
|
|
||||||
"'(number, number) -> string'"
|
"'(number, number) -> string'"
|
||||||
"\ncould not be converted into\n\t"
|
"\ncould not be converted into\n\t"
|
||||||
"'(number) -> string'"
|
"'(number) -> string'"
|
||||||
|
@ -1543,8 +1542,7 @@ local b: B = a
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
const std::string expected =
|
const std::string expected = "Type\n\t"
|
||||||
"Type\n\t"
|
|
||||||
"'(number, number) -> string'"
|
"'(number, number) -> string'"
|
||||||
"\ncould not be converted into\n\t"
|
"\ncould not be converted into\n\t"
|
||||||
"'(number, string) -> string'"
|
"'(number, string) -> string'"
|
||||||
|
@ -1568,8 +1566,7 @@ local b: B = a
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
const std::string expected =
|
const std::string expected = "Type\n\t"
|
||||||
"Type\n\t"
|
|
||||||
"'(number, number) -> number'"
|
"'(number, number) -> number'"
|
||||||
"\ncould not be converted into\n\t"
|
"\ncould not be converted into\n\t"
|
||||||
"'(number, number) -> (number, boolean)'"
|
"'(number, number) -> (number, boolean)'"
|
||||||
|
@ -1592,8 +1589,7 @@ local b: B = a
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
const std::string expected =
|
const std::string expected = "Type\n\t"
|
||||||
"Type\n\t"
|
|
||||||
"'(number, number) -> string'"
|
"'(number, number) -> string'"
|
||||||
"\ncould not be converted into\n\t"
|
"\ncould not be converted into\n\t"
|
||||||
"'(number, number) -> number'"
|
"'(number, number) -> number'"
|
||||||
|
@ -1617,8 +1613,7 @@ local b: B = a
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
const std::string expected =
|
const std::string expected = "Type\n\t"
|
||||||
"Type\n\t"
|
|
||||||
"'(number, number) -> (number, string)'"
|
"'(number, number) -> (number, string)'"
|
||||||
"\ncould not be converted into\n\t"
|
"\ncould not be converted into\n\t"
|
||||||
"'(number, number) -> (number, boolean)'"
|
"'(number, number) -> (number, boolean)'"
|
||||||
|
@ -1688,7 +1683,7 @@ t.f = function(x)
|
||||||
end
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization && FFlag::LuauSolverV2)
|
if (FFlag::LuauEagerGeneralization2 && FFlag::LuauSolverV2)
|
||||||
{
|
{
|
||||||
// FIXME CLI-151985
|
// FIXME CLI-151985
|
||||||
LUAU_CHECK_ERROR_COUNT(3, result);
|
LUAU_CHECK_ERROR_COUNT(3, result);
|
||||||
|
@ -1773,7 +1768,7 @@ t.f = function(x)
|
||||||
end
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization && FFlag::LuauSolverV2)
|
if (FFlag::LuauEagerGeneralization2 && FFlag::LuauSolverV2)
|
||||||
{
|
{
|
||||||
// FIXME CLI-151985
|
// FIXME CLI-151985
|
||||||
LUAU_CHECK_ERROR_COUNT(2, result);
|
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")
|
TEST_CASE_FIXTURE(Fixture, "dont_infer_parameter_types_for_functions_from_their_call_site")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sffs[] = {{FFlag::LuauHasPropProperBlock, true}, {FFlag::LuauOptimizeFalsyAndTruthyIntersect, true}};
|
||||||
{FFlag::LuauHasPropProperBlock, true},
|
|
||||||
{FFlag::LuauOptimizeFalsyAndTruthyIntersect, true}
|
|
||||||
};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local t = {}
|
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")));
|
CHECK_EQ("<a>(a) -> a", toString(requireType("f")));
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization && FFlag::LuauSolverV2)
|
if (FFlag::LuauEagerGeneralization2 && FFlag::LuauSolverV2)
|
||||||
{
|
{
|
||||||
LUAU_CHECK_NO_ERRORS(result);
|
LUAU_CHECK_NO_ERRORS(result);
|
||||||
CHECK("<a>({ read p: { read q: a } }) -> (a & ~(false?))?" == toString(requireType("g")));
|
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.
|
// 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);
|
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||||
auto tm1 = get<TypeMismatch>(result.errors[0]);
|
auto tm1 = get<TypeMismatch>(result.errors[0]);
|
||||||
|
|
|
@ -357,8 +357,7 @@ TEST_CASE_FIXTURE(Fixture, "table_intersection_write_sealed_indirect")
|
||||||
{
|
{
|
||||||
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
||||||
|
|
||||||
const std::string expected =
|
const std::string expected = "Type\n\t"
|
||||||
"Type\n\t"
|
|
||||||
"'(string, number) -> string'"
|
"'(string, number) -> string'"
|
||||||
"\ncould not be converted into\n\t"
|
"\ncould not be converted into\n\t"
|
||||||
"'(string) -> string'\n"
|
"'(string) -> string'\n"
|
||||||
|
@ -389,8 +388,7 @@ TEST_CASE_FIXTURE(Fixture, "table_write_sealed_indirect")
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
||||||
const std::string expected =
|
const std::string expected = "Type\n\t"
|
||||||
"Type\n\t"
|
|
||||||
"'(string, number) -> string'"
|
"'(string, number) -> string'"
|
||||||
"\ncould not be converted into\n\t"
|
"\ncould not be converted into\n\t"
|
||||||
"'(string) -> string'\n"
|
"'(string) -> string'\n"
|
||||||
|
@ -428,8 +426,7 @@ local a: XYZ = 3
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
{
|
{
|
||||||
const std::string expected =
|
const std::string expected = "Type "
|
||||||
"Type "
|
|
||||||
"'number'"
|
"'number'"
|
||||||
" could not be converted into "
|
" could not be converted into "
|
||||||
"'X & Y & Z'; \n"
|
"'X & Y & Z'; \n"
|
||||||
|
@ -527,8 +524,7 @@ TEST_CASE_FIXTURE(Fixture, "intersect_bool_and_false")
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
{
|
{
|
||||||
const std::string expected =
|
const std::string expected = "Type "
|
||||||
"Type "
|
|
||||||
"'boolean & false'"
|
"'boolean & false'"
|
||||||
" could not be converted into "
|
" could not be converted into "
|
||||||
"'true'; \n"
|
"'true'; \n"
|
||||||
|
@ -557,8 +553,7 @@ TEST_CASE_FIXTURE(Fixture, "intersect_false_and_bool_and_false")
|
||||||
// TODO: odd stringification of `false & (boolean & false)`.)
|
// TODO: odd stringification of `false & (boolean & false)`.)
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
{
|
{
|
||||||
const std::string expected =
|
const std::string expected = "Type "
|
||||||
"Type "
|
|
||||||
"'boolean & false & false'"
|
"'boolean & false & false'"
|
||||||
" could not be converted into "
|
" could not be converted into "
|
||||||
"'true'; \n"
|
"'true'; \n"
|
||||||
|
@ -640,13 +635,11 @@ TEST_CASE_FIXTURE(Fixture, "union_saturate_overloaded_functions")
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
|
||||||
const std::string expected =
|
const std::string expected = "Type\n\t"
|
||||||
"Type\n\t"
|
|
||||||
"'((number) -> number) & ((string) -> string)'"
|
"'((number) -> number) & ((string) -> string)'"
|
||||||
"\ncould not be converted into\n\t"
|
"\ncould not be converted into\n\t"
|
||||||
"'(boolean | number) -> boolean | number'; none of the intersection parts are compatible";
|
"'(boolean | number) -> boolean | number'; none of the intersection parts are compatible";
|
||||||
CHECK_EQ(expected, toString(result.errors[0]));
|
CHECK_EQ(expected, toString(result.errors[0]));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "intersection_of_tables")
|
TEST_CASE_FIXTURE(Fixture, "intersection_of_tables")
|
||||||
|
@ -662,8 +655,7 @@ TEST_CASE_FIXTURE(Fixture, "intersection_of_tables")
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
{
|
{
|
||||||
const std::string expected =
|
const std::string expected = "Type "
|
||||||
"Type "
|
|
||||||
"'{ p: number?, q: number?, r: number? } & { p: number?, q: string? }'"
|
"'{ p: number?, q: number?, r: number? } & { p: number?, q: string? }'"
|
||||||
" could not be converted into "
|
" could not be converted into "
|
||||||
"'{ p: nil }'; \n"
|
"'{ p: nil }'; \n"
|
||||||
|
@ -696,8 +688,7 @@ TEST_CASE_FIXTURE(Fixture, "intersection_of_tables_with_top_properties")
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
{
|
{
|
||||||
const std::string expected =
|
const std::string expected = "Type\n\t"
|
||||||
"Type\n\t"
|
|
||||||
"'{ p: number?, q: any } & { p: unknown, q: string? }'"
|
"'{ p: number?, q: any } & { p: unknown, q: string? }'"
|
||||||
"\ncould not be converted into\n\t"
|
"\ncould not be converted into\n\t"
|
||||||
"'{ p: string?, q: number? }'; \n"
|
"'{ p: string?, q: number? }'; \n"
|
||||||
|
@ -946,8 +937,7 @@ TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_unknown_arguments")
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
|
||||||
const std::string expected =
|
const std::string expected = "Type\n\t"
|
||||||
"Type\n\t"
|
|
||||||
"'((number) -> number?) & ((unknown) -> string?)'"
|
"'((number) -> number?) & ((unknown) -> string?)'"
|
||||||
"\ncould not be converted into\n\t"
|
"\ncould not be converted into\n\t"
|
||||||
"'(number?) -> nil'; none of the intersection parts are compatible";
|
"'(number?) -> nil'; none of the intersection parts are compatible";
|
||||||
|
@ -1172,8 +1162,7 @@ TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_weird_typepacks_4")
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
{
|
{
|
||||||
const std::string expected =
|
const std::string expected = "Type\n\t"
|
||||||
"Type\n\t"
|
|
||||||
"'((a...) -> ()) & ((number, a...) -> number)'"
|
"'((a...) -> ()) & ((number, a...) -> number)'"
|
||||||
"\ncould not be converted into\n\t"
|
"\ncould not be converted into\n\t"
|
||||||
"'((a...) -> ()) & ((number, a...) -> number)'; \n"
|
"'((a...) -> ()) & ((number, a...) -> number)'; \n"
|
||||||
|
|
|
@ -1519,7 +1519,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "repeat_is_linearish")
|
||||||
)"));
|
)"));
|
||||||
|
|
||||||
CHECK_EQ("nil", toString(requireType("y")));
|
CHECK_EQ("nil", toString(requireType("y")));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "ensure_local_in_loop_does_not_escape")
|
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")));
|
CHECK_EQ("number", toString(requireType("y")));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions)
|
LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||||
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
||||||
LUAU_FASTFLAG(LuauClipVariadicAnysFromArgsToGenericFuncs2)
|
LUAU_FASTFLAG(LuauClipVariadicAnysFromArgsToGenericFuncs2)
|
||||||
|
|
||||||
|
@ -467,8 +467,7 @@ local b: B.T = a
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
{
|
{
|
||||||
const std::string expected =
|
const std::string expected = "Type 'T' from 'game/A' could not be converted into 'T' from 'game/B'; \n"
|
||||||
"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 "
|
"this is because accessing `x` results in `number` in the former type and `string` in the latter type, and "
|
||||||
"`number` is not exactly `string`";
|
"`number` is not exactly `string`";
|
||||||
CHECK(expected == toString(result.errors[0]));
|
CHECK(expected == toString(result.errors[0]));
|
||||||
|
@ -514,8 +513,7 @@ local b: B.T = a
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
{
|
{
|
||||||
const std::string expected =
|
const std::string expected = "Type 'T' from 'game/B' could not be converted into 'T' from 'game/C'; \n"
|
||||||
"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 "
|
"this is because accessing `x` results in `number` in the former type and `string` in the latter type, and "
|
||||||
"`number` is not exactly `string`";
|
"`number` is not exactly `string`";
|
||||||
CHECK(expected == toString(result.errors[0]));
|
CHECK(expected == toString(result.errors[0]));
|
||||||
|
@ -787,7 +785,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "leaky_generics")
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
CHECK_EQ("(unknown) -> unknown", toString(requireTypeAtPosition({13, 23})));
|
CHECK_EQ("(unknown) -> unknown", toString(requireTypeAtPosition({13, 23})));
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TypeInferOperators");
|
TEST_SUITE_BEGIN("TypeInferOperators");
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ TEST_CASE_FIXTURE(Fixture, "or_joins_types")
|
||||||
)");
|
)");
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
// FIXME: Regression
|
// FIXME: Regression
|
||||||
CHECK("(string & ~(false?)) | number" == toString(*requireType("s")));
|
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);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
// FIXME: Regression.
|
// FIXME: Regression.
|
||||||
CHECK("(string & ~(false?)) | number" == toString(*requireType("s")));
|
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);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
// FIXME: Regression
|
// FIXME: Regression
|
||||||
CHECK("(string & ~(false?)) | string" == toString(requireType("s")));
|
CHECK("(string & ~(false?)) | string" == toString(requireType("s")));
|
||||||
|
@ -634,7 +634,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "typecheck_unary_minus_error")
|
||||||
local a = -foo
|
local a = -foo
|
||||||
)");
|
)");
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization && FFlag::LuauSolverV2)
|
if (FFlag::LuauEagerGeneralization2 && FFlag::LuauSolverV2)
|
||||||
{
|
{
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||||
LUAU_FASTFLAG(LuauFunctionCallsAreNotNilable)
|
LUAU_FASTFLAG(LuauFunctionCallsAreNotNilable)
|
||||||
LUAU_FASTFLAG(LuauWeakNilRefinementType)
|
LUAU_FASTFLAG(LuauWeakNilRefinementType)
|
||||||
LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions)
|
LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions)
|
||||||
|
@ -770,7 +770,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "nonoptional_type_can_narrow_to_nil_if_sense_
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
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("string & unknown & unknown & ~nil" == toString(requireTypeAtPosition({6, 24}))); // type(v) ~= "nil"
|
||||||
|
@ -1273,7 +1273,10 @@ TEST_CASE_FIXTURE(Fixture, "discriminate_from_truthiness_of_x")
|
||||||
// time, sometimes this is due to hitting the simplifier rather than
|
// time, sometimes this is due to hitting the simplifier rather than
|
||||||
// normalization.
|
// normalization.
|
||||||
CHECK("{ tag: \"exists\", x: string } & { x: ~(false?) }" == toString(requireTypeAtPosition({5, 28})));
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -2514,7 +2517,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "remove_recursive_upper_bound_when_generalizi
|
||||||
end
|
end
|
||||||
)"));
|
)"));
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
CHECK_EQ("nil & string & unknown", toString(requireTypeAtPosition({4, 24})));
|
CHECK_EQ("nil & string & unknown", toString(requireTypeAtPosition({4, 24})));
|
||||||
else
|
else
|
||||||
CHECK_EQ("nil", toString(requireTypeAtPosition({4, 24})));
|
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")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1451")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauWeakNilRefinementType, true}};
|
||||||
{FFlag::LuauSolverV2, true},
|
|
||||||
{FFlag::LuauWeakNilRefinementType, true}
|
|
||||||
};
|
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||||
type Part = {
|
type Part = {
|
||||||
|
@ -2682,10 +2682,17 @@ TEST_CASE_FIXTURE(RefinementExternTypeFixture, "cannot_call_a_function")
|
||||||
{
|
{
|
||||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
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)
|
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
|
else
|
||||||
CHECK_EQ(toString(result.errors[1]), "Cannot call a value of type function");
|
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);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
|
||||||
if (FFlag::LuauBetterCannotCallFunctionPrimitive)
|
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
|
else
|
||||||
CHECK_EQ(toString(result.errors[0]), "Cannot call a value of type function");
|
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);
|
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]));
|
CHECK(expected == toString(result.errors[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -464,7 +465,6 @@ TEST_CASE_FIXTURE(Fixture, "parametric_tagged_union_alias")
|
||||||
"\ncould not be converted into\n\t"
|
"\ncould not be converted into\n\t"
|
||||||
"'Err<number> | Ok<string>'";
|
"'Err<number> | Ok<string>'";
|
||||||
CHECK(toString(result.errors[0]) == expectedError);
|
CHECK(toString(result.errors[0]) == expectedError);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "if_then_else_expression_singleton_options")
|
TEST_CASE_FIXTURE(Fixture, "if_then_else_expression_singleton_options")
|
||||||
|
|
|
@ -21,8 +21,8 @@ LUAU_FASTFLAG(LuauSolverV2)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||||
LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering)
|
LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||||
LUAU_FASTFLAG(DebugLuauAssertOnForcedConstraint)
|
LUAU_FASTFLAG(DebugLuauAssertOnForcedConstraint)
|
||||||
LUAU_FASTFLAG(LuauBidirectionalInferenceElideAssert)
|
LUAU_FASTFLAG(LuauBidirectionalInferenceElideAssert)
|
||||||
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
||||||
|
@ -702,7 +702,7 @@ TEST_CASE_FIXTURE(Fixture, "indexers_get_quantified_too")
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2 && FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauSolverV2 && FFlag::LuauEagerGeneralization2)
|
||||||
CHECK("<a>({a}) -> ()" == toString(requireType("swap")));
|
CHECK("<a>({a}) -> ()" == toString(requireType("swap")));
|
||||||
else if (FFlag::LuauSolverV2)
|
else if (FFlag::LuauSolverV2)
|
||||||
CHECK("({unknown}) -> ()" == toString(requireType("swap")));
|
CHECK("({unknown}) -> ()" == toString(requireType("swap")));
|
||||||
|
@ -2379,7 +2379,7 @@ TEST_CASE_FIXTURE(Fixture, "invariant_table_properties_means_instantiating_table
|
||||||
local c : string = t.m("hi")
|
local c : string = t.m("hi")
|
||||||
)");
|
)");
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization && FFlag::LuauSolverV2)
|
if (FFlag::LuauEagerGeneralization2 && FFlag::LuauSolverV2)
|
||||||
{
|
{
|
||||||
// FIXME CLI-151985
|
// FIXME CLI-151985
|
||||||
LUAU_CHECK_ERROR_COUNT(2, result);
|
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[] = {
|
ScopedFastFlag sff[] = {
|
||||||
{FFlag::LuauReportSubtypingErrors, true},
|
{FFlag::LuauReportSubtypingErrors, true},
|
||||||
{FFlag::LuauEagerGeneralization, true},
|
{FFlag::LuauEagerGeneralization2, true},
|
||||||
{FFlag::LuauSubtypeGenericsAndNegations, true},
|
{FFlag::LuauSubtypeGenericsAndNegations, true},
|
||||||
{FFlag::LuauNoMoreInjectiveTypeFunctions, true}
|
{FFlag::LuauNoMoreInjectiveTypeFunctions, true}
|
||||||
};
|
};
|
||||||
|
@ -4298,7 +4298,7 @@ TEST_CASE_FIXTURE(Fixture, "cli_84607_missing_prop_in_array_or_dict")
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
{
|
{
|
||||||
for (const auto& err: result.errors)
|
for (const auto& err : result.errors)
|
||||||
{
|
{
|
||||||
const auto* error = get<MissingProperties>(err);
|
const auto* error = get<MissingProperties>(err);
|
||||||
REQUIRE(error);
|
REQUIRE(error);
|
||||||
|
@ -4644,9 +4644,7 @@ TEST_CASE_FIXTURE(Fixture, "table_writes_introduce_write_properties")
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ScopedFastFlag sff[] = {
|
ScopedFastFlag sff[] = {
|
||||||
{FFlag::LuauEagerGeneralization, true},
|
{FFlag::LuauEagerGeneralization2, true}, {FFlag::LuauSubtypeGenericsAndNegations, true}, {FFlag::LuauNoMoreInjectiveTypeFunctions, true}
|
||||||
{FFlag::LuauSubtypeGenericsAndNegations, true},
|
|
||||||
{FFlag::LuauNoMoreInjectiveTypeFunctions, true}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
|
@ -4698,7 +4696,7 @@ TEST_CASE_FIXTURE(Fixture, "refined_thing_can_be_an_array")
|
||||||
end
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2 && !FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauSolverV2 && !FFlag::LuauEagerGeneralization2)
|
||||||
{
|
{
|
||||||
LUAU_CHECK_ERROR_COUNT(1, result);
|
LUAU_CHECK_ERROR_COUNT(1, result);
|
||||||
LUAU_CHECK_ERROR(result, NotATable);
|
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);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
// FIXME CLI-114134. We need to simplify types more consistently.
|
// FIXME CLI-114134. We need to simplify types more consistently.
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
CHECK("({number} & {number}, unknown) -> ()" == toString(requireType("f")));
|
CHECK("({number} & {number}, unknown) -> ()" == toString(requireType("f")));
|
||||||
else
|
else
|
||||||
CHECK_EQ("(unknown & {number} & {number}, unknown) -> ()", toString(requireType("f")));
|
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)
|
if (!FFlag::LuauSolverV2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type self = {} & {}
|
type self = {} & {}
|
||||||
type Class = typeof(setmetatable())
|
type Class = typeof(setmetatable())
|
||||||
|
@ -5098,18 +5094,20 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "subtyping_with_a_metatable_table_path")
|
||||||
end
|
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(
|
CHECK_EQ(
|
||||||
"Type pack '{ @metatable { }, { } & { } }' could not be converted into 'Class'; \n"
|
"Type pack '{ @metatable { }, { } & { } }' could not be converted into 'setmetatable<nil, nil>'; \n"
|
||||||
"this is because \n\t"
|
"this is because the 1st entry in the type pack is `{ @metatable { }, { } & { } }` and in the 1st entry in the type packreduces to "
|
||||||
" * in the 1st entry in the type pack, the metatable portion is `{ }` in the former type and `nil` in the latter type, and `{ }` "
|
"`never`, and `{ @metatable { }, { } & { } }` is not a subtype of `never`",
|
||||||
"is not a subtype of `nil`\n\t"
|
toString(result.errors[2])
|
||||||
" * 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])
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5399,10 +5397,7 @@ TEST_CASE_FIXTURE(Fixture, "returning_mismatched_optional_in_table")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "optional_function_in_table")
|
TEST_CASE_FIXTURE(Fixture, "optional_function_in_table")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauTableLiteralSubtypeSpecificCheck, true}};
|
||||||
{FFlag::LuauSolverV2, true},
|
|
||||||
{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}
|
|
||||||
};
|
|
||||||
|
|
||||||
LUAU_CHECK_NO_ERRORS(check(R"(
|
LUAU_CHECK_NO_ERRORS(check(R"(
|
||||||
local t: { (() -> ())? } = {
|
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")
|
TEST_CASE_FIXTURE(Fixture, "bigger_nested_table_causes_big_type_error")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauTableLiteralSubtypeSpecificCheck, true}};
|
||||||
{FFlag::LuauSolverV2, true},
|
|
||||||
{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}
|
|
||||||
};
|
|
||||||
|
|
||||||
auto result = check(R"(
|
auto result = check(R"(
|
||||||
type File = {
|
type File = {
|
||||||
|
@ -5794,7 +5786,7 @@ TEST_CASE_FIXTURE(Fixture, "large_table_inference_does_not_bleed")
|
||||||
local otherWords: { Word } = {"foo"}
|
local otherWords: { Word } = {"foo"}
|
||||||
)");
|
)");
|
||||||
LUAU_REQUIRE_ERROR_COUNT(3, result);
|
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 that all of the errors are localized to `words`, not `otherWords`
|
||||||
CHECK(err.location.begin.line == 2);
|
CHECK(err.location.begin.line == 2);
|
||||||
}
|
}
|
||||||
|
@ -5809,10 +5801,7 @@ TEST_CASE_FIXTURE(Fixture, "extremely_large_table" * doctest::timeout(2.0))
|
||||||
{FFlag::LuauDisablePrimitiveInferenceInLargeTables, true},
|
{FFlag::LuauDisablePrimitiveInferenceInLargeTables, true},
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::string source =
|
const std::string source = "local res = {\n" + rep("\"foo\",\n", 100'000) + "}";
|
||||||
"local res = {\n" +
|
|
||||||
rep("\"foo\",\n", 100'000) +
|
|
||||||
"}";
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(check(source));
|
LUAU_REQUIRE_NO_ERRORS(check(source));
|
||||||
CHECK_EQ("{string}", toString(requireType("res"), {true}));
|
CHECK_EQ("{string}", toString(requireType("res"), {true}));
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ LUAU_FASTFLAG(LuauTypeCheckerAcceptNumberConcats)
|
||||||
LUAU_FASTFLAG(LuauPreprocessTypestatedArgument)
|
LUAU_FASTFLAG(LuauPreprocessTypestatedArgument)
|
||||||
LUAU_FASTFLAG(LuauMagicFreezeCheckBlocked2)
|
LUAU_FASTFLAG(LuauMagicFreezeCheckBlocked2)
|
||||||
LUAU_FASTFLAG(LuauNoMoreInjectiveTypeFunctions)
|
LUAU_FASTFLAG(LuauNoMoreInjectiveTypeFunctions)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||||
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
||||||
LUAU_FASTFLAG(LuauHasPropProperBlock)
|
LUAU_FASTFLAG(LuauHasPropProperBlock)
|
||||||
LUAU_FASTFLAG(LuauStringPartLengthLimit)
|
LUAU_FASTFLAG(LuauStringPartLengthLimit)
|
||||||
|
@ -447,7 +447,7 @@ TEST_CASE_FIXTURE(Fixture, "check_expr_recursion_limit")
|
||||||
#endif
|
#endif
|
||||||
ScopedFastInt luauRecursionLimit{FInt::LuauRecursionLimit, limit + 100};
|
ScopedFastInt luauRecursionLimit{FInt::LuauRecursionLimit, limit + 100};
|
||||||
ScopedFastInt luauCheckRecursionLimit{FInt::LuauCheckRecursionLimit, limit - 100};
|
ScopedFastInt luauCheckRecursionLimit{FInt::LuauCheckRecursionLimit, limit - 100};
|
||||||
ScopedFastFlag _{FFlag::LuauEagerGeneralization, false};
|
ScopedFastFlag _{FFlag::LuauEagerGeneralization2, false};
|
||||||
|
|
||||||
CheckResult result = check(R"(("foo"))" + rep(":lower()", limit));
|
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")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "assert_table_freeze_constraint_solving")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauMagicFreezeCheckBlocked2, true}};
|
||||||
{FFlag::LuauSolverV2, true},
|
|
||||||
{FFlag::LuauMagicFreezeCheckBlocked2, true}
|
|
||||||
};
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||||
local f = table.freeze
|
local f = table.freeze
|
||||||
f(table)
|
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")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_assert_table_freeze_constraint_solving")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauMagicFreezeCheckBlocked2, true}};
|
||||||
{FFlag::LuauSolverV2, true},
|
|
||||||
{FFlag::LuauMagicFreezeCheckBlocked2, true}
|
|
||||||
};
|
|
||||||
// This is the original fuzzer version of the above issue.
|
// This is the original fuzzer version of the above issue.
|
||||||
CheckResult results = check(R"(
|
CheckResult results = check(R"(
|
||||||
local function l0()
|
local function l0()
|
||||||
|
@ -2041,7 +2035,7 @@ TEST_CASE_FIXTURE(Fixture, "fuzz_generalize_one_remove_type_assert")
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sffs[] = {
|
||||||
{FFlag::LuauSolverV2, true},
|
{FFlag::LuauSolverV2, true},
|
||||||
{FFlag::LuauHasPropProperBlock, true},
|
{FFlag::LuauHasPropProperBlock, true},
|
||||||
{FFlag::LuauEagerGeneralization, true},
|
{FFlag::LuauEagerGeneralization2, true},
|
||||||
{FFlag::LuauOptimizeFalsyAndTruthyIntersect, true},
|
{FFlag::LuauOptimizeFalsyAndTruthyIntersect, true},
|
||||||
{FFlag::LuauSubtypeGenericsAndNegations, true},
|
{FFlag::LuauSubtypeGenericsAndNegations, true},
|
||||||
{FFlag::LuauNoMoreInjectiveTypeFunctions, true},
|
{FFlag::LuauNoMoreInjectiveTypeFunctions, true},
|
||||||
|
@ -2079,7 +2073,7 @@ TEST_CASE_FIXTURE(Fixture, "fuzz_generalize_one_remove_type_assert_2")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sffs[] = {
|
||||||
{FFlag::LuauSolverV2, true},
|
{FFlag::LuauSolverV2, true},
|
||||||
{FFlag::LuauEagerGeneralization, true},
|
{FFlag::LuauEagerGeneralization2, true},
|
||||||
{FFlag::LuauOptimizeFalsyAndTruthyIntersect, true},
|
{FFlag::LuauOptimizeFalsyAndTruthyIntersect, true},
|
||||||
{FFlag::LuauSubtypeGenericsAndNegations, true},
|
{FFlag::LuauSubtypeGenericsAndNegations, true},
|
||||||
{FFlag::LuauNoMoreInjectiveTypeFunctions, true},
|
{FFlag::LuauNoMoreInjectiveTypeFunctions, true},
|
||||||
|
@ -2116,7 +2110,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_simplify_combinatorial_explosion")
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sffs[] = {
|
||||||
{FFlag::LuauSolverV2, true},
|
{FFlag::LuauSolverV2, true},
|
||||||
{FFlag::LuauHasPropProperBlock, true},
|
{FFlag::LuauHasPropProperBlock, true},
|
||||||
{FFlag::LuauEagerGeneralization, true},
|
{FFlag::LuauEagerGeneralization2, true},
|
||||||
{FFlag::LuauOptimizeFalsyAndTruthyIntersect, true},
|
{FFlag::LuauOptimizeFalsyAndTruthyIntersect, true},
|
||||||
{FFlag::LuauStringPartLengthLimit, true},
|
{FFlag::LuauStringPartLengthLimit, true},
|
||||||
{FFlag::LuauSimplificationRecheckAssumption, true},
|
{FFlag::LuauSimplificationRecheckAssumption, true},
|
||||||
|
|
|
@ -12,7 +12,7 @@ using namespace Luau;
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||||
LUAU_FASTFLAG(LuauReportSubtypingErrors)
|
LUAU_FASTFLAG(LuauReportSubtypingErrors)
|
||||||
LUAU_FASTFLAG(LuauTrackInferredFunctionTypeFromCall)
|
LUAU_FASTFLAG(LuauTrackInferredFunctionTypeFromCall)
|
||||||
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
|
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
|
||||||
|
@ -100,7 +100,7 @@ TEST_CASE_FIXTURE(Fixture, "higher_order_function")
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
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")));
|
CHECK_EQ("<a, b..., c...>((c...) -> (b...), (a) -> (c...), a) -> (b...)", toString(requireType("apply")));
|
||||||
else
|
else
|
||||||
CHECK_EQ("<a, b..., c...>((b...) -> (c...), (a) -> (b...), a) -> (c...)", toString(requireType("apply")));
|
CHECK_EQ("<a, b..., c...>((b...) -> (c...), (a) -> (b...), a) -> (c...)", toString(requireType("apply")));
|
||||||
|
@ -966,8 +966,7 @@ a = b
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
{
|
{
|
||||||
|
|
||||||
const std::string expected =
|
const std::string expected = "Type\n\t"
|
||||||
"Type\n\t"
|
|
||||||
"'() -> (number, ...boolean)'"
|
"'() -> (number, ...boolean)'"
|
||||||
"\ncould not be converted into\n\t"
|
"\ncould not be converted into\n\t"
|
||||||
"'() -> (number, ...string)'; \n"
|
"'() -> (number, ...string)'; \n"
|
||||||
|
|
|
@ -8,7 +8,7 @@ LUAU_FASTFLAG(LuauRefineWaitForBlockedTypesInTarget)
|
||||||
LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType)
|
LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType)
|
||||||
LUAU_FASTFLAG(LuauDfgIfBlocksShouldRespectControlFlow)
|
LUAU_FASTFLAG(LuauDfgIfBlocksShouldRespectControlFlow)
|
||||||
LUAU_FASTFLAG(LuauReportSubtypingErrors)
|
LUAU_FASTFLAG(LuauReportSubtypingErrors)
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||||
LUAU_FASTFLAG(LuauPreprocessTypestatedArgument)
|
LUAU_FASTFLAG(LuauPreprocessTypestatedArgument)
|
||||||
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
|
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
|
||||||
LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops)
|
LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops)
|
||||||
|
@ -418,7 +418,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "prototyped_recursive_functions_but_has_futur
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sffs[] = {
|
||||||
{FFlag::LuauSolverV2, true},
|
{FFlag::LuauSolverV2, true},
|
||||||
{FFlag::LuauReportSubtypingErrors, true},
|
{FFlag::LuauReportSubtypingErrors, true},
|
||||||
{FFlag::LuauEagerGeneralization, true},
|
{FFlag::LuauEagerGeneralization2, true},
|
||||||
{FFlag::LuauSubtypeGenericsAndNegations, true},
|
{FFlag::LuauSubtypeGenericsAndNegations, true},
|
||||||
{FFlag::LuauNoMoreInjectiveTypeFunctions, true},
|
{FFlag::LuauNoMoreInjectiveTypeFunctions, true},
|
||||||
};
|
};
|
||||||
|
@ -763,9 +763,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "refinement_through_erroring")
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "refinement_through_erroring_in_loop")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "refinement_through_erroring_in_loop")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sffs[] = {
|
||||||
{FFlag::LuauSolverV2, true},
|
{FFlag::LuauSolverV2, true}, {FFlag::LuauDfgIfBlocksShouldRespectControlFlow, true}, {FFlag::LuauDfgAllowUpdatesInLoops, true}
|
||||||
{FFlag::LuauDfgIfBlocksShouldRespectControlFlow, true},
|
|
||||||
{FFlag::LuauDfgAllowUpdatesInLoops,true}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
|
|
|
@ -10,7 +10,7 @@ using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauEagerGeneralization)
|
LUAU_FASTFLAG(LuauEagerGeneralization2)
|
||||||
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
|
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("UnionTypes");
|
TEST_SUITE_BEGIN("UnionTypes");
|
||||||
|
@ -675,8 +675,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_union_write_indirect")
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
// NOTE: union normalization will improve this message
|
// NOTE: union normalization will improve this message
|
||||||
|
|
||||||
const std::string expected =
|
const std::string expected = "Type\n\t"
|
||||||
"Type\n\t"
|
|
||||||
"'(string) -> number'"
|
"'(string) -> number'"
|
||||||
"\ncould not be converted into\n\t"
|
"\ncould not be converted into\n\t"
|
||||||
"'((number) -> string) | ((number) -> string)'; none of the union options are compatible";
|
"'((number) -> string) | ((number) -> string)'; none of the union options are compatible";
|
||||||
|
@ -767,8 +766,7 @@ TEST_CASE_FIXTURE(Fixture, "union_of_functions_mentioning_generic_typepacks")
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
|
||||||
const std::string expected =
|
const std::string expected = "Type\n\t"
|
||||||
"Type\n\t"
|
|
||||||
"'(number, a...) -> (number?, a...)'"
|
"'(number, a...) -> (number?, a...)'"
|
||||||
"\ncould not be converted into\n\t"
|
"\ncould not be converted into\n\t"
|
||||||
"'((number) -> number) | ((number?, a...) -> (number?, a...))'; none of the union options are compatible";
|
"'((number) -> number) | ((number?, a...) -> (number?, a...))'; none of the union options are compatible";
|
||||||
|
@ -788,8 +786,7 @@ TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_arg_arities")
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
|
||||||
const std::string expected =
|
const std::string expected = "Type\n\t"
|
||||||
"Type\n\t"
|
|
||||||
"'(number) -> number?'"
|
"'(number) -> number?'"
|
||||||
"\ncould not be converted into\n\t"
|
"\ncould not be converted into\n\t"
|
||||||
"'((number) -> nil) | ((number, string?) -> number)'; none of the union options are compatible";
|
"'((number) -> nil) | ((number, string?) -> number)'; none of the union options are compatible";
|
||||||
|
@ -809,8 +806,7 @@ TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_result_arities")
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
|
||||||
const std::string expected =
|
const std::string expected = "Type\n\t"
|
||||||
"Type\n\t"
|
|
||||||
"'() -> number | string'"
|
"'() -> number | string'"
|
||||||
"\ncould not be converted into\n\t"
|
"\ncould not be converted into\n\t"
|
||||||
"'(() -> (string, string)) | (() -> number)'; none of the union options are compatible";
|
"'(() -> (string, string)) | (() -> number)'; none of the union options are compatible";
|
||||||
|
@ -830,8 +826,7 @@ TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_variadics")
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
|
||||||
const std::string expected =
|
const std::string expected = "Type\n\t"
|
||||||
"Type\n\t"
|
|
||||||
"'(...nil) -> (...number?)'"
|
"'(...nil) -> (...number?)'"
|
||||||
"\ncould not be converted into\n\t"
|
"\ncould not be converted into\n\t"
|
||||||
"'((...string?) -> (...number)) | ((...string?) -> nil)'; none of the union options are compatible";
|
"'((...string?) -> (...number)) | ((...string?) -> nil)'; none of the union options are compatible";
|
||||||
|
@ -850,8 +845,7 @@ TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_arg_variadics")
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
{
|
{
|
||||||
const std::string expected =
|
const std::string expected = "Type\n\t"
|
||||||
"Type\n\t"
|
|
||||||
"'(number) -> ()'"
|
"'(number) -> ()'"
|
||||||
"\ncould not be converted into\n\t"
|
"\ncould not be converted into\n\t"
|
||||||
"'((...number?) -> ()) | ((number?) -> ())'";
|
"'((...number?) -> ()) | ((number?) -> ())'";
|
||||||
|
@ -880,8 +874,7 @@ TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_result_variadics
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
|
||||||
const std::string expected =
|
const std::string expected = "Type\n\t"
|
||||||
"Type\n\t"
|
|
||||||
"'() -> (number?, ...number)'"
|
"'() -> (number?, ...number)'"
|
||||||
"\ncould not be converted into\n\t"
|
"\ncould not be converted into\n\t"
|
||||||
"'(() -> (...number)) | (() -> number)'; none of the union options are compatible";
|
"'(() -> (...number)) | (() -> number)'; none of the union options are compatible";
|
||||||
|
@ -902,13 +895,14 @@ TEST_CASE_FIXTURE(Fixture, "less_greedy_unification_with_union_types")
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
if (FFlag::LuauEagerGeneralization)
|
if (FFlag::LuauEagerGeneralization2)
|
||||||
CHECK_EQ(
|
CHECK_EQ(
|
||||||
"<a>(({ read x: a } & { x: number }) | ({ read x: a } & { x: string })) -> { x: number } | { x: string }", toString(requireType("f"))
|
"<a>(({ read x: a } & { x: number }) | ({ read x: a } & { x: string })) -> { x: number } | { x: string }", toString(requireType("f"))
|
||||||
);
|
);
|
||||||
else
|
else
|
||||||
CHECK_EQ(
|
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