Skip to content

Commit

Permalink
Sync to upstream/release/583 (#974)
Browse files Browse the repository at this point in the history
* Fixed indexing table intersections using `x["prop"]` syntax:
#971
* Add console output codepage for Windows:
#967
* Added `Frontend::parse` for a fast source graph preparation
* luau_load should check GC
* Work toward a type-diff system for nicer error messages

New Solver
* Correctly suppress errors in more cases
* Further improvements to typechecking of function calls and return
statements
* Crash fixes
* Propagate refinements drawn from the condition of a while loop into
the loop body

JIT
* Fix accidental bailout for math.frexp/modf/sign in A64
* Work toward bringing type annotation info in
* Do not propagate Luau IR constants of wrong type into load
instructions
* CHECK_SAFEENV exits to VM on failure
* Implement error handling in A64 reg allocator
* Inline the string.len builtin
* Do not enter native code of a function if arguments don’t match

---------

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
  • Loading branch information
3 people authored Jul 7, 2023
1 parent c98a9d7 commit e25de95
Show file tree
Hide file tree
Showing 68 changed files with 1,893 additions and 293 deletions.
10 changes: 7 additions & 3 deletions Analysis/include/Luau/Constraint.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ struct PackSubtypeConstraint
{
TypePackId subPack;
TypePackId superPack;

// HACK!! TODO clip.
// We need to know which of `PackSubtypeConstraint` are emitted from `AstStatReturn` vs any others.
// Then we force these specific `PackSubtypeConstraint` to only dispatch in the order of the `return`s.
bool returns = false;
};

// generalizedType ~ gen sourceType
Expand Down Expand Up @@ -108,13 +113,12 @@ struct FunctionCallConstraint
TypeId fn;
TypePackId argsPack;
TypePackId result;
class AstExprCall* callSite;
class AstExprCall* callSite = nullptr;
std::vector<std::optional<TypeId>> discriminantTypes;

// When we dispatch this constraint, we update the key at this map to record
// the overload that we selected.
DenseHashMap<const AstNode*, TypeId>* astOriginalCallTypes;
DenseHashMap<const AstNode*, TypeId>* astOverloadResolvedTypes;
DenseHashMap<const AstNode*, TypeId>* astOverloadResolvedTypes = nullptr;
};

// result ~ prim ExpectedType SomeSingletonType MultitonType
Expand Down
138 changes: 138 additions & 0 deletions Analysis/include/Luau/Differ.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once

#include "Luau/Type.h"
#include <optional>
#include <string>

namespace Luau
{
struct DiffPathNode
{
// TODO: consider using Variants to simplify toString implementation
enum Kind
{
TableProperty,
FunctionArgument,
FunctionReturn,
Union,
Intersection,
};
Kind kind;
// non-null when TableProperty
std::optional<Name> tableProperty;
// non-null when FunctionArgument, FunctionReturn, Union, or Intersection (i.e. anonymous fields)
std::optional<int> index;

/**
* Do not use for leaf nodes
*/
DiffPathNode(Kind kind)
: kind(kind)
{
}

DiffPathNode(Kind kind, std::optional<Name> tableProperty, std::optional<int> index)
: kind(kind)
, tableProperty(tableProperty)
, index(index)
{
}

std::string toString() const;

static DiffPathNode constructWithTableProperty(Name tableProperty);
};
struct DiffPathNodeLeaf
{
std::optional<TypeId> ty;
std::optional<Name> tableProperty;
DiffPathNodeLeaf(std::optional<TypeId> ty, std::optional<Name> tableProperty)
: ty(ty)
, tableProperty(tableProperty)
{
}

static DiffPathNodeLeaf nullopts();
};
struct DiffPath
{
std::vector<DiffPathNode> path;

std::string toString(bool prependDot) const;
};
struct DiffError
{
enum Kind
{
Normal,
MissingProperty,
LengthMismatchInFnArgs,
LengthMismatchInFnRets,
LengthMismatchInUnion,
LengthMismatchInIntersection,
};
Kind kind;

DiffPath diffPath;
DiffPathNodeLeaf left;
DiffPathNodeLeaf right;

std::string leftRootName;
std::string rightRootName;

DiffError(Kind kind, DiffPathNodeLeaf left, DiffPathNodeLeaf right, std::string leftRootName, std::string rightRootName)
: kind(kind)
, left(left)
, right(right)
, leftRootName(leftRootName)
, rightRootName(rightRootName)
{
checkValidInitialization(left, right);
}
DiffError(Kind kind, DiffPath diffPath, DiffPathNodeLeaf left, DiffPathNodeLeaf right, std::string leftRootName, std::string rightRootName)
: kind(kind)
, diffPath(diffPath)
, left(left)
, right(right)
, leftRootName(leftRootName)
, rightRootName(rightRootName)
{
checkValidInitialization(left, right);
}

std::string toString() const;

private:
std::string toStringALeaf(std::string rootName, const DiffPathNodeLeaf& leaf, const DiffPathNodeLeaf& otherLeaf) const;
void checkValidInitialization(const DiffPathNodeLeaf& left, const DiffPathNodeLeaf& right);
void checkNonMissingPropertyLeavesHaveNulloptTableProperty() const;
};

struct DifferResult
{
std::optional<DiffError> diffError;

DifferResult() {}
DifferResult(DiffError diffError)
: diffError(diffError)
{
}

void wrapDiffPath(DiffPathNode node);
};
struct DifferEnvironment
{
TypeId rootLeft;
TypeId rootRight;
};
DifferResult diff(TypeId ty1, TypeId ty2);

/**
* True if ty is a "simple" type, i.e. cannot contain types.
* string, number, boolean are simple types.
* function and table are not simple types.
*/
bool isSimple(TypeId ty);

} // namespace Luau
4 changes: 4 additions & 0 deletions Analysis/include/Luau/Frontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ struct Frontend

Frontend(FileResolver* fileResolver, ConfigResolver* configResolver, const FrontendOptions& options = {});

// Parse module graph and prepare SourceNode/SourceModule data, including required dependencies without running typechecking
void parse(const ModuleName& name);

// Parse and typecheck module graph
CheckResult check(const ModuleName& name, std::optional<FrontendOptions> optionOverride = {}); // new shininess

bool isDirty(const ModuleName& name, bool forAutocomplete = false) const;
Expand Down
9 changes: 6 additions & 3 deletions Analysis/include/Luau/Normalize.h
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,9 @@ struct NormalizedType
/// Returns true if the type is a subtype of string(it could be a singleton). Behaves like Type::isString()
bool isSubtypeOfString() const;

/// Returns true if this type should result in error suppressing behavior.
bool shouldSuppressErrors() const;

// Helpers that improve readability of the above (they just say if the component is present)
bool hasTops() const;
bool hasBooleans() const;
Expand Down Expand Up @@ -343,7 +346,7 @@ class Normalizer
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, int ignoreSmallerTyvars = -1);
bool unionNormalWithTy(NormalizedType& here, TypeId there, std::unordered_set<TypeId>& seenSetTypes, int ignoreSmallerTyvars = -1);

// ------- Negations
std::optional<NormalizedType> negateNormal(const NormalizedType& here);
Expand All @@ -365,9 +368,9 @@ 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);
bool intersectTyvarsWithTy(NormalizedTyvars& here, TypeId there, std::unordered_set<TypeId>& seenSetTypes);
bool intersectNormals(NormalizedType& here, const NormalizedType& there, int ignoreSmallerTyvars = -1);
bool intersectNormalWithTy(NormalizedType& here, TypeId there);
bool intersectNormalWithTy(NormalizedType& here, TypeId there, std::unordered_set<TypeId>& seenSetTypes);
bool normalizeIntersections(const std::vector<TypeId>& intersections, NormalizedType& outType);

// Check for inhabitance
Expand Down
2 changes: 0 additions & 2 deletions Analysis/include/Luau/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@

LUAU_FASTINT(LuauTableTypeMaximumStringifierLength)
LUAU_FASTINT(LuauTypeMaximumStringifierLength)
LUAU_FASTFLAG(LuauTypecheckClassTypeIndexers)

namespace Luau
{
Expand Down Expand Up @@ -527,7 +526,6 @@ struct ClassType
, definitionModuleName(definitionModuleName)
, indexer(indexer)
{
LUAU_ASSERT(FFlag::LuauTypecheckClassTypeIndexers);
}
};

Expand Down
9 changes: 3 additions & 6 deletions Analysis/include/Luau/VisitType.h
Original file line number Diff line number Diff line change
Expand Up @@ -304,13 +304,10 @@ struct GenericTypeVisitor
if (ctv->metatable)
traverse(*ctv->metatable);

if (FFlag::LuauTypecheckClassTypeIndexers)
if (ctv->indexer)
{
if (ctv->indexer)
{
traverse(ctv->indexer->indexType);
traverse(ctv->indexer->indexResultType);
}
traverse(ctv->indexer->indexType);
traverse(ctv->indexer->indexResultType);
}
}
}
Expand Down
20 changes: 4 additions & 16 deletions Analysis/src/Clone.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ Property clone(const Property& prop, TypeArena& dest, CloneState& cloneState)

static TableIndexer clone(const TableIndexer& indexer, TypeArena& dest, CloneState& cloneState)
{
LUAU_ASSERT(FFlag::LuauTypecheckClassTypeIndexers);
return TableIndexer{clone(indexer.indexType, dest, cloneState), clone(indexer.indexResultType, dest, cloneState)};
}

Expand Down Expand Up @@ -312,16 +311,8 @@ void TypeCloner::operator()(const TableType& t)
for (const auto& [name, prop] : t.props)
ttv->props[name] = clone(prop, dest, cloneState);

if (FFlag::LuauTypecheckClassTypeIndexers)
{
if (t.indexer)
ttv->indexer = clone(*t.indexer, dest, cloneState);
}
else
{
if (t.indexer)
ttv->indexer = TableIndexer{clone(t.indexer->indexType, dest, cloneState), clone(t.indexer->indexResultType, dest, cloneState)};
}
if (t.indexer)
ttv->indexer = clone(*t.indexer, dest, cloneState);

for (TypeId& arg : ttv->instantiatedTypeParams)
arg = clone(arg, dest, cloneState);
Expand Down Expand Up @@ -360,11 +351,8 @@ void TypeCloner::operator()(const ClassType& t)
if (t.metatable)
ctv->metatable = clone(*t.metatable, dest, cloneState);

if (FFlag::LuauTypecheckClassTypeIndexers)
{
if (t.indexer)
ctv->indexer = clone(*t.indexer, dest, cloneState);
}
if (t.indexer)
ctv->indexer = clone(*t.indexer, dest, cloneState);
}

void TypeCloner::operator()(const AnyType& t)
Expand Down
44 changes: 36 additions & 8 deletions Analysis/src/ConstraintGraphBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -776,9 +776,10 @@ ControlFlow ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatForIn* f

ControlFlow ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatWhile* while_)
{
check(scope, while_->condition);
RefinementId refinement = check(scope, while_->condition).refinement;

ScopePtr whileScope = childScope(while_, scope);
applyRefinements(whileScope, while_->condition->location, refinement);

visit(whileScope, while_->body);

Expand Down Expand Up @@ -825,8 +826,17 @@ ControlFlow ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocalFun
std::unique_ptr<Constraint> c =
std::make_unique<Constraint>(constraintScope, function->name->location, GeneralizationConstraint{functionType, sig.signature});

forEachConstraint(start, end, this, [&c](const ConstraintPtr& constraint) {
Constraint* previous = nullptr;
forEachConstraint(start, end, this, [&c, &previous](const ConstraintPtr& constraint) {
c->dependencies.push_back(NotNull{constraint.get()});

if (auto psc = get<PackSubtypeConstraint>(*constraint); psc && psc->returns)
{
if (previous)
constraint->dependencies.push_back(NotNull{previous});

previous = constraint.get();
}
});

addConstraint(scope, std::move(c));
Expand Down Expand Up @@ -915,9 +925,18 @@ ControlFlow ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatFunction
std::unique_ptr<Constraint> c =
std::make_unique<Constraint>(constraintScope, function->name->location, GeneralizationConstraint{generalizedType, sig.signature});

forEachConstraint(start, end, this, [&c, &excludeList](const ConstraintPtr& constraint) {
Constraint* previous = nullptr;
forEachConstraint(start, end, this, [&c, &excludeList, &previous](const ConstraintPtr& constraint) {
if (!excludeList.count(constraint.get()))
c->dependencies.push_back(NotNull{constraint.get()});

if (auto psc = get<PackSubtypeConstraint>(*constraint); psc && psc->returns)
{
if (previous)
constraint->dependencies.push_back(NotNull{previous});

previous = constraint.get();
}
});

addConstraint(scope, std::move(c));
Expand All @@ -936,7 +955,7 @@ ControlFlow ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatReturn*
expectedTypes.push_back(ty);

TypePackId exprTypes = checkPack(scope, ret->list, expectedTypes).tp;
addConstraint(scope, ret->location, PackSubtypeConstraint{exprTypes, scope->returnType});
addConstraint(scope, ret->location, PackSubtypeConstraint{exprTypes, scope->returnType, /*returns*/ true});

return ControlFlow::Returns;
}
Expand Down Expand Up @@ -1408,6 +1427,7 @@ InferencePack ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstExprCa
std::vector<std::optional<TypeId>> expectedTypesForCall = getExpectedCallTypesForFunctionOverloads(fnType);

module->astOriginalCallTypes[call->func] = fnType;
module->astOriginalCallTypes[call] = fnType;

TypePackId expectedArgPack = arena->freshTypePack(scope.get());
TypePackId expectedRetPack = arena->freshTypePack(scope.get());
Expand Down Expand Up @@ -1547,7 +1567,6 @@ InferencePack ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstExprCa
rets,
call,
std::move(discriminantTypes),
&module->astOriginalCallTypes,
&module->astOverloadResolvedTypes,
});

Expand Down Expand Up @@ -1642,7 +1661,7 @@ Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprConstantSt
if (expectedType)
{
const TypeId expectedTy = follow(*expectedType);
if (get<BlockedType>(expectedTy) || get<PendingExpansionType>(expectedTy))
if (get<BlockedType>(expectedTy) || get<PendingExpansionType>(expectedTy) || get<FreeType>(expectedTy))
{
TypeId ty = arena->addType(BlockedType{});
TypeId singletonType = arena->addType(SingletonType(StringSingleton{std::string(string->value.data, string->value.size)}));
Expand Down Expand Up @@ -1774,8 +1793,17 @@ Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprFunction*
TypeId generalizedTy = arena->addType(BlockedType{});
NotNull<Constraint> gc = addConstraint(sig.signatureScope, func->location, GeneralizationConstraint{generalizedTy, sig.signature});

forEachConstraint(startCheckpoint, endCheckpoint, this, [gc](const ConstraintPtr& constraint) {
Constraint* previous = nullptr;
forEachConstraint(startCheckpoint, endCheckpoint, this, [gc, &previous](const ConstraintPtr& constraint) {
gc->dependencies.emplace_back(constraint.get());

if (auto psc = get<PackSubtypeConstraint>(*constraint); psc && psc->returns)
{
if (previous)
constraint->dependencies.push_back(NotNull{previous});

previous = constraint.get();
}
});

return Inference{generalizedTy};
Expand Down Expand Up @@ -2412,7 +2440,7 @@ void ConstraintGraphBuilder::checkFunctionBody(const ScopePtr& scope, AstExprFun

if (nullptr != getFallthrough(fn->body))
{
TypePackId empty = arena->addTypePack({}); // TODO we could have CSG retain one of these forever
TypePackId empty = arena->addTypePack({}); // TODO we could have CGB retain one of these forever
addConstraint(scope, fn->location, PackSubtypeConstraint{scope->returnType, empty});
}
}
Expand Down
Loading

0 comments on commit e25de95

Please sign in to comment.