Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/Analysis/src/SubtypingUnifier.cpp
2725 views
1
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
2
3
#include "Luau/SubtypingUnifier.h"
4
5
#include "Luau/ConstraintSolver.h"
6
#include "Luau/Simplify.h"
7
#include "Luau/Subtyping.h"
8
#include "Luau/TypeUtils.h"
9
#include "Luau/Unifier2.h"
10
11
LUAU_FASTFLAG(LuauUnifyWithSubtyping2)
12
13
namespace Luau
14
{
15
16
SubtypingUnifier::SubtypingUnifier(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, NotNull<InternalErrorReporter> reporter)
17
: arena{arena}
18
, builtinTypes{builtinTypes}
19
, reporter{reporter}
20
{
21
}
22
23
24
bool SubtypingUnifier::canBeUnified(TypeId ty) const
25
{
26
ty = follow(ty);
27
if (auto tbl = get<TableType>(ty))
28
return tbl->state != TableState::Sealed;
29
30
return is<FreeType>(ty) || isBlocked(ty);
31
}
32
33
SubtypingUnifier::Result SubtypingUnifier::dispatchConstraints(
34
NotNull<const Constraint> constraint,
35
std::vector<ConstraintV> assumedConstraints
36
) const
37
{
38
UnifyResult unifierRes = UnifyResult::Ok;
39
// NOTE: You *could* potentially reuse the input vector, but this seems
40
// easier to read.
41
std::vector<ConstraintV> outstandingConstraints;
42
outstandingConstraints.reserve(assumedConstraints.size());
43
UpperBounds upperBounds{nullptr};
44
for (auto& cv : assumedConstraints)
45
{
46
const auto& [unified, dispatched] = dispatchOneConstraint(constraint, cv, upperBounds);
47
unifierRes &= unified;
48
if (!dispatched)
49
outstandingConstraints.push_back(std::move(cv));
50
}
51
return {unifierRes, std::move(outstandingConstraints), std::move(upperBounds)};
52
}
53
54
OccursCheckResult SubtypingUnifier::occursCheck(TypePackId needle, TypePackId haystack) const
55
{
56
needle = follow(needle);
57
haystack = follow(haystack);
58
59
if (getMutable<ErrorTypePack>(needle))
60
return OccursCheckResult::Pass;
61
62
if (!getMutable<FreeTypePack>(needle))
63
reporter->ice("Expected needle pack to be free");
64
65
while (!getMutable<ErrorTypePack>(haystack))
66
{
67
if (needle == haystack)
68
return OccursCheckResult::Fail;
69
70
if (auto a = get<TypePack>(haystack); a && a->tail)
71
{
72
haystack = follow(*a->tail);
73
continue;
74
}
75
76
break;
77
}
78
79
return OccursCheckResult::Pass;
80
}
81
82
83
std::pair<UnifyResult, bool> SubtypingUnifier::dispatchOneConstraint(
84
NotNull<const Constraint> constraint,
85
const ConstraintV& cv,
86
UpperBounds& upperBoundContributors
87
) const
88
{
89
if (const auto sc = get_if<SubtypeConstraint>(&cv))
90
{
91
TypeId subTy = follow(sc->subType);
92
TypeId superTy = follow(sc->superType);
93
94
LUAU_ASSERT(canBeUnified(subTy) || canBeUnified(superTy));
95
96
if (isBlocked(subTy) || isBlocked(superTy))
97
return {UnifyResult::Ok, false};
98
99
if (auto superFreeTy = getMutable<FreeType>(superTy))
100
{
101
superFreeTy->lowerBound = simplifyUnion(builtinTypes, arena, subTy, superFreeTy->lowerBound).result;
102
}
103
104
if (auto subFreeType = getMutable<FreeType>(subTy))
105
{
106
subFreeType->upperBound = simplifyIntersection(builtinTypes, arena, subFreeType->upperBound, superTy).result;
107
upperBoundContributors[subTy].emplace_back(constraint->location, superTy);
108
}
109
110
// FIXME CLI-182960: Unification shouldn't be the mechanism for adding table indexers
111
if (auto pair = get2<TableType, TableType>(subTy, superTy); pair && !pair.first->indexer)
112
getMutable<TableType>(subTy)->indexer = TableIndexer{pair.second->indexer->indexType, pair.second->indexer->indexResultType};
113
}
114
else if (auto psc = get_if<PackSubtypeConstraint>(&cv))
115
{
116
TypePackId subTp = follow(psc->subPack);
117
TypePackId superTp = follow(psc->superPack);
118
// There *should* be an assertion here that either part of the constraint is
119
// free, but because free type packs are replaced on-the-spot, we *may*
120
// encounter conflicting constraints. One example is for:
121
//
122
// local callbacks: { () -> () } = {
123
// function () end,
124
// function () end
125
// }
126
//
127
// We'll end up minting two sets of constraints for each lambda (as we need
128
// them to be _exactly_ `()` as per the table type).
129
if (is<FreeTypePack>(subTp))
130
{
131
if (OccursCheckResult::Fail == occursCheck(subTp, superTp))
132
{
133
emplaceTypePack<BoundTypePack>(asMutable(subTp), builtinTypes->errorTypePack);
134
return {UnifyResult::OccursCheckFailed, true};
135
}
136
emplaceTypePack<BoundTypePack>(asMutable(subTp), superTp);
137
return {UnifyResult::Ok, true};
138
}
139
140
if (is<FreeTypePack>(superTp))
141
{
142
if (OccursCheckResult::Fail == occursCheck(superTp, subTp))
143
{
144
emplaceTypePack<BoundTypePack>(asMutable(superTp), builtinTypes->errorTypePack);
145
return {UnifyResult::OccursCheckFailed, true};
146
}
147
148
emplaceTypePack<BoundTypePack>(asMutable(superTp), subTp);
149
return {UnifyResult::Ok, true};
150
}
151
}
152
else
153
{
154
LUAU_ASSERT(!"Unreachable, unexpected constraint in subtyping unifier.");
155
return {UnifyResult::Ok, false};
156
}
157
158
return {UnifyResult::Ok, true};
159
}
160
161
162
163
} // namespace Luau
164
165