Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/Analysis/src/Constraint.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/Constraint.h"
4
#include "Luau/TypeFunction.h"
5
#include "Luau/VisitType.h"
6
7
LUAU_FASTFLAG(LuauUseConstraintSetsToTrackFreeTypes)
8
9
namespace Luau
10
{
11
12
Constraint::Constraint(NotNull<Scope> scope, const Location& location, ConstraintV&& c)
13
: scope(scope)
14
, location(location)
15
, c(std::move(c))
16
{
17
}
18
19
struct ReferenceCountInitializer : TypeOnceVisitor
20
{
21
NotNull<TypeIds> mutatedTypes;
22
TypePackIds* mutatedTypePacks;
23
bool traverseIntoTypeFunctions = true;
24
25
explicit ReferenceCountInitializer(NotNull<TypeIds> mutatedTypes)
26
: TypeOnceVisitor("ReferenceCountInitializer", /* skipBoundTypes */ true)
27
, mutatedTypes(mutatedTypes)
28
, mutatedTypePacks(nullptr)
29
{
30
LUAU_ASSERT(!FFlag::LuauUseConstraintSetsToTrackFreeTypes);
31
}
32
33
explicit ReferenceCountInitializer(
34
NotNull<TypeIds> mutatedTypes,
35
NotNull<TypePackIds> mutatedTypePacks
36
)
37
: TypeOnceVisitor("ReferenceCountInitializer", /* skipBoundTypes */ true)
38
, mutatedTypes(mutatedTypes)
39
, mutatedTypePacks(mutatedTypePacks.get())
40
{
41
LUAU_ASSERT(FFlag::LuauUseConstraintSetsToTrackFreeTypes);
42
}
43
44
bool visit(TypeId ty, const FreeType&) override
45
{
46
mutatedTypes->insert(ty);
47
return false;
48
}
49
50
bool visit(TypeId ty, const BlockedType&) override
51
{
52
mutatedTypes->insert(ty);
53
return false;
54
}
55
56
bool visit(TypeId ty, const PendingExpansionType&) override
57
{
58
mutatedTypes->insert(ty);
59
return false;
60
}
61
62
bool visit(TypeId ty, const TableType& tt) override
63
{
64
if (tt.state == TableState::Unsealed || tt.state == TableState::Free)
65
mutatedTypes->insert(ty);
66
67
return true;
68
}
69
70
bool visit(TypeId ty, const ExternType&) override
71
{
72
// ExternTypes never contain free types.
73
return false;
74
}
75
76
bool visit(TypeId, const TypeFunctionInstanceType& tfit) override
77
{
78
return tfit.function->canReduceGenerics;
79
}
80
};
81
82
bool isReferenceCountedType(const TypeId typ)
83
{
84
if (auto tt = get<TableType>(typ))
85
return tt->state == TableState::Free || tt->state == TableState::Unsealed;
86
87
// n.b. this should match whatever `ReferenceCountInitializer` includes.
88
return get<FreeType>(typ) || get<BlockedType>(typ) || get<PendingExpansionType>(typ);
89
}
90
91
TypeIds Constraint::DEPRECATED_getMaybeMutatedFreeTypes() const
92
{
93
LUAU_ASSERT(!FFlag::LuauUseConstraintSetsToTrackFreeTypes);
94
// For the purpose of this function and reference counting in general, we are only considering
95
// mutations that affect the _bounds_ of the free type, and not something that may bind the free
96
// type itself to a new type. As such, `ReduceConstraint` and `GeneralizationConstraint` have no
97
// contribution to the output set here.
98
99
TypeIds types;
100
ReferenceCountInitializer rci{NotNull{&types}};
101
102
if (auto ec = get<EqualityConstraint>(*this))
103
{
104
rci.traverse(ec->resultType);
105
rci.traverse(ec->assignmentType);
106
}
107
else if (auto sc = get<SubtypeConstraint>(*this))
108
{
109
rci.traverse(sc->subType);
110
rci.traverse(sc->superType);
111
}
112
else if (auto psc = get<PackSubtypeConstraint>(*this))
113
{
114
rci.traverse(psc->subPack);
115
rci.traverse(psc->superPack);
116
}
117
else if (auto itc = get<IterableConstraint>(*this))
118
{
119
for (TypeId ty : itc->variables)
120
rci.traverse(ty);
121
// `IterableConstraints` should not mutate `iterator`.
122
}
123
else if (auto nc = get<NameConstraint>(*this))
124
{
125
rci.traverse(nc->namedType);
126
}
127
else if (auto taec = get<TypeAliasExpansionConstraint>(*this))
128
{
129
rci.traverse(taec->target);
130
}
131
else if (auto fchc = get<FunctionCheckConstraint>(*this))
132
{
133
rci.traverse(fchc->argsPack);
134
}
135
else if (auto fcc = get<FunctionCallConstraint>(*this))
136
{
137
rci.traverseIntoTypeFunctions = false;
138
rci.traverse(fcc->fn);
139
rci.traverse(fcc->argsPack);
140
rci.traverseIntoTypeFunctions = true;
141
}
142
else if (auto ptc = get<PrimitiveTypeConstraint>(*this))
143
{
144
rci.traverse(ptc->freeType);
145
}
146
else if (auto hpc = get<HasPropConstraint>(*this))
147
{
148
rci.traverse(hpc->resultType);
149
rci.traverse(hpc->subjectType);
150
}
151
else if (auto hic = get<HasIndexerConstraint>(*this))
152
{
153
rci.traverse(hic->subjectType);
154
rci.traverse(hic->resultType);
155
// `HasIndexerConstraint` should not mutate `indexType`.
156
}
157
else if (auto apc = get<AssignPropConstraint>(*this))
158
{
159
rci.traverse(apc->lhsType);
160
rci.traverse(apc->rhsType);
161
}
162
else if (auto aic = get<AssignIndexConstraint>(*this))
163
{
164
rci.traverse(aic->lhsType);
165
rci.traverse(aic->indexType);
166
rci.traverse(aic->rhsType);
167
}
168
else if (auto uc = get<UnpackConstraint>(*this))
169
{
170
for (TypeId ty : uc->resultPack)
171
rci.traverse(ty);
172
// `UnpackConstraint` should not mutate `sourcePack`.
173
}
174
else if (auto rpc = get<ReducePackConstraint>(*this))
175
{
176
rci.traverse(rpc->tp);
177
}
178
else if (auto pftc = get<PushFunctionTypeConstraint>(*this))
179
{
180
rci.traverse(pftc->functionType);
181
}
182
else if (auto ptc = get<PushTypeConstraint>(*this))
183
{
184
rci.traverse(ptc->targetType);
185
}
186
187
return types;
188
}
189
190
std::pair<TypeIds, TypePackIds> Constraint::getMaybeMutatedTypes() const
191
{
192
LUAU_ASSERT(FFlag::LuauUseConstraintSetsToTrackFreeTypes);
193
194
// For the purpose of this function and reference counting in general, we are only considering
195
// mutations that affect the _bounds_ of the free type, and not something that may bind the free
196
// type itself to a new type. As such, `ReduceConstraint` and `GeneralizationConstraint` have no
197
// contribution to the output set here.
198
199
TypeIds types;
200
201
// NOTE: In the future we'd like to track references to type packs, so we're
202
// adding this local, but we do not modify it.
203
TypePackIds typePacks;
204
205
ReferenceCountInitializer rci{NotNull{&types}, NotNull{&typePacks}};
206
207
if (auto ec = get<EqualityConstraint>(*this))
208
{
209
rci.traverse(ec->resultType);
210
rci.traverse(ec->assignmentType);
211
}
212
else if (auto sc = get<SubtypeConstraint>(*this))
213
{
214
rci.traverse(sc->subType);
215
rci.traverse(sc->superType);
216
}
217
else if (auto psc = get<PackSubtypeConstraint>(*this))
218
{
219
rci.traverse(psc->subPack);
220
rci.traverse(psc->superPack);
221
}
222
else if (auto itc = get<IterableConstraint>(*this))
223
{
224
for (TypeId ty : itc->variables)
225
rci.traverse(ty);
226
// `IterableConstraints` should not mutate `iterator`.
227
}
228
else if (auto nc = get<NameConstraint>(*this))
229
{
230
rci.traverse(nc->namedType);
231
}
232
else if (auto taec = get<TypeAliasExpansionConstraint>(*this))
233
{
234
rci.traverse(taec->target);
235
}
236
else if (auto fchc = get<FunctionCheckConstraint>(*this))
237
{
238
rci.traverse(fchc->argsPack);
239
}
240
else if (auto fcc = get<FunctionCallConstraint>(*this))
241
{
242
rci.traverseIntoTypeFunctions = false;
243
rci.traverse(fcc->fn);
244
rci.traverse(fcc->argsPack);
245
rci.traverseIntoTypeFunctions = true;
246
}
247
else if (auto ptc = get<PrimitiveTypeConstraint>(*this))
248
{
249
rci.traverse(ptc->freeType);
250
}
251
else if (auto hpc = get<HasPropConstraint>(*this))
252
{
253
rci.traverse(hpc->resultType);
254
rci.traverse(hpc->subjectType);
255
}
256
else if (auto hic = get<HasIndexerConstraint>(*this))
257
{
258
rci.traverse(hic->subjectType);
259
rci.traverse(hic->resultType);
260
// `HasIndexerConstraint` should not mutate `indexType`.
261
}
262
else if (auto apc = get<AssignPropConstraint>(*this))
263
{
264
rci.traverse(apc->lhsType);
265
rci.traverse(apc->rhsType);
266
}
267
else if (auto aic = get<AssignIndexConstraint>(*this))
268
{
269
rci.traverse(aic->lhsType);
270
rci.traverse(aic->indexType);
271
rci.traverse(aic->rhsType);
272
}
273
else if (auto uc = get<UnpackConstraint>(*this))
274
{
275
for (TypeId ty : uc->resultPack)
276
rci.traverse(ty);
277
// Consider:
278
//
279
// function set(dictionary, key, value)
280
// local new = table.clone(dictionary)
281
// new[key] = value
282
// return new
283
// end
284
//
285
// In this case, we would expect `dictionary` to be inferred as
286
// something like `{ [T]: K }` for some generic `T` and `K`.
287
// However, in order to avoid eagerly generalizing dictionary,
288
// we need to track that it may be mutated by the line:
289
//
290
// new[key] = value
291
//
292
// ... this implies that `UnpackConstraint` can mutate both
293
// it's LHS and RHS operands. LHS directly, and RHS by proxy.
294
rci.traverse(uc->sourcePack);
295
}
296
else if (auto rpc = get<ReducePackConstraint>(*this))
297
{
298
rci.traverse(rpc->tp);
299
}
300
else if (auto pftc = get<PushFunctionTypeConstraint>(*this))
301
{
302
rci.traverse(pftc->functionType);
303
}
304
else if (auto ptc = get<PushTypeConstraint>(*this))
305
{
306
rci.traverse(ptc->targetType);
307
}
308
309
return { std::move(types), std::move(typePacks) };
310
}
311
312
} // namespace Luau
313
314