Skip to content

Commit ff502f0

Browse files
aatxealexmccordandyfriesenVighnesh-Vaviralg
authored
Sync to upstream/release/607 (luau-lang#1131)
# What's changed? * Fix up the `std::iterator_traits` definitions for some Luau data structures. * Replace some of the usages of `std::unordered_set` and `std::unordered_map` with Luau-provided data structures to increase performance and reduce overall number of heap allocations. * Update some of the documentation links in comments throughout the codebase to correctly point to the moved repository. * Expanded JSON encoder for AST to support singleton types. * Fixed a bug in `luau-analyze` where exceptions in the last module being checked during multithreaded analysis would not be rethrown. ### New type solver * Introduce a `refine` type family to handle deferred refinements during type inference, replacing the old `RefineConstraint`. * Continued work on the implementation of type states, fixing some known bugs/blockers. * Added support for variadic functions in new non-strict mode, enabling broader support for builtins and the Roblox API. ### 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: Vyacheslav Egorov <[email protected]> --------- Co-authored-by: Alexander McCord <[email protected]> Co-authored-by: Andy Friesen <[email protected]> Co-authored-by: Vighnesh <[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]>
1 parent 2173938 commit ff502f0

38 files changed

+885
-535
lines changed

Analysis/include/Luau/Constraint.h

+1-19
Original file line numberDiff line numberDiff line change
@@ -197,24 +197,6 @@ struct UnpackConstraint
197197
bool resultIsLValue = false;
198198
};
199199

200-
// resultType ~ refine type mode discriminant
201-
//
202-
// Compute type & discriminant (or type | discriminant) as soon as possible (but
203-
// no sooner), simplify, and bind resultType to that type.
204-
struct RefineConstraint
205-
{
206-
enum
207-
{
208-
Intersection,
209-
Union
210-
} mode;
211-
212-
TypeId resultType;
213-
214-
TypeId type;
215-
TypeId discriminant;
216-
};
217-
218200
// resultType ~ T0 op T1 op ... op TN
219201
//
220202
// op is either union or intersection. If any of the input types are blocked,
@@ -249,7 +231,7 @@ struct ReducePackConstraint
249231

250232
using ConstraintV = Variant<SubtypeConstraint, PackSubtypeConstraint, GeneralizationConstraint, InstantiationConstraint, IterableConstraint,
251233
NameConstraint, TypeAliasExpansionConstraint, FunctionCallConstraint, PrimitiveTypeConstraint, HasPropConstraint, SetPropConstraint,
252-
SetIndexerConstraint, SingletonOrTopTypeConstraint, UnpackConstraint, RefineConstraint, SetOpConstraint, ReduceConstraint, ReducePackConstraint>;
234+
SetIndexerConstraint, SingletonOrTopTypeConstraint, UnpackConstraint, SetOpConstraint, ReduceConstraint, ReducePackConstraint>;
253235

254236
struct Constraint
255237
{

Analysis/include/Luau/ConstraintSolver.h

-1
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,6 @@ struct ConstraintSolver
132132
bool tryDispatch(const SetIndexerConstraint& c, NotNull<const Constraint> constraint, bool force);
133133
bool tryDispatch(const SingletonOrTopTypeConstraint& c, NotNull<const Constraint> constraint);
134134
bool tryDispatch(const UnpackConstraint& c, NotNull<const Constraint> constraint);
135-
bool tryDispatch(const RefineConstraint& c, NotNull<const Constraint> constraint, bool force);
136135
bool tryDispatch(const SetOpConstraint& c, NotNull<const Constraint> constraint, bool force);
137136
bool tryDispatch(const ReduceConstraint& c, NotNull<const Constraint> constraint, bool force);
138137
bool tryDispatch(const ReducePackConstraint& c, NotNull<const Constraint> constraint, bool force);

Analysis/include/Luau/DataFlowGraph.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,8 @@ struct DataFlowGraphBuilder
137137
DfgScope* childScope(DfgScope* scope, DfgScope::ScopeType scopeType = DfgScope::Linear);
138138

139139
void join(DfgScope* p, DfgScope* a, DfgScope* b);
140-
void joinBindings(DfgScope::Bindings& p, const DfgScope::Bindings& a, const DfgScope::Bindings& b);
141-
void joinProps(DfgScope::Props& p, const DfgScope::Props& a, const DfgScope::Props& b);
140+
void joinBindings(DfgScope* p, const DfgScope& a, const DfgScope& b);
141+
void joinProps(DfgScope* p, const DfgScope& a, const DfgScope& b);
142142

143143
DefId lookup(DfgScope* scope, Symbol symbol);
144144
DefId lookup(DfgScope* scope, DefId def, const std::string& key);

Analysis/include/Luau/Set.h

+6
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,12 @@ class Set
118118
class const_iterator
119119
{
120120
public:
121+
using value_type = T;
122+
using reference = T&;
123+
using pointer = T*;
124+
using difference_type = ptrdiff_t;
125+
using iterator_category = std::forward_iterator_tag;
126+
121127
const_iterator(typename Impl::const_iterator impl, typename Impl::const_iterator end)
122128
: impl(impl)
123129
, end(end)

Analysis/include/Luau/Type.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ struct PrimitiveType
176176
}
177177
};
178178

179-
// Singleton types https://github.com/Roblox/luau/blob/master/rfcs/syntax-singleton-types.md
179+
// Singleton types https://github.com/luau-lang/rfcs/blob/master/docs/syntax-singleton-types.md
180180
// Types for true and false
181181
struct BooleanSingleton
182182
{

Analysis/include/Luau/TypeFamily.h

+2
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ struct BuiltinTypeFamilies
162162
TypeFamily leFamily;
163163
TypeFamily eqFamily;
164164

165+
TypeFamily refineFamily;
166+
165167
void addToScope(NotNull<TypeArena> arena, NotNull<Scope> scope) const;
166168
};
167169

Analysis/src/AstJsonEncoder.cpp

+34
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,23 @@ struct AstJsonEncoder : public AstVisitor
200200
{
201201
writeString(name.value ? name.value : "");
202202
}
203+
void write(std::optional<AstArgumentName> name)
204+
{
205+
if (name)
206+
write(*name);
207+
else
208+
writeRaw("null");
209+
}
210+
void write(AstArgumentName name)
211+
{
212+
writeRaw("{");
213+
bool c = pushComma();
214+
writeType("AstArgumentName");
215+
write("name", name.first);
216+
write("location", name.second);
217+
popComma(c);
218+
writeRaw("}");
219+
}
203220

204221
void write(const Position& position)
205222
{
@@ -848,6 +865,7 @@ struct AstJsonEncoder : public AstVisitor
848865
PROP(generics);
849866
PROP(genericPacks);
850867
PROP(argTypes);
868+
PROP(argNames);
851869
PROP(returnTypes);
852870
});
853871
}
@@ -902,6 +920,22 @@ struct AstJsonEncoder : public AstVisitor
902920
});
903921
}
904922

923+
bool visit(class AstTypeSingletonBool* node) override
924+
{
925+
writeNode(node, "AstTypeSingletonBool", [&]() {
926+
write("value", node->value);
927+
});
928+
return false;
929+
}
930+
931+
bool visit(class AstTypeSingletonString* node) override
932+
{
933+
writeNode(node, "AstTypeSingletonString", [&]() {
934+
write("value", node->value);
935+
});
936+
return false;
937+
}
938+
905939
bool visit(class AstExprGroup* node) override
906940
{
907941
write(node);

Analysis/src/ConstraintGenerator.cpp

+9-8
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ void ConstraintGenerator::unionRefinements(const RefinementContext& lhs, const R
257257
return types[0];
258258
else if (2 == types.size())
259259
{
260-
// TODO: It may be advantageous to create a RefineConstraint here when there are blockedTypes.
260+
// TODO: It may be advantageous to introduce a refine type family here when there are blockedTypes.
261261
SimplifyResult sr = simplifyIntersection(builtinTypes, arena, types[0], types[1]);
262262
if (sr.blockedTypes.empty())
263263
return sr.result;
@@ -441,10 +441,14 @@ void ConstraintGenerator::applyRefinements(const ScopePtr& scope, Location locat
441441
{
442442
if (mustDeferIntersection(ty) || mustDeferIntersection(dt))
443443
{
444-
TypeId r = arena->addType(BlockedType{});
445-
addConstraint(scope, location, RefineConstraint{RefineConstraint::Intersection, r, ty, dt});
446-
447-
ty = r;
444+
TypeId resultType = arena->addType(TypeFamilyInstanceType{
445+
NotNull{&kBuiltinTypeFamilies.refineFamily},
446+
{ty, dt},
447+
{},
448+
});
449+
addConstraint(scope, location, ReduceConstraint{resultType});
450+
451+
ty = resultType;
448452
}
449453
else
450454
{
@@ -1005,9 +1009,6 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatAssign* ass
10051009

10061010
checkLValue(scope, lvalue, assignee);
10071011
assignees.push_back(assignee);
1008-
1009-
DefId def = dfg->getDef(lvalue);
1010-
scope->lvalueTypes[def] = assignee;
10111012
}
10121013

10131014
TypePackId resultPack = checkPack(scope, assign->values).tp;

Analysis/src/ConstraintSolver.cpp

+3-150
Original file line numberDiff line numberDiff line change
@@ -545,8 +545,6 @@ bool ConstraintSolver::tryDispatch(NotNull<const Constraint> constraint, bool fo
545545
success = tryDispatch(*sottc, constraint);
546546
else if (auto uc = get<UnpackConstraint>(*constraint))
547547
success = tryDispatch(*uc, constraint);
548-
else if (auto rc = get<RefineConstraint>(*constraint))
549-
success = tryDispatch(*rc, constraint, force);
550548
else if (auto soc = get<SetOpConstraint>(*constraint))
551549
success = tryDispatch(*soc, constraint, force);
552550
else if (auto rc = get<ReduceConstraint>(*constraint))
@@ -887,9 +885,9 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
887885
// In order to prevent infinite types from being expanded and causing us to
888886
// cycle infinitely, we need to scan the type function for cases where we
889887
// expand the same alias with different type saturatedTypeArguments. See
890-
// https://github.com/Roblox/luau/pull/68 for the RFC responsible for this.
891-
// This is a little nicer than using a recursion limit because we can catch
892-
// the infinite expansion before actually trying to expand it.
888+
// https://github.com/luau-lang/luau/pull/68 for the RFC responsible for
889+
// this. This is a little nicer than using a recursion limit because we can
890+
// catch the infinite expansion before actually trying to expand it.
893891
InfiniteTypeFinder itf{this, signature, constraint->scope};
894892
itf.traverse(tf->type);
895893

@@ -1505,151 +1503,6 @@ bool ConstraintSolver::tryDispatch(const UnpackConstraint& c, NotNull<const Cons
15051503
return true;
15061504
}
15071505

1508-
namespace
1509-
{
1510-
1511-
/*
1512-
* Search for types that prevent us from being ready to dispatch a particular
1513-
* RefineConstraint.
1514-
*/
1515-
struct FindRefineConstraintBlockers : TypeOnceVisitor
1516-
{
1517-
DenseHashSet<TypeId> found{nullptr};
1518-
bool visit(TypeId ty, const BlockedType&) override
1519-
{
1520-
found.insert(ty);
1521-
return false;
1522-
}
1523-
1524-
bool visit(TypeId ty, const PendingExpansionType&) override
1525-
{
1526-
found.insert(ty);
1527-
return false;
1528-
}
1529-
1530-
bool visit(TypeId ty, const ClassType&) override
1531-
{
1532-
return false;
1533-
}
1534-
};
1535-
1536-
} // namespace
1537-
1538-
static bool isNegatedAny(TypeId ty)
1539-
{
1540-
ty = follow(ty);
1541-
const NegationType* nt = get<NegationType>(ty);
1542-
if (!nt)
1543-
return false;
1544-
TypeId negatedTy = follow(nt->ty);
1545-
return bool(get<AnyType>(negatedTy));
1546-
}
1547-
1548-
bool ConstraintSolver::tryDispatch(const RefineConstraint& c, NotNull<const Constraint> constraint, bool force)
1549-
{
1550-
if (isBlocked(c.discriminant))
1551-
return block(c.discriminant, constraint);
1552-
1553-
FindRefineConstraintBlockers fbt;
1554-
fbt.traverse(c.discriminant);
1555-
1556-
if (!fbt.found.empty())
1557-
{
1558-
bool foundOne = false;
1559-
1560-
for (TypeId blocked : fbt.found)
1561-
{
1562-
if (blocked == c.type)
1563-
continue;
1564-
1565-
block(blocked, constraint);
1566-
foundOne = true;
1567-
}
1568-
1569-
if (foundOne)
1570-
return false;
1571-
}
1572-
1573-
/* HACK: Refinements sometimes produce a type T & ~any under the assumption
1574-
* that ~any is the same as any. This is so so weird, but refinements needs
1575-
* some way to say "I may refine this, but I'm not sure."
1576-
*
1577-
* It does this by refining on a blocked type and deferring the decision
1578-
* until it is unblocked.
1579-
*
1580-
* Refinements also get negated, so we wind up with types like T & ~*blocked*
1581-
*
1582-
* We need to treat T & ~any as T in this case.
1583-
*/
1584-
1585-
if (c.mode == RefineConstraint::Intersection && isNegatedAny(c.discriminant))
1586-
{
1587-
asMutable(c.resultType)->ty.emplace<BoundType>(c.type);
1588-
unblock(c.resultType, constraint->location);
1589-
return true;
1590-
}
1591-
1592-
const TypeId type = follow(c.type);
1593-
1594-
if (hasUnresolvedConstraints(type))
1595-
return block(type, constraint);
1596-
1597-
LUAU_ASSERT(get<BlockedType>(c.resultType));
1598-
1599-
if (type == c.resultType)
1600-
{
1601-
/*
1602-
* Sometimes, we get a constraint of the form
1603-
*
1604-
* *blocked-N* ~ refine *blocked-N* & U
1605-
*
1606-
* The constraint essentially states that a particular type is a
1607-
* refinement of itself. This is weird and I think vacuous.
1608-
*
1609-
* I *believe* it is safe to replace the result with a fresh type that
1610-
* is constrained by U. We effect this by minting a fresh type for the
1611-
* result when U = any, else we bind the result to whatever discriminant
1612-
* was offered.
1613-
*/
1614-
if (get<AnyType>(follow(c.discriminant)))
1615-
{
1616-
TypeId f = freshType(arena, builtinTypes, constraint->scope);
1617-
asMutable(c.resultType)->ty.emplace<BoundType>(f);
1618-
}
1619-
else
1620-
asMutable(c.resultType)->ty.emplace<BoundType>(c.discriminant);
1621-
1622-
unblock(c.resultType, constraint->location);
1623-
return true;
1624-
}
1625-
1626-
auto [result, blockedTypes] = c.mode == RefineConstraint::Intersection ? simplifyIntersection(builtinTypes, NotNull{arena}, type, c.discriminant)
1627-
: simplifyUnion(builtinTypes, NotNull{arena}, type, c.discriminant);
1628-
1629-
if (!force && !blockedTypes.empty())
1630-
return block(blockedTypes, constraint);
1631-
1632-
switch (shouldSuppressErrors(normalizer, c.type))
1633-
{
1634-
case ErrorSuppression::Suppress:
1635-
{
1636-
auto resultOrError = simplifyUnion(builtinTypes, arena, result, builtinTypes->errorType).result;
1637-
asMutable(c.resultType)->ty.emplace<BoundType>(resultOrError);
1638-
break;
1639-
}
1640-
case ErrorSuppression::DoNotSuppress:
1641-
asMutable(c.resultType)->ty.emplace<BoundType>(result);
1642-
break;
1643-
case ErrorSuppression::NormalizationFailed:
1644-
reportError(NormalizationTooComplex{}, constraint->location);
1645-
break;
1646-
}
1647-
1648-
unblock(c.resultType, constraint->location);
1649-
1650-
return true;
1651-
}
1652-
16531506
bool ConstraintSolver::tryDispatch(const SetOpConstraint& c, NotNull<const Constraint> constraint, bool force)
16541507
{
16551508
bool blocked = false;

0 commit comments

Comments
 (0)