Skip to content

Commit

Permalink
Sync to upstream/release/630 (#1295)
Browse files Browse the repository at this point in the history
### What's new

* A bug in exception handling in GCC(11/12/13) on MacOS prevents our
test suite from running.
* Parser now supports leading `|` or `&` when declaring `Union` and
`Intersection` types (#1286)
* We now support parsing of attributes on functions as described in the
[rfc](luau-lang/rfcs#30)
* With this change, expressions such as `local x = @Native function(x)
    return x+1 end` and `f(@Native function(x) return x+1 end)` are now
    valid.
* Added support for `@native` attribute - we can now force native
compilation of individual functions if the `@native` attribute is
specified before the `function` keyword (works for lambdas too).

### New Solver

* Many fixes in the new solver for crashes and instability
* Refinements now use simplification and not normalization in a specific
case of two tables
* Assume that compound assignments do not change the type of the
left-side operand
* Fix error that prevented Class Methods from being overloaded

### VM

* Updated description of Garbage Collector invariant

---
### Internal Contributors

Co-authored-by: Aaron Weiss <[email protected]>
Co-authored-by: Alexander McCord <[email protected]>
Co-authored-by: Andy Friesen <[email protected]>
Co-authored-by: Aviral Goel <[email protected]>
Co-authored-by: Vighnesh Vijay <[email protected]>
Co-authored-by: Vyacheslav Egorov <[email protected]>

---------

Co-authored-by: Aaron Weiss <[email protected]>
Co-authored-by: Alexander McCord <[email protected]>
Co-authored-by: Andy Friesen <[email protected]>
Co-authored-by: Aviral Goel <[email protected]>
Co-authored-by: David Cope <[email protected]>
Co-authored-by: Lily Brown <[email protected]>
Co-authored-by: Vyacheslav Egorov <[email protected]>
  • Loading branch information
8 people authored Jun 14, 2024
1 parent 0fa6a51 commit 7d40330
Show file tree
Hide file tree
Showing 50 changed files with 1,699 additions and 445 deletions.
2 changes: 1 addition & 1 deletion Analysis/include/Luau/ConstraintGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ struct ConstraintGenerator
std::function<void(const ModuleName&, const ScopePtr&)> prepareModuleScope;
std::vector<RequireCycle> requireCycles;

DenseHashMap<TypeId, std::vector<TypeId>> localTypes{nullptr};
DenseHashMap<TypeId, TypeIds> localTypes{nullptr};

DcrLogger* logger;

Expand Down
56 changes: 34 additions & 22 deletions Analysis/include/Luau/ConstraintSolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ struct ConstraintSolver
// Irreducible/uninhabited type families or type pack families.
DenseHashSet<const void*> uninhabitedTypeFamilies{{}};

// The set of types that will definitely be unchanged by generalization.
DenseHashSet<TypeId> generalizedTypes_{nullptr};
const NotNull<DenseHashSet<TypeId>> generalizedTypes{&generalizedTypes_};

// Recorded errors that take place within the solver.
ErrorVec errors;

Expand All @@ -103,6 +107,8 @@ struct ConstraintSolver
DcrLogger* logger;
TypeCheckLimits limits;

DenseHashMap<TypeId, const Constraint*> typeFamiliesToFinalize{nullptr};

explicit ConstraintSolver(NotNull<Normalizer> normalizer, NotNull<Scope> rootScope, std::vector<NotNull<Constraint>> constraints,
ModuleName moduleName, NotNull<ModuleResolver> moduleResolver, std::vector<RequireCycle> requireCycles, DcrLogger* logger,
TypeCheckLimits limits);
Expand All @@ -116,8 +122,35 @@ struct ConstraintSolver
**/
void run();


/**
* Attempts to perform one final reduction on type families after every constraint has been completed
*
**/
void finalizeTypeFamilies();

bool isDone();

private:
/**
* Bind a type variable to another type.
*
* A constraint is required and will validate that blockedTy is owned by this
* constraint. This prevents one constraint from interfering with another's
* blocked types.
*
* Bind will also unblock the type variable for you.
*/
void bind(NotNull<const Constraint> constraint, TypeId ty, TypeId boundTo);
void bind(NotNull<const Constraint> constraint, TypePackId tp, TypePackId boundTo);

template<typename T, typename... Args>
void emplace(NotNull<const Constraint> constraint, TypeId ty, Args&&... args);

template<typename T, typename... Args>
void emplace(NotNull<const Constraint> constraint, TypePackId tp, Args&&... args);

public:
/** Attempt to dispatch a constraint. Returns true if it was successful. If
* tryDispatch() returns false, the constraint remains in the unsolved set
* and will be retried later.
Expand All @@ -135,19 +168,14 @@ struct ConstraintSolver
bool tryDispatch(const PrimitiveTypeConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const HasPropConstraint& c, NotNull<const Constraint> constraint);


bool tryDispatchHasIndexer(
int& recursionDepth, NotNull<const Constraint> constraint, TypeId subjectType, TypeId indexType, TypeId resultType, Set<TypeId>& seen);
bool tryDispatch(const HasIndexerConstraint& c, NotNull<const Constraint> constraint);

std::pair<bool, std::optional<TypeId>> tryDispatchSetIndexer(
NotNull<const Constraint> constraint, TypeId subjectType, TypeId indexType, TypeId propType, bool expandFreeTypeBounds);

bool tryDispatch(const AssignPropConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const AssignIndexConstraint& c, NotNull<const Constraint> constraint);

bool tryDispatchUnpack1(NotNull<const Constraint> constraint, TypeId resultType, TypeId sourceType);
bool tryDispatch(const UnpackConstraint& c, NotNull<const Constraint> constraint);

bool tryDispatch(const ReduceConstraint& c, NotNull<const Constraint> constraint, bool force);
bool tryDispatch(const ReducePackConstraint& c, NotNull<const Constraint> constraint, bool force);
bool tryDispatch(const EqualityConstraint& c, NotNull<const Constraint> constraint, bool force);
Expand Down Expand Up @@ -298,22 +326,6 @@ struct ConstraintSolver
template<typename TID>
bool unify(NotNull<const Constraint> constraint, TID subTy, TID superTy);

private:
/**
* Bind a BlockedType to another type while taking care not to bind it to
* itself in the case that resultTy == blockedTy. This can happen if we
* have a tautological constraint. When it does, we must instead bind
* blockedTy to a fresh type belonging to an appropriate scope.
*
* To determine which scope is appropriate, we also accept rootTy, which is
* to be the type that contains blockedTy.
*
* A constraint is required and will validate that blockedTy is owned by this
* constraint. This prevents one constraint from interfering with another's
* blocked types.
*/
void bindBlockedType(TypeId blockedTy, TypeId resultTy, TypeId rootTy, NotNull<const Constraint> constraint);

/**
* Marks a constraint as being blocked on a type or type pack. The constraint
* solver will not attempt to dispatch blocked constraints until their
Expand Down
2 changes: 1 addition & 1 deletion Analysis/include/Luau/Generalization.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
namespace Luau
{

std::optional<TypeId> generalize(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, NotNull<Scope> scope, TypeId ty);
std::optional<TypeId> generalize(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, NotNull<Scope> scope, NotNull<DenseHashSet<TypeId>> bakedTypes, TypeId ty);

}
6 changes: 6 additions & 0 deletions Analysis/include/Luau/Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ struct Module
DenseHashMap<const AstType*, TypeId> astResolvedTypes{nullptr};
DenseHashMap<const AstTypePack*, TypePackId> astResolvedTypePacks{nullptr};

// The computed result type of a compound assignment. (eg foo += 1)
//
// Type checking uses this to check that the result of such an operation is
// actually compatible with the left-side operand.
DenseHashMap<const AstStat*, TypeId> astCompoundAssignResultTypes{nullptr};

DenseHashMap<TypeId, std::vector<std::pair<Location, TypeId>>> upperBoundContributors{nullptr};

// Map AST nodes to the scope they create. Cannot be NotNull<Scope> because
Expand Down
2 changes: 1 addition & 1 deletion Analysis/include/Luau/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -658,7 +658,7 @@ struct NegationType
using ErrorType = Unifiable::Error;

using TypeVariant =
Unifiable::Variant<TypeId, FreeType, GenericType, PrimitiveType, BlockedType, PendingExpansionType, SingletonType, FunctionType, TableType,
Unifiable::Variant<TypeId, FreeType, GenericType, PrimitiveType, SingletonType, BlockedType, PendingExpansionType, FunctionType, TableType,
MetatableType, ClassType, AnyType, UnionType, IntersectionType, LazyType, UnknownType, NeverType, NegationType, TypeFamilyInstanceType>;

struct Type final
Expand Down
2 changes: 2 additions & 0 deletions Analysis/include/Luau/TypeFamily.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,8 @@ struct BuiltinTypeFamilies
TypeFamily keyofFamily;
TypeFamily rawkeyofFamily;

TypeFamily indexFamily;

void addToScope(NotNull<TypeArena> arena, NotNull<Scope> scope) const;
};

Expand Down
4 changes: 4 additions & 0 deletions Analysis/src/Constraint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ DenseHashSet<TypeId> Constraint::getMaybeMutatedFreeTypes() const
{
rci.traverse(taec->target);
}
else if (auto fchc = get<FunctionCheckConstraint>(*this))
{
rci.traverse(fchc->argsPack);
}
else if (auto ptc = get<PrimitiveTypeConstraint>(*this))
{
rci.traverse(ptc->freeType);
Expand Down
74 changes: 40 additions & 34 deletions Analysis/src/ConstraintGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,11 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
// FIXME: This isn't the most efficient thing.
TypeId domainTy = builtinTypes->neverType;
for (TypeId d : domain)
{
if (d == ty)
continue;
domainTy = simplifyUnion(builtinTypes, arena, domainTy, d).result;
}

LUAU_ASSERT(get<BlockedType>(ty));
asMutable(ty)->ty.emplace<BoundType>(domainTy);
Expand Down Expand Up @@ -323,7 +327,7 @@ std::optional<TypeId> ConstraintGenerator::lookup(const ScopePtr& scope, Locatio
if (!ty)
{
ty = arena->addType(BlockedType{});
localTypes[*ty] = {};
localTypes.try_insert(*ty, {});
rootScope->lvalueTypes[operand] = *ty;
}

Expand Down Expand Up @@ -717,7 +721,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatLocal* stat
const Location location = local->location;

TypeId assignee = arena->addType(BlockedType{});
localTypes[assignee] = {};
localTypes.try_insert(assignee, {});

assignees.push_back(assignee);

Expand Down Expand Up @@ -756,9 +760,9 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatLocal* stat
for (size_t i = 0; i < statLocal->vars.size; ++i)
{
LUAU_ASSERT(get<BlockedType>(assignees[i]));
std::vector<TypeId>* localDomain = localTypes.find(assignees[i]);
TypeIds* localDomain = localTypes.find(assignees[i]);
LUAU_ASSERT(localDomain);
localDomain->push_back(annotatedTypes[i]);
localDomain->insert(annotatedTypes[i]);
}

TypePackId annotatedPack = arena->addTypePack(std::move(annotatedTypes));
Expand Down Expand Up @@ -790,9 +794,9 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatLocal* stat
for (size_t i = 0; i < statLocal->vars.size; ++i)
{
LUAU_ASSERT(get<BlockedType>(assignees[i]));
std::vector<TypeId>* localDomain = localTypes.find(assignees[i]);
TypeIds* localDomain = localTypes.find(assignees[i]);
LUAU_ASSERT(localDomain);
localDomain->push_back(valueTypes[i]);
localDomain->insert(valueTypes[i]);
}
}

Expand Down Expand Up @@ -898,7 +902,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatForIn* forI
variableTypes.push_back(assignee);

TypeId loopVar = arena->addType(BlockedType{});
localTypes[loopVar].push_back(assignee);
localTypes[loopVar].insert(assignee);

if (var->annotation)
{
Expand Down Expand Up @@ -1183,8 +1187,13 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatCompoundAss
{
AstExprBinary binop = AstExprBinary{assign->location, assign->op, assign->var, assign->value};
TypeId resultTy = check(scope, &binop).ty;
module->astCompoundAssignResultTypes[assign] = resultTy;

TypeId lhsType = check(scope, assign->var).ty;
visitLValue(scope, assign->var, lhsType);

visitLValue(scope, assign->var, resultTy);
follow(lhsType);
follow(resultTy);

return ControlFlow::None;
}
Expand Down Expand Up @@ -1383,16 +1392,15 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClas
}
}

if (ctv->props.count(propName) == 0)
TableType::Props& props = assignToMetatable ? metatable->props : ctv->props;

if (props.count(propName) == 0)
{
if (assignToMetatable)
metatable->props[propName] = {propTy};
else
ctv->props[propName] = {propTy};
props[propName] = {propTy};
}
else
{
TypeId currentTy = assignToMetatable ? metatable->props[propName].type() : ctv->props[propName].type();
TypeId currentTy = props[propName].type();

// We special-case this logic to keep the intersection flat; otherwise we
// would create a ton of nested intersection types.
Expand All @@ -1402,19 +1410,13 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClas
options.push_back(propTy);
TypeId newItv = arena->addType(IntersectionType{std::move(options)});

if (assignToMetatable)
metatable->props[propName] = {newItv};
else
ctv->props[propName] = {newItv};
props[propName] = {newItv};
}
else if (get<FunctionType>(currentTy))
{
TypeId intersection = arena->addType(IntersectionType{{currentTy, propTy}});

if (assignToMetatable)
metatable->props[propName] = {intersection};
else
ctv->props[propName] = {intersection};
props[propName] = {intersection};
}
else
{
Expand Down Expand Up @@ -1913,8 +1915,8 @@ Inference ConstraintGenerator::checkIndexName(
// the current lexical position within the script.
if (!tt)
{
if (auto localDomain = localTypes.find(obj); localDomain && 1 == localDomain->size())
tt = getTableType(localDomain->front());
if (TypeIds* localDomain = localTypes.find(obj); localDomain && 1 == localDomain->size())
tt = getTableType(*localDomain->begin());
}

if (tt)
Expand Down Expand Up @@ -2327,14 +2329,14 @@ void ConstraintGenerator::visitLValue(const ScopePtr& scope, AstExprLocal* local

if (ty)
{
std::vector<TypeId>* localDomain = localTypes.find(*ty);
TypeIds* localDomain = localTypes.find(*ty);
if (localDomain)
localDomain->push_back(rhsType);
localDomain->insert(rhsType);
}
else
{
ty = arena->addType(BlockedType{});
localTypes[*ty].push_back(rhsType);
localTypes[*ty].insert(rhsType);

if (annotatedTy)
{
Expand All @@ -2359,8 +2361,8 @@ void ConstraintGenerator::visitLValue(const ScopePtr& scope, AstExprLocal* local
if (annotatedTy)
addConstraint(scope, local->location, SubtypeConstraint{rhsType, *annotatedTy});

if (auto localDomain = localTypes.find(*ty))
localDomain->push_back(rhsType);
if (TypeIds* localDomain = localTypes.find(*ty))
localDomain->insert(rhsType);
}

void ConstraintGenerator::visitLValue(const ScopePtr& scope, AstExprGlobal* global, TypeId rhsType)
Expand All @@ -2383,7 +2385,8 @@ void ConstraintGenerator::visitLValue(const ScopePtr& scope, AstExprIndexName* e

bool incremented = recordPropertyAssignment(lhsTy);

addConstraint(scope, expr->location, AssignPropConstraint{lhsTy, expr->index.value, rhsType, propTy, incremented});
auto apc = addConstraint(scope, expr->location, AssignPropConstraint{lhsTy, expr->index.value, rhsType, propTy, incremented});
getMutable<BlockedType>(propTy)->setOwner(apc);
}

void ConstraintGenerator::visitLValue(const ScopePtr& scope, AstExprIndexExpr* expr, TypeId rhsType)
Expand All @@ -2398,7 +2401,8 @@ void ConstraintGenerator::visitLValue(const ScopePtr& scope, AstExprIndexExpr* e

bool incremented = recordPropertyAssignment(lhsTy);

addConstraint(scope, expr->location, AssignPropConstraint{lhsTy, std::move(propName), rhsType, propTy, incremented});
auto apc = addConstraint(scope, expr->location, AssignPropConstraint{lhsTy, std::move(propName), rhsType, propTy, incremented});
getMutable<BlockedType>(propTy)->setOwner(apc);

return;
}
Expand All @@ -2407,7 +2411,8 @@ void ConstraintGenerator::visitLValue(const ScopePtr& scope, AstExprIndexExpr* e
TypeId indexTy = check(scope, expr->index).ty;
TypeId propTy = arena->addType(BlockedType{});
module->astTypes[expr] = propTy;
addConstraint(scope, expr->location, AssignIndexConstraint{lhsTy, indexTy, rhsType, propTy});
auto aic = addConstraint(scope, expr->location, AssignIndexConstraint{lhsTy, indexTy, rhsType, propTy});
getMutable<BlockedType>(propTy)->setOwner(aic);
}

Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr, std::optional<TypeId> expectedType)
Expand Down Expand Up @@ -2447,7 +2452,8 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr,

if (AstExprConstantString* key = item.key->as<AstExprConstantString>())
{
ttv->props[key->value.begin()] = {itemTy};
std::string propName{key->value.data, key->value.size};
ttv->props[propName] = {itemTy};
}
else
{
Expand Down Expand Up @@ -3187,7 +3193,7 @@ bool ConstraintGenerator::recordPropertyAssignment(TypeId ty)
}
else if (auto mt = get<MetatableType>(t))
queue.push_back(mt->table);
else if (auto localDomain = localTypes.find(t))
else if (TypeIds* localDomain = localTypes.find(t))
{
for (TypeId domainTy : *localDomain)
queue.push_back(domainTy);
Expand Down
Loading

0 comments on commit 7d40330

Please sign in to comment.