Skip to content

Commit

Permalink
Sync to upstream/release/578 (#939)
Browse files Browse the repository at this point in the history
* Fixed gcc warning about uninitialized `std::optional`
* Fixed inlining of functions when they are used to compute their own
arguments

In the new type solver:
* Type families that are not part of a function signature cannot be
resolved at instantiation time and will now produce an error. This will
be relaxed in the future when we get constraint clauses on function
signatures (internally)
* `never` type is now comparable
* Improved typechecking of `for..in` statements
* Fixed checks for number type in `Add` type family
* Performance was improved, with particularly large gains on large
projects

And in native code generation (jit):
* We eliminated the call instruction overhead when native code support
is enabled in the VM
* Small optimizations to arm64 lowering
* Reworked LOP_GETIMPORT handling to reduce assembly code size
* Fixed non-deterministic binary output
* Fixed bad code generation caused by incorrect SSA to VM register links
invalidation

---------

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
  • Loading branch information
3 people committed May 25, 2023
1 parent 721f6e1 commit 271c509
Show file tree
Hide file tree
Showing 82 changed files with 1,615 additions and 788 deletions.
3 changes: 3 additions & 0 deletions Analysis/include/Luau/Constraint.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ struct IterableConstraint
{
TypePackId iterator;
TypePackId variables;

const AstNode* nextAstFragment;
DenseHashMap<const AstNode*, TypeId>* astOverloadResolvedTypes;
};

// name(namedType) = name
Expand Down
4 changes: 2 additions & 2 deletions Analysis/include/Luau/ConstraintSolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,15 +201,15 @@ struct ConstraintSolver
* @param subType the sub-type to unify.
* @param superType the super-type to unify.
*/
void unify(TypeId subType, TypeId superType, NotNull<Scope> scope);
ErrorVec unify(TypeId subType, TypeId superType, NotNull<Scope> scope);

/**
* Creates a new Unifier and performs a single unification operation. Commits
* the result.
* @param subPack the sub-type pack to unify.
* @param superPack the super-type pack to unify.
*/
void unify(TypePackId subPack, TypePackId superPack, NotNull<Scope> scope);
ErrorVec unify(TypePackId subPack, TypePackId superPack, NotNull<Scope> scope);

/** Pushes a new solver constraint to the solver.
* @param cv the body of the constraint.
Expand Down
16 changes: 15 additions & 1 deletion Analysis/include/Luau/Error.h
Original file line number Diff line number Diff line change
Expand Up @@ -343,13 +343,27 @@ struct UninhabitedTypePackFamily
bool operator==(const UninhabitedTypePackFamily& rhs) const;
};

struct WhereClauseNeeded
{
TypeId ty;

bool operator==(const WhereClauseNeeded& rhs) const;
};

struct PackWhereClauseNeeded
{
TypePackId tp;

bool operator==(const PackWhereClauseNeeded& rhs) const;
};

using TypeErrorData =
Variant<TypeMismatch, UnknownSymbol, UnknownProperty, NotATable, CannotExtendTable, OnlyTablesCanHaveMethods, DuplicateTypeDefinition,
CountMismatch, FunctionDoesNotTakeSelf, FunctionRequiresSelf, OccursCheckFailed, UnknownRequire, IncorrectGenericParameterCount, SyntaxError,
CodeTooComplex, UnificationTooComplex, UnknownPropButFoundLikeProp, GenericError, InternalError, CannotCallNonFunction, ExtraInformation,
DeprecatedApiUsed, ModuleHasCyclicDependency, IllegalRequire, FunctionExitsWithoutReturning, DuplicateGenericParameter,
CannotInferBinaryOperation, MissingProperties, SwappedGenericTypeParameter, OptionalValueAccess, MissingUnionProperty, TypesAreUnrelated,
NormalizationTooComplex, TypePackMismatch, DynamicPropertyLookupOnClassesUnsafe, UninhabitedTypeFamily, UninhabitedTypePackFamily>;
NormalizationTooComplex, TypePackMismatch, DynamicPropertyLookupOnClassesUnsafe, UninhabitedTypeFamily, UninhabitedTypePackFamily, WhereClauseNeeded, PackWhereClauseNeeded>;

struct TypeErrorSummary
{
Expand Down
2 changes: 0 additions & 2 deletions Analysis/include/Luau/Frontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,6 @@ struct Frontend
std::optional<CheckResult> getCheckResult(const ModuleName& name, bool accumulateNested, bool forAutocomplete = false);

private:
CheckResult check_DEPRECATED(const ModuleName& name, std::optional<FrontendOptions> optionOverride = {});

struct TypeCheckLimits
{
std::optional<double> finishTime;
Expand Down
11 changes: 9 additions & 2 deletions Analysis/include/Luau/Normalize.h
Original file line number Diff line number Diff line change
Expand Up @@ -285,14 +285,19 @@ class Normalizer
std::unordered_map<const TypeIds*, TypeId> cachedIntersections;
std::unordered_map<const TypeIds*, TypeId> cachedUnions;
std::unordered_map<const TypeIds*, std::unique_ptr<TypeIds>> cachedTypeIds;

DenseHashMap<TypeId, bool> cachedIsInhabited{nullptr};
DenseHashMap<std::pair<TypeId, TypeId>, bool, TypeIdPairHash> cachedIsInhabitedIntersection{{nullptr, nullptr}};

bool withinResourceLimits();

public:
TypeArena* arena;
NotNull<BuiltinTypes> builtinTypes;
NotNull<UnifierSharedState> sharedState;
bool cacheInhabitance = false;

Normalizer(TypeArena* arena, NotNull<BuiltinTypes> builtinTypes, NotNull<UnifierSharedState> sharedState);
Normalizer(TypeArena* arena, NotNull<BuiltinTypes> builtinTypes, NotNull<UnifierSharedState> sharedState, bool cacheInhabitance = false);
Normalizer(const Normalizer&) = delete;
Normalizer(Normalizer&&) = delete;
Normalizer() = delete;
Expand Down Expand Up @@ -355,8 +360,10 @@ class Normalizer
bool normalizeIntersections(const std::vector<TypeId>& intersections, NormalizedType& outType);

// Check for inhabitance
bool isInhabited(TypeId ty, std::unordered_set<TypeId> seen = {});
bool isInhabited(TypeId ty);
bool isInhabited(TypeId ty, std::unordered_set<TypeId> seen);
bool isInhabited(const NormalizedType* norm, std::unordered_set<TypeId> seen = {});

// Check for intersections being inhabited
bool isIntersectionInhabited(TypeId left, TypeId right);

Expand Down
31 changes: 31 additions & 0 deletions Analysis/include/Luau/Substitution.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,13 @@ struct Tarjan
void visitChild(TypeId ty);
void visitChild(TypePackId ty);

template<typename Ty>
void visitChild(std::optional<Ty> ty)
{
if (ty)
visitChild(*ty);
}

// Visit the root vertex.
TarjanResult visitRoot(TypeId ty);
TarjanResult visitRoot(TypePackId ty);
Expand All @@ -127,10 +134,22 @@ struct Tarjan
{
return false;
}

virtual bool ignoreChildren(TypePackId ty)
{
return false;
}

// Some subclasses might ignore children visit, but not other actions like replacing the children
virtual bool ignoreChildrenVisit(TypeId ty)
{
return ignoreChildren(ty);
}

virtual bool ignoreChildrenVisit(TypePackId ty)
{
return ignoreChildren(ty);
}
};

// We use Tarjan to calculate dirty bits. We set `dirty[i]` true
Expand Down Expand Up @@ -186,8 +205,10 @@ struct Substitution : FindDirty

TypeId replace(TypeId ty);
TypePackId replace(TypePackId tp);

void replaceChildren(TypeId ty);
void replaceChildren(TypePackId tp);

TypeId clone(TypeId ty);
TypePackId clone(TypePackId tp);

Expand All @@ -211,6 +232,16 @@ struct Substitution : FindDirty
{
return arena->addTypePack(TypePackVar{tp});
}

private:
template<typename Ty>
std::optional<Ty> replace(std::optional<Ty> ty)
{
if (ty)
return replace(*ty);
else
return std::nullopt;
}
};

} // namespace Luau
25 changes: 13 additions & 12 deletions Analysis/include/Luau/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,13 @@ struct Property
static Property writeonly(TypeId ty);
static Property rw(TypeId ty); // Shared read-write type.
static Property rw(TypeId read, TypeId write); // Separate read-write type.
static std::optional<Property> create(std::optional<TypeId> read, std::optional<TypeId> write);

// Invariant: at least one of the two optionals are not nullopt!
// If the read type is not nullopt, but the write type is, then the property is readonly.
// If the read type is nullopt, but the write type is not, then the property is writeonly.
// If the read and write types are not nullopt, then the property is read and write.
// Otherwise, an assertion where read and write types are both nullopt will be tripped.
static Property create(std::optional<TypeId> read, std::optional<TypeId> write);

bool deprecated = false;
std::string deprecatedSuggestion;
Expand All @@ -414,6 +420,8 @@ struct Property
std::optional<TypeId> readType() const;
std::optional<TypeId> writeType() const;

bool isShared() const;

private:
std::optional<TypeId> readTy;
std::optional<TypeId> writeTy;
Expand Down Expand Up @@ -614,30 +622,26 @@ struct IntersectionType
struct LazyType
{
LazyType() = default;
LazyType(std::function<TypeId()> thunk_DEPRECATED, std::function<void(LazyType&)> unwrap)
: thunk_DEPRECATED(thunk_DEPRECATED)
, unwrap(unwrap)
LazyType(std::function<void(LazyType&)> unwrap)
: unwrap(unwrap)
{
}

// std::atomic is sad and requires a manual copy
LazyType(const LazyType& rhs)
: thunk_DEPRECATED(rhs.thunk_DEPRECATED)
, unwrap(rhs.unwrap)
: unwrap(rhs.unwrap)
, unwrapped(rhs.unwrapped.load())
{
}

LazyType(LazyType&& rhs) noexcept
: thunk_DEPRECATED(std::move(rhs.thunk_DEPRECATED))
, unwrap(std::move(rhs.unwrap))
: unwrap(std::move(rhs.unwrap))
, unwrapped(rhs.unwrapped.load())
{
}

LazyType& operator=(const LazyType& rhs)
{
thunk_DEPRECATED = rhs.thunk_DEPRECATED;
unwrap = rhs.unwrap;
unwrapped = rhs.unwrapped.load();

Expand All @@ -646,15 +650,12 @@ struct LazyType

LazyType& operator=(LazyType&& rhs) noexcept
{
thunk_DEPRECATED = std::move(rhs.thunk_DEPRECATED);
unwrap = std::move(rhs.unwrap);
unwrapped = rhs.unwrapped.load();

return *this;
}

std::function<TypeId()> thunk_DEPRECATED;

std::function<void(LazyType&)> unwrap;
std::atomic<TypeId> unwrapped = nullptr;
};
Expand Down
9 changes: 9 additions & 0 deletions Analysis/include/Luau/TypeUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,13 @@ std::vector<TypeId> reduceUnion(const std::vector<TypeId>& types);
*/
TypeId stripNil(NotNull<BuiltinTypes> builtinTypes, TypeArena& arena, TypeId ty);

template<typename T, typename Ty>
const T* get(std::optional<Ty> ty)
{
if (ty)
return get<T>(*ty);
else
return nullptr;
}

} // namespace Luau
4 changes: 1 addition & 3 deletions Analysis/include/Luau/Unifier.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ struct Unifier
TypeArena* const types;
NotNull<BuiltinTypes> builtinTypes;
NotNull<Normalizer> normalizer;
Mode mode;

NotNull<Scope> scope; // const Scope maybe
TxnLog log;
Expand All @@ -78,7 +77,7 @@ struct Unifier
std::vector<TypePackId> blockedTypePacks;

Unifier(
NotNull<Normalizer> normalizer, Mode mode, NotNull<Scope> scope, const Location& location, Variance variance, TxnLog* parentLog = nullptr);
NotNull<Normalizer> normalizer, NotNull<Scope> scope, const Location& location, Variance variance, TxnLog* parentLog = nullptr);

// Configure the Unifier to test for scope subsumption via embedded Scope
// pointers rather than TypeLevels.
Expand Down Expand Up @@ -154,7 +153,6 @@ struct Unifier
LUAU_NOINLINE void reportError(Location location, TypeErrorData data);

private:
bool isNonstrictMode() const;
TypeMismatch::Context mismatchContext();

void checkChildUnifierTypeMismatch(const ErrorVec& innerErrors, TypeId wantedType, TypeId givenType);
Expand Down
35 changes: 28 additions & 7 deletions Analysis/include/Luau/VisitType.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

LUAU_FASTINT(LuauVisitRecursionLimit)
LUAU_FASTFLAG(LuauBoundLazyTypes2)
LUAU_FASTFLAG(DebugLuauReadWriteProperties)

namespace Luau
{
Expand Down Expand Up @@ -250,7 +251,18 @@ struct GenericTypeVisitor
else
{
for (auto& [_name, prop] : ttv->props)
traverse(prop.type());
{
if (FFlag::DebugLuauReadWriteProperties)
{
if (auto ty = prop.readType())
traverse(*ty);

if (auto ty = prop.writeType())
traverse(*ty);
}
else
traverse(prop.type());
}

if (ttv->indexer)
{
Expand All @@ -273,7 +285,18 @@ struct GenericTypeVisitor
if (visit(ty, *ctv))
{
for (const auto& [name, prop] : ctv->props)
traverse(prop.type());
{
if (FFlag::DebugLuauReadWriteProperties)
{
if (auto ty = prop.readType())
traverse(*ty);

if (auto ty = prop.writeType())
traverse(*ty);
}
else
traverse(prop.type());
}

if (ctv->parent)
traverse(*ctv->parent);
Expand Down Expand Up @@ -311,11 +334,9 @@ struct GenericTypeVisitor
}
else if (auto ltv = get<LazyType>(ty))
{
if (FFlag::LuauBoundLazyTypes2)
{
if (TypeId unwrapped = ltv->unwrapped)
traverse(unwrapped);
}
if (TypeId unwrapped = ltv->unwrapped)
traverse(unwrapped);

// Visiting into LazyType that hasn't been unwrapped may necessarily cause infinite expansion, so we don't do that on purpose.
// Asserting also makes no sense, because the type _will_ happen here, most likely as a property of some ClassType
// that doesn't need to be expanded.
Expand Down
22 changes: 20 additions & 2 deletions Analysis/src/AstQuery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

#include <algorithm>

LUAU_FASTFLAG(DebugLuauReadWriteProperties)

namespace Luau
{

Expand Down Expand Up @@ -501,12 +503,28 @@ std::optional<DocumentationSymbol> getDocumentationSymbolAtPosition(const Source
if (const TableType* ttv = get<TableType>(parentTy))
{
if (auto propIt = ttv->props.find(indexName->index.value); propIt != ttv->props.end())
return checkOverloadedDocumentationSymbol(module, propIt->second.type(), parentExpr, propIt->second.documentationSymbol);
{
if (FFlag::DebugLuauReadWriteProperties)
{
if (auto ty = propIt->second.readType())
return checkOverloadedDocumentationSymbol(module, *ty, parentExpr, propIt->second.documentationSymbol);
}
else
return checkOverloadedDocumentationSymbol(module, propIt->second.type(), parentExpr, propIt->second.documentationSymbol);
}
}
else if (const ClassType* ctv = get<ClassType>(parentTy))
{
if (auto propIt = ctv->props.find(indexName->index.value); propIt != ctv->props.end())
return checkOverloadedDocumentationSymbol(module, propIt->second.type(), parentExpr, propIt->second.documentationSymbol);
{
if (FFlag::DebugLuauReadWriteProperties)
{
if (auto ty = propIt->second.readType())
return checkOverloadedDocumentationSymbol(module, *ty, parentExpr, propIt->second.documentationSymbol);
}
else
return checkOverloadedDocumentationSymbol(module, propIt->second.type(), parentExpr, propIt->second.documentationSymbol);
}
}
}
}
Expand Down
Loading

0 comments on commit 271c509

Please sign in to comment.