Skip to content

Commit

Permalink
Sync to upstream/release/620 (#1223)
Browse files Browse the repository at this point in the history
# What's Changed

## New Type Solver
- Many more fixes to crashes, assertions, and hangs
- Annotated locals now countermand the inferred types of locals, meaning
that for a type `type MyType = number | string`, `local foo : MyType =
5` behaves the same as `local foo = 5 :: MyType`, where before, foo
would be assigned the type of the value on the rhs.
- Type Normalization now respects resource limits.
- Subtyping between classes and cyclic tables now supported

## Native Code Generation
- Work on the Native Code Generation(NCG) allocator continues

---

# Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: James McNellis <jmcnellis@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
  • Loading branch information
8 people committed Apr 5, 2024
1 parent 9649e5e commit 67e16cb
Show file tree
Hide file tree
Showing 42 changed files with 1,394 additions and 801 deletions.
36 changes: 25 additions & 11 deletions Analysis/include/Luau/Normalize.h
Expand Up @@ -216,6 +216,20 @@ struct NormalizedFunctionType
struct NormalizedType;
using NormalizedTyvars = std::unordered_map<TypeId, std::unique_ptr<NormalizedType>>;

// Operations provided by `Normalizer` can have ternary results:
// 1. The operation returned true.
// 2. The operation returned false.
// 3. They can hit resource limitations, which invalidates _all normalized types_.
enum class NormalizationResult
{
// The operation returned true or succeeded.
True,
// The operation returned false or failed.
False,
// Resource limits were hit, invalidating all normalized types.
HitLimits,
};

// A normalized type is either any, unknown, or one of the form P | T | F | G where
// * P is a union of primitive types (including singletons, classes and the error type)
// * T is a union of table types
Expand Down Expand Up @@ -366,8 +380,8 @@ class Normalizer
void unionFunctions(NormalizedFunctionType& heress, const NormalizedFunctionType& theress);
void unionTablesWithTable(TypeIds& heres, TypeId there);
void unionTables(TypeIds& heres, const TypeIds& theres);
bool unionNormals(NormalizedType& here, const NormalizedType& there, int ignoreSmallerTyvars = -1);
bool unionNormalWithTy(NormalizedType& here, TypeId there, Set<TypeId>& seenSetTypes, int ignoreSmallerTyvars = -1);
NormalizationResult unionNormals(NormalizedType& here, const NormalizedType& there, int ignoreSmallerTyvars = -1);
NormalizationResult unionNormalWithTy(NormalizedType& here, TypeId there, Set<TypeId>& seenSetTypes, int ignoreSmallerTyvars = -1);

// ------- Negations
std::optional<NormalizedType> negateNormal(const NormalizedType& here);
Expand All @@ -389,19 +403,19 @@ class Normalizer
std::optional<TypeId> intersectionOfFunctions(TypeId here, TypeId there);
void intersectFunctionsWithFunction(NormalizedFunctionType& heress, TypeId there);
void intersectFunctions(NormalizedFunctionType& heress, const NormalizedFunctionType& theress);
bool intersectTyvarsWithTy(NormalizedTyvars& here, TypeId there, Set<TypeId>& seenSetTypes);
bool intersectNormals(NormalizedType& here, const NormalizedType& there, int ignoreSmallerTyvars = -1);
bool intersectNormalWithTy(NormalizedType& here, TypeId there, Set<TypeId>& seenSetTypes);
bool normalizeIntersections(const std::vector<TypeId>& intersections, NormalizedType& outType);
NormalizationResult intersectTyvarsWithTy(NormalizedTyvars& here, TypeId there, Set<TypeId>& seenSetTypes);
NormalizationResult intersectNormals(NormalizedType& here, const NormalizedType& there, int ignoreSmallerTyvars = -1);
NormalizationResult intersectNormalWithTy(NormalizedType& here, TypeId there, Set<TypeId>& seenSetTypes);
NormalizationResult normalizeIntersections(const std::vector<TypeId>& intersections, NormalizedType& outType);

// Check for inhabitance
bool isInhabited(TypeId ty);
bool isInhabited(TypeId ty, Set<TypeId>& seen);
bool isInhabited(const NormalizedType* norm);
bool isInhabited(const NormalizedType* norm, Set<TypeId>& seen);
NormalizationResult isInhabited(TypeId ty);
NormalizationResult isInhabited(TypeId ty, Set<TypeId>& seen);
NormalizationResult isInhabited(const NormalizedType* norm);
NormalizationResult isInhabited(const NormalizedType* norm, Set<TypeId>& seen);

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

// -------- Convert back from a normalized type to a type
TypeId typeFromNormal(const NormalizedType& norm);
Expand Down
10 changes: 9 additions & 1 deletion Analysis/include/Luau/Subtyping.h
Expand Up @@ -103,6 +103,14 @@ struct SubtypingEnvironment
DenseHashMap<TypeId, GenericBounds> mappedGenerics{nullptr};
DenseHashMap<TypePackId, TypePackId> mappedGenericPacks{nullptr};

/*
* See the test cyclic_tables_are_assumed_to_be_compatible_with_classes for
* details.
*
* An empty value is equivalent to a nonexistent key.
*/
DenseHashMap<TypeId, TypeId> substitutions{nullptr};

DenseHashMap<std::pair<TypeId, TypeId>, SubtypingResult, TypePairHash> ephemeralCache{{}};

/// Applies `mappedGenerics` to the given type.
Expand Down Expand Up @@ -192,7 +200,7 @@ struct Subtyping
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const MetatableType* superMt);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const TableType* superTable);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const ClassType* subClass, const ClassType* superClass);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const ClassType* subClass, const TableType* superTable);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const ClassType* subClass, TypeId superTy, const TableType* superTable);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const FunctionType* subFunction, const FunctionType* superFunction);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const PrimitiveType* subPrim, const TableType* superTable);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const SingletonType* subSingleton, const TableType* superTable);
Expand Down
14 changes: 5 additions & 9 deletions Analysis/src/Autocomplete.cpp
Expand Up @@ -14,7 +14,6 @@
#include <utility>

LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
LUAU_FASTFLAGVARIABLE(LuauAutocompleteStringLiteralBounds, false);

static const std::unordered_set<std::string> kStatementStartingKeywords = {
"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"};
Expand Down Expand Up @@ -465,15 +464,12 @@ AutocompleteEntryMap autocompleteModuleTypes(const Module& module, Position posi

static void autocompleteStringSingleton(TypeId ty, bool addQuotes, AstNode* node, Position position, AutocompleteEntryMap& result)
{
if (FFlag::LuauAutocompleteStringLiteralBounds)
if (position == node->location.begin || position == node->location.end)
{
if (position == node->location.begin || position == node->location.end)
{
if (auto str = node->as<AstExprConstantString>(); str && str->quoteStyle == AstExprConstantString::Quoted)
return;
else if (node->is<AstExprInterpString>())
return;
}
if (auto str = node->as<AstExprConstantString>(); str && str->quoteStyle == AstExprConstantString::Quoted)
return;
else if (node->is<AstExprInterpString>())
return;
}

auto formatKey = [addQuotes](const std::string& key) {
Expand Down
14 changes: 9 additions & 5 deletions Analysis/src/ConstraintGenerator.cpp
Expand Up @@ -738,14 +738,18 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatLocal* stat
scope->lvalueTypes[def] = assignee;
}

TypePackId resultPack = checkPack(scope, statLocal->values, expectedTypes).tp;
addConstraint(scope, statLocal->location, UnpackConstraint{arena->addTypePack(std::move(assignees)), resultPack, /*resultIsLValue*/ true});
TypePackId rvaluePack = checkPack(scope, statLocal->values, expectedTypes).tp;

// Types must flow between whatever annotations were provided and the rhs expression.
if (hasAnnotation)
addConstraint(scope, statLocal->location, PackSubtypeConstraint{resultPack, arena->addTypePack(std::move(annotatedTypes))});
{
TypePackId annotatedPack = arena->addTypePack(std::move(annotatedTypes));
addConstraint(scope, statLocal->location, UnpackConstraint{arena->addTypePack(std::move(assignees)), annotatedPack, /*resultIsLValue*/ true});
addConstraint(scope, statLocal->location, PackSubtypeConstraint{rvaluePack, annotatedPack});
}
else
addConstraint(scope, statLocal->location, UnpackConstraint{arena->addTypePack(std::move(assignees)), rvaluePack, /*resultIsLValue*/ true});

if (statLocal->vars.size == 1 && statLocal->values.size == 1 && firstValueType && scope.get() == rootScope)
if (statLocal->vars.size == 1 && statLocal->values.size == 1 && firstValueType && scope.get() == rootScope && !hasAnnotation)
{
AstLocal* var = statLocal->vars.data[0];
AstExpr* value = statLocal->values.data[0];
Expand Down
10 changes: 4 additions & 6 deletions Analysis/src/ConstraintSolver.cpp
Expand Up @@ -27,7 +27,7 @@
#include <utility>

LUAU_FASTFLAGVARIABLE(DebugLuauLogSolver, false);

LUAU_FASTFLAGVARIABLE(DebugLuauLogBindings, false);
LUAU_FASTINTVARIABLE(LuauSolverRecursionLimit, 500);

namespace Luau
Expand Down Expand Up @@ -465,10 +465,8 @@ void ConstraintSolver::run()
reduceFamilies(instance, Location{}, TypeFamilyContext{arena, builtinTypes, rootScope, normalizer, NotNull{&iceReporter}, NotNull{&limits}}, false);
}

if (FFlag::DebugLuauLogSolver)
{
if (FFlag::DebugLuauLogSolver || FFlag::DebugLuauLogBindings)
dumpBindings(rootScope, opts);
}

if (logger)
{
Expand Down Expand Up @@ -1761,7 +1759,7 @@ bool ConstraintSolver::tryDispatchUnpack1(NotNull<const Constraint> constraint,
else
{
LUAU_ASSERT(resultIsLValue);
unify(constraint, resultTy, srcTy);
unify(constraint, srcTy, resultTy);
}

unblock(resultTy, constraint->location);
Expand Down Expand Up @@ -1812,7 +1810,7 @@ bool ConstraintSolver::tryDispatch(const UnpackConstraint& c, NotNull<const Cons
tryDispatchUnpack1(constraint, resultTy, srcTy, c.resultIsLValue);
}
else
unify(constraint, resultTy, srcTy);
unify(constraint, srcTy, resultTy);

++resultIter;
++i;
Expand Down
4 changes: 2 additions & 2 deletions Analysis/src/EmbeddedBuiltinDefinitions.cpp
Expand Up @@ -314,9 +314,9 @@ type DateTypeResult = {
}
declare os: {
time: @checked (time: DateTypeArg?) -> number,
time: (time: DateTypeArg?) -> number,
date: ((formatString: "*t" | "!*t", time: number?) -> DateTypeResult) & ((formatString: string?, time: number?) -> string),
difftime: @checked (t2: DateTypeResult | number, t1: DateTypeResult | number) -> number,
difftime: (t2: DateTypeResult | number, t1: DateTypeResult | number) -> number,
clock: () -> number,
}
Expand Down
10 changes: 9 additions & 1 deletion Analysis/src/Frontend.cpp
Expand Up @@ -38,6 +38,8 @@ LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson, false)
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJsonFile, false)
LUAU_FASTFLAGVARIABLE(DebugLuauForbidInternalTypes, false)
LUAU_FASTFLAGVARIABLE(DebugLuauForceStrictMode, false)
LUAU_FASTFLAGVARIABLE(DebugLuauForceNonStrictMode, false)

namespace Luau
{
Expand Down Expand Up @@ -891,7 +893,13 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item)
SourceNode& sourceNode = *item.sourceNode;
const SourceModule& sourceModule = *item.sourceModule;
const Config& config = item.config;
Mode mode = sourceModule.mode.value_or(config.mode);
Mode mode;
if (FFlag::DebugLuauForceStrictMode)
mode = Mode::Strict;
else if (FFlag::DebugLuauForceNonStrictMode)
mode = Mode::Nonstrict;
else
mode = sourceModule.mode.value_or(config.mode);
ScopePtr environmentScope = item.environmentScope;
double timestamp = getTimestamp();
const std::vector<RequireCycle>& requireCycles = item.requireCycles;
Expand Down

0 comments on commit 67e16cb

Please sign in to comment.