Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/Analysis/src/Instantiation.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
#include "Luau/Instantiation.h"
3
4
#include "Luau/Clone.h"
5
#include "Luau/Common.h"
6
#include "Luau/Instantiation2.h" // including for `Replacer` which was stolen since it will be kept in the new solver
7
#include "Luau/ToString.h"
8
#include "Luau/TxnLog.h"
9
#include "Luau/TypeArena.h"
10
#include "Luau/TypeCheckLimits.h"
11
12
#include <algorithm>
13
14
LUAU_FASTFLAG(LuauSolverV2)
15
LUAU_FASTFLAGVARIABLE(LuauReplacerRespectsReboundGenerics)
16
LUAU_FASTFLAGVARIABLE(LuauReplacerIsSolverAgnostic)
17
18
namespace Luau
19
{
20
21
void Instantiation::resetState(const TxnLog* log, TypeArena* arena, NotNull<BuiltinTypes> builtinTypes, TypeLevel level, Scope* scope)
22
{
23
Substitution::resetState(log, arena);
24
25
this->builtinTypes = builtinTypes;
26
27
this->level = level;
28
this->scope = scope;
29
}
30
31
bool Instantiation::isDirty(TypeId ty)
32
{
33
if (const FunctionType* ftv = log->getMutable<FunctionType>(ty))
34
{
35
if (ftv->hasNoFreeOrGenericTypes)
36
return false;
37
38
return true;
39
}
40
else
41
{
42
return false;
43
}
44
}
45
46
bool Instantiation::isDirty(TypePackId tp)
47
{
48
return false;
49
}
50
51
bool Instantiation::ignoreChildren(TypeId ty)
52
{
53
if (log->getMutable<FunctionType>(ty))
54
return true;
55
else if (get<ExternType>(ty))
56
return true;
57
else
58
return false;
59
}
60
61
TypeId Instantiation::clean(TypeId ty)
62
{
63
const FunctionType* ftv = log->getMutable<FunctionType>(ty);
64
LUAU_ASSERT(ftv);
65
66
FunctionType clone = FunctionType{level, ftv->argTypes, ftv->retTypes, ftv->definition, ftv->hasSelf};
67
clone.magic = ftv->magic;
68
clone.tags = ftv->tags;
69
clone.argNames = ftv->argNames;
70
clone.isDeprecatedFunction = ftv->isDeprecatedFunction;
71
clone.deprecatedInfo = ftv->deprecatedInfo;
72
TypeId result = addType(std::move(clone));
73
74
// Annoyingly, we have to do this even if there are no generics,
75
// to replace any generic tables.
76
reusableReplaceGenerics.resetState(log, arena, builtinTypes, level, scope, ftv->generics, ftv->genericPacks);
77
78
// TODO: What to do if this returns nullopt?
79
// We don't have access to the error-reporting machinery
80
result = reusableReplaceGenerics.substitute(result).value_or(result);
81
82
asMutable(result)->documentationSymbol = ty->documentationSymbol;
83
return result;
84
}
85
86
TypePackId Instantiation::clean(TypePackId tp)
87
{
88
LUAU_ASSERT(false);
89
return tp;
90
}
91
92
void ReplaceGenerics::resetState(
93
const TxnLog* log,
94
TypeArena* arena,
95
NotNull<BuiltinTypes> builtinTypes,
96
TypeLevel level,
97
Scope* scope,
98
const std::vector<TypeId>& generics,
99
const std::vector<TypePackId>& genericPacks
100
)
101
{
102
Substitution::resetState(log, arena);
103
104
this->builtinTypes = builtinTypes;
105
106
this->level = level;
107
this->scope = scope;
108
109
this->generics = generics;
110
this->genericPacks = genericPacks;
111
}
112
113
bool ReplaceGenerics::ignoreChildren(TypeId ty)
114
{
115
if (const FunctionType* ftv = log->getMutable<FunctionType>(ty))
116
{
117
if (ftv->hasNoFreeOrGenericTypes)
118
return true;
119
120
// We aren't recursing in the case of a generic function which
121
// binds the same generics. This can happen if, for example, there's recursive types.
122
// If T = <a>(a,T)->T then instantiating T should produce T' = (X,T)->T not T' = (X,T')->T'.
123
// It's OK to use vector equality here, since we always generate fresh generics
124
// whenever we quantify, so the vectors overlap if and only if they are equal.
125
return (!generics.empty() || !genericPacks.empty()) && (ftv->generics == generics) && (ftv->genericPacks == genericPacks);
126
}
127
else if (get<ExternType>(ty))
128
return true;
129
else
130
{
131
return false;
132
}
133
}
134
135
bool ReplaceGenerics::isDirty(TypeId ty)
136
{
137
if (const TableType* ttv = log->getMutable<TableType>(ty))
138
return ttv->state == TableState::Generic;
139
else if (log->getMutable<GenericType>(ty))
140
return std::find(generics.begin(), generics.end(), ty) != generics.end();
141
else
142
return false;
143
}
144
145
bool ReplaceGenerics::isDirty(TypePackId tp)
146
{
147
if (log->getMutable<GenericTypePack>(tp))
148
return std::find(genericPacks.begin(), genericPacks.end(), tp) != genericPacks.end();
149
else
150
return false;
151
}
152
153
TypeId ReplaceGenerics::clean(TypeId ty)
154
{
155
LUAU_ASSERT(isDirty(ty));
156
157
if (FFlag::LuauReplacerIsSolverAgnostic)
158
{
159
if (const TableType* ttv = log->getMutable<TableType>(ty))
160
{
161
TableType clone = TableType{ttv->props, ttv->indexer, level, scope, TableState::Free};
162
clone.definitionModuleName = ttv->definitionModuleName;
163
clone.definitionLocation = ttv->definitionLocation;
164
return addType(std::move(clone));
165
}
166
else
167
return arena->freshType(builtinTypes, scope, level);
168
}
169
else
170
{
171
if (const TableType* ttv = log->getMutable<TableType>(ty))
172
{
173
TableType clone = TableType{ttv->props, ttv->indexer, level, scope, TableState::Free};
174
clone.definitionModuleName = ttv->definitionModuleName;
175
clone.definitionLocation = ttv->definitionLocation;
176
return addType(std::move(clone));
177
}
178
else if (FFlag::LuauSolverV2)
179
{
180
TypeId res = freshType(NotNull{arena}, builtinTypes, scope);
181
getMutable<FreeType>(res)->level = level;
182
return res;
183
}
184
else
185
{
186
return arena->freshType(builtinTypes, scope, level);
187
}
188
}
189
}
190
191
TypePackId ReplaceGenerics::clean(TypePackId tp)
192
{
193
LUAU_ASSERT(isDirty(tp));
194
return addTypePack(TypePackVar(FreeTypePack{scope, level}));
195
}
196
197
std::optional<TypeId> instantiate(
198
NotNull<BuiltinTypes> builtinTypes,
199
NotNull<TypeArena> arena,
200
NotNull<TypeCheckLimits> limits,
201
NotNull<Scope> scope,
202
TypeId ty
203
)
204
{
205
ty = follow(ty);
206
207
const FunctionType* ft = get<FunctionType>(ty);
208
if (!ft)
209
return ty;
210
211
if (ft->generics.empty() && ft->genericPacks.empty())
212
return ty;
213
214
DenseHashMap<TypeId, TypeId> replacements{nullptr};
215
DenseHashMap<TypePackId, TypePackId> replacementPacks{nullptr};
216
217
for (TypeId g : ft->generics)
218
replacements[g] = freshType(arena, builtinTypes, scope);
219
220
for (TypePackId g : ft->genericPacks)
221
replacementPacks[g] = arena->freshTypePack(scope);
222
223
if (FFlag::LuauReplacerRespectsReboundGenerics)
224
{
225
Replacer r{arena, NotNull{&replacements}, NotNull{&replacementPacks}};
226
227
if (limits->instantiationChildLimit)
228
r.childLimit = *limits->instantiationChildLimit;
229
230
CloneState cs{builtinTypes};
231
// We clone persistent types here to enable instantiation for generic
232
// builtins like `table.find`; otherwise, the lines after would
233
// immediately corrupt the definitions of the original function.
234
auto clonedFunctionTypeId = shallowClone(ty, *arena, cs, /* clonePersistentTypes */ true);
235
FunctionType* ft2 = getMutable<FunctionType>(clonedFunctionTypeId);
236
LUAU_ASSERT(ft != ft2);
237
238
ft2->generics.clear();
239
ft2->genericPacks.clear();
240
241
return r.substitute(clonedFunctionTypeId);
242
}
243
else
244
{
245
Replacer_DEPRECATED r{arena, std::move(replacements), std::move(replacementPacks)};
246
247
if (limits->instantiationChildLimit)
248
r.childLimit = *limits->instantiationChildLimit;
249
250
std::optional<TypeId> res = r.substitute(ty);
251
if (!res)
252
return res;
253
254
FunctionType* ft2 = getMutable<FunctionType>(*res);
255
LUAU_ASSERT(ft != ft2);
256
257
ft2->generics.clear();
258
ft2->genericPacks.clear();
259
260
return res;
261
}
262
}
263
264
} // namespace Luau
265
266