Skip to content

Commit 7d40330

Browse files
Vighnesh-Vaatxealexmccordandyfriesenaviralg
authored
Sync to upstream/release/630 (#1295)
### 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 <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: 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>
1 parent 0fa6a51 commit 7d40330

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1699
-445
lines changed

Analysis/include/Luau/ConstraintGenerator.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ struct ConstraintGenerator
118118
std::function<void(const ModuleName&, const ScopePtr&)> prepareModuleScope;
119119
std::vector<RequireCycle> requireCycles;
120120

121-
DenseHashMap<TypeId, std::vector<TypeId>> localTypes{nullptr};
121+
DenseHashMap<TypeId, TypeIds> localTypes{nullptr};
122122

123123
DcrLogger* logger;
124124

Analysis/include/Luau/ConstraintSolver.h

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ struct ConstraintSolver
9494
// Irreducible/uninhabited type families or type pack families.
9595
DenseHashSet<const void*> uninhabitedTypeFamilies{{}};
9696

97+
// The set of types that will definitely be unchanged by generalization.
98+
DenseHashSet<TypeId> generalizedTypes_{nullptr};
99+
const NotNull<DenseHashSet<TypeId>> generalizedTypes{&generalizedTypes_};
100+
97101
// Recorded errors that take place within the solver.
98102
ErrorVec errors;
99103

@@ -103,6 +107,8 @@ struct ConstraintSolver
103107
DcrLogger* logger;
104108
TypeCheckLimits limits;
105109

110+
DenseHashMap<TypeId, const Constraint*> typeFamiliesToFinalize{nullptr};
111+
106112
explicit ConstraintSolver(NotNull<Normalizer> normalizer, NotNull<Scope> rootScope, std::vector<NotNull<Constraint>> constraints,
107113
ModuleName moduleName, NotNull<ModuleResolver> moduleResolver, std::vector<RequireCycle> requireCycles, DcrLogger* logger,
108114
TypeCheckLimits limits);
@@ -116,8 +122,35 @@ struct ConstraintSolver
116122
**/
117123
void run();
118124

125+
126+
/**
127+
* Attempts to perform one final reduction on type families after every constraint has been completed
128+
*
129+
**/
130+
void finalizeTypeFamilies();
131+
119132
bool isDone();
120133

134+
private:
135+
/**
136+
* Bind a type variable to another type.
137+
*
138+
* A constraint is required and will validate that blockedTy is owned by this
139+
* constraint. This prevents one constraint from interfering with another's
140+
* blocked types.
141+
*
142+
* Bind will also unblock the type variable for you.
143+
*/
144+
void bind(NotNull<const Constraint> constraint, TypeId ty, TypeId boundTo);
145+
void bind(NotNull<const Constraint> constraint, TypePackId tp, TypePackId boundTo);
146+
147+
template<typename T, typename... Args>
148+
void emplace(NotNull<const Constraint> constraint, TypeId ty, Args&&... args);
149+
150+
template<typename T, typename... Args>
151+
void emplace(NotNull<const Constraint> constraint, TypePackId tp, Args&&... args);
152+
153+
public:
121154
/** Attempt to dispatch a constraint. Returns true if it was successful. If
122155
* tryDispatch() returns false, the constraint remains in the unsolved set
123156
* and will be retried later.
@@ -135,19 +168,14 @@ struct ConstraintSolver
135168
bool tryDispatch(const PrimitiveTypeConstraint& c, NotNull<const Constraint> constraint);
136169
bool tryDispatch(const HasPropConstraint& c, NotNull<const Constraint> constraint);
137170

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

142-
std::pair<bool, std::optional<TypeId>> tryDispatchSetIndexer(
143-
NotNull<const Constraint> constraint, TypeId subjectType, TypeId indexType, TypeId propType, bool expandFreeTypeBounds);
144-
145176
bool tryDispatch(const AssignPropConstraint& c, NotNull<const Constraint> constraint);
146177
bool tryDispatch(const AssignIndexConstraint& c, NotNull<const Constraint> constraint);
147-
148-
bool tryDispatchUnpack1(NotNull<const Constraint> constraint, TypeId resultType, TypeId sourceType);
149178
bool tryDispatch(const UnpackConstraint& c, NotNull<const Constraint> constraint);
150-
151179
bool tryDispatch(const ReduceConstraint& c, NotNull<const Constraint> constraint, bool force);
152180
bool tryDispatch(const ReducePackConstraint& c, NotNull<const Constraint> constraint, bool force);
153181
bool tryDispatch(const EqualityConstraint& c, NotNull<const Constraint> constraint, bool force);
@@ -298,22 +326,6 @@ struct ConstraintSolver
298326
template<typename TID>
299327
bool unify(NotNull<const Constraint> constraint, TID subTy, TID superTy);
300328

301-
private:
302-
/**
303-
* Bind a BlockedType to another type while taking care not to bind it to
304-
* itself in the case that resultTy == blockedTy. This can happen if we
305-
* have a tautological constraint. When it does, we must instead bind
306-
* blockedTy to a fresh type belonging to an appropriate scope.
307-
*
308-
* To determine which scope is appropriate, we also accept rootTy, which is
309-
* to be the type that contains blockedTy.
310-
*
311-
* A constraint is required and will validate that blockedTy is owned by this
312-
* constraint. This prevents one constraint from interfering with another's
313-
* blocked types.
314-
*/
315-
void bindBlockedType(TypeId blockedTy, TypeId resultTy, TypeId rootTy, NotNull<const Constraint> constraint);
316-
317329
/**
318330
* Marks a constraint as being blocked on a type or type pack. The constraint
319331
* solver will not attempt to dispatch blocked constraints until their

Analysis/include/Luau/Generalization.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@
88
namespace Luau
99
{
1010

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

1313
}

Analysis/include/Luau/Module.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,12 @@ struct Module
102102
DenseHashMap<const AstType*, TypeId> astResolvedTypes{nullptr};
103103
DenseHashMap<const AstTypePack*, TypePackId> astResolvedTypePacks{nullptr};
104104

105+
// The computed result type of a compound assignment. (eg foo += 1)
106+
//
107+
// Type checking uses this to check that the result of such an operation is
108+
// actually compatible with the left-side operand.
109+
DenseHashMap<const AstStat*, TypeId> astCompoundAssignResultTypes{nullptr};
110+
105111
DenseHashMap<TypeId, std::vector<std::pair<Location, TypeId>>> upperBoundContributors{nullptr};
106112

107113
// Map AST nodes to the scope they create. Cannot be NotNull<Scope> because

Analysis/include/Luau/Type.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,7 @@ struct NegationType
658658
using ErrorType = Unifiable::Error;
659659

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

664664
struct Type final

Analysis/include/Luau/TypeFamily.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,8 @@ struct BuiltinTypeFamilies
179179
TypeFamily keyofFamily;
180180
TypeFamily rawkeyofFamily;
181181

182+
TypeFamily indexFamily;
183+
182184
void addToScope(NotNull<TypeArena> arena, NotNull<Scope> scope) const;
183185
};
184186

Analysis/src/Constraint.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ DenseHashSet<TypeId> Constraint::getMaybeMutatedFreeTypes() const
9393
{
9494
rci.traverse(taec->target);
9595
}
96+
else if (auto fchc = get<FunctionCheckConstraint>(*this))
97+
{
98+
rci.traverse(fchc->argsPack);
99+
}
96100
else if (auto ptc = get<PrimitiveTypeConstraint>(*this))
97101
{
98102
rci.traverse(ptc->freeType);

Analysis/src/ConstraintGenerator.cpp

Lines changed: 40 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,11 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
253253
// FIXME: This isn't the most efficient thing.
254254
TypeId domainTy = builtinTypes->neverType;
255255
for (TypeId d : domain)
256+
{
257+
if (d == ty)
258+
continue;
256259
domainTy = simplifyUnion(builtinTypes, arena, domainTy, d).result;
260+
}
257261

258262
LUAU_ASSERT(get<BlockedType>(ty));
259263
asMutable(ty)->ty.emplace<BoundType>(domainTy);
@@ -323,7 +327,7 @@ std::optional<TypeId> ConstraintGenerator::lookup(const ScopePtr& scope, Locatio
323327
if (!ty)
324328
{
325329
ty = arena->addType(BlockedType{});
326-
localTypes[*ty] = {};
330+
localTypes.try_insert(*ty, {});
327331
rootScope->lvalueTypes[operand] = *ty;
328332
}
329333

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

719723
TypeId assignee = arena->addType(BlockedType{});
720-
localTypes[assignee] = {};
724+
localTypes.try_insert(assignee, {});
721725

722726
assignees.push_back(assignee);
723727

@@ -756,9 +760,9 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatLocal* stat
756760
for (size_t i = 0; i < statLocal->vars.size; ++i)
757761
{
758762
LUAU_ASSERT(get<BlockedType>(assignees[i]));
759-
std::vector<TypeId>* localDomain = localTypes.find(assignees[i]);
763+
TypeIds* localDomain = localTypes.find(assignees[i]);
760764
LUAU_ASSERT(localDomain);
761-
localDomain->push_back(annotatedTypes[i]);
765+
localDomain->insert(annotatedTypes[i]);
762766
}
763767

764768
TypePackId annotatedPack = arena->addTypePack(std::move(annotatedTypes));
@@ -790,9 +794,9 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatLocal* stat
790794
for (size_t i = 0; i < statLocal->vars.size; ++i)
791795
{
792796
LUAU_ASSERT(get<BlockedType>(assignees[i]));
793-
std::vector<TypeId>* localDomain = localTypes.find(assignees[i]);
797+
TypeIds* localDomain = localTypes.find(assignees[i]);
794798
LUAU_ASSERT(localDomain);
795-
localDomain->push_back(valueTypes[i]);
799+
localDomain->insert(valueTypes[i]);
796800
}
797801
}
798802

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

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

903907
if (var->annotation)
904908
{
@@ -1183,8 +1187,13 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatCompoundAss
11831187
{
11841188
AstExprBinary binop = AstExprBinary{assign->location, assign->op, assign->var, assign->value};
11851189
TypeId resultTy = check(scope, &binop).ty;
1190+
module->astCompoundAssignResultTypes[assign] = resultTy;
1191+
1192+
TypeId lhsType = check(scope, assign->var).ty;
1193+
visitLValue(scope, assign->var, lhsType);
11861194

1187-
visitLValue(scope, assign->var, resultTy);
1195+
follow(lhsType);
1196+
follow(resultTy);
11881197

11891198
return ControlFlow::None;
11901199
}
@@ -1383,16 +1392,15 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClas
13831392
}
13841393
}
13851394

1386-
if (ctv->props.count(propName) == 0)
1395+
TableType::Props& props = assignToMetatable ? metatable->props : ctv->props;
1396+
1397+
if (props.count(propName) == 0)
13871398
{
1388-
if (assignToMetatable)
1389-
metatable->props[propName] = {propTy};
1390-
else
1391-
ctv->props[propName] = {propTy};
1399+
props[propName] = {propTy};
13921400
}
13931401
else
13941402
{
1395-
TypeId currentTy = assignToMetatable ? metatable->props[propName].type() : ctv->props[propName].type();
1403+
TypeId currentTy = props[propName].type();
13961404

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

1405-
if (assignToMetatable)
1406-
metatable->props[propName] = {newItv};
1407-
else
1408-
ctv->props[propName] = {newItv};
1413+
props[propName] = {newItv};
14091414
}
14101415
else if (get<FunctionType>(currentTy))
14111416
{
14121417
TypeId intersection = arena->addType(IntersectionType{{currentTy, propTy}});
14131418

1414-
if (assignToMetatable)
1415-
metatable->props[propName] = {intersection};
1416-
else
1417-
ctv->props[propName] = {intersection};
1419+
props[propName] = {intersection};
14181420
}
14191421
else
14201422
{
@@ -1913,8 +1915,8 @@ Inference ConstraintGenerator::checkIndexName(
19131915
// the current lexical position within the script.
19141916
if (!tt)
19151917
{
1916-
if (auto localDomain = localTypes.find(obj); localDomain && 1 == localDomain->size())
1917-
tt = getTableType(localDomain->front());
1918+
if (TypeIds* localDomain = localTypes.find(obj); localDomain && 1 == localDomain->size())
1919+
tt = getTableType(*localDomain->begin());
19181920
}
19191921

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

23282330
if (ty)
23292331
{
2330-
std::vector<TypeId>* localDomain = localTypes.find(*ty);
2332+
TypeIds* localDomain = localTypes.find(*ty);
23312333
if (localDomain)
2332-
localDomain->push_back(rhsType);
2334+
localDomain->insert(rhsType);
23332335
}
23342336
else
23352337
{
23362338
ty = arena->addType(BlockedType{});
2337-
localTypes[*ty].push_back(rhsType);
2339+
localTypes[*ty].insert(rhsType);
23382340

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

2362-
if (auto localDomain = localTypes.find(*ty))
2363-
localDomain->push_back(rhsType);
2364+
if (TypeIds* localDomain = localTypes.find(*ty))
2365+
localDomain->insert(rhsType);
23642366
}
23652367

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

23842386
bool incremented = recordPropertyAssignment(lhsTy);
23852387

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

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

23992402
bool incremented = recordPropertyAssignment(lhsTy);
24002403

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

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

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

24482453
if (AstExprConstantString* key = item.key->as<AstExprConstantString>())
24492454
{
2450-
ttv->props[key->value.begin()] = {itemTy};
2455+
std::string propName{key->value.data, key->value.size};
2456+
ttv->props[propName] = {itemTy};
24512457
}
24522458
else
24532459
{
@@ -3187,7 +3193,7 @@ bool ConstraintGenerator::recordPropertyAssignment(TypeId ty)
31873193
}
31883194
else if (auto mt = get<MetatableType>(t))
31893195
queue.push_back(mt->table);
3190-
else if (auto localDomain = localTypes.find(t))
3196+
else if (TypeIds* localDomain = localTypes.find(t))
31913197
{
31923198
for (TypeId domainTy : *localDomain)
31933199
queue.push_back(domainTy);

0 commit comments

Comments
 (0)