Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/Analysis/src/Module.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/Module.h"
3
4
#include "Luau/Clone.h"
5
#include "Luau/Common.h"
6
#include "Luau/ConstraintGenerator.h"
7
#include "Luau/Normalize.h"
8
#include "Luau/RecursionCounter.h"
9
#include "Luau/Scope.h"
10
#include "Luau/Type.h"
11
#include "Luau/TypeInfer.h"
12
#include "Luau/TypePack.h"
13
#include "Luau/VisitType.h"
14
15
#include <algorithm>
16
17
namespace Luau
18
{
19
20
static void defaultLogLuau(std::string_view context, std::string_view input)
21
{
22
// The default is to do nothing because we don't want to mess with
23
// the xml parsing done by the dcr script.
24
}
25
26
Luau::LogLuauProc logLuau = &defaultLogLuau;
27
28
void setLogLuau(LogLuauProc ll)
29
{
30
logLuau = ll;
31
}
32
33
void resetLogLuauProc()
34
{
35
logLuau = &defaultLogLuau;
36
}
37
38
static bool contains(Position pos, Comment comment)
39
{
40
if (comment.location.contains(pos))
41
return true;
42
else if (comment.type == Lexeme::BrokenComment && comment.location.begin <= pos) // Broken comments are broken specifically because they don't
43
// have an end
44
return true;
45
// comments actually span the whole line - in incremental mode, we could pass a cursor outside of the current parsed comment range span, but it
46
// would still be 'within' the comment So, the cursor must be on the same line and the comment itself must come strictly after the `begin`
47
else if (comment.type == Lexeme::Comment && comment.location.end.line == pos.line && comment.location.begin <= pos)
48
return true;
49
else
50
return false;
51
}
52
53
bool isWithinComment(const std::vector<Comment>& commentLocations, Position pos)
54
{
55
auto iter = std::lower_bound(
56
commentLocations.begin(),
57
commentLocations.end(),
58
Comment{Lexeme::Comment, Location{pos, pos}},
59
[](const Comment& a, const Comment& b)
60
{
61
if (a.type == Lexeme::Comment)
62
return a.location.end.line < b.location.end.line;
63
return a.location.end < b.location.end;
64
}
65
);
66
67
if (iter == commentLocations.end())
68
return false;
69
70
if (contains(pos, *iter))
71
return true;
72
73
// Due to the nature of std::lower_bound, it is possible that iter points at a comment that ends
74
// at pos. We'll try the next comment, if it exists.
75
++iter;
76
if (iter == commentLocations.end())
77
return false;
78
79
return contains(pos, *iter);
80
}
81
82
bool isWithinComment(const SourceModule& sourceModule, Position pos)
83
{
84
return isWithinComment(sourceModule.commentLocations, pos);
85
}
86
87
bool isWithinComment(const ParseResult& result, Position pos)
88
{
89
return isWithinComment(result.commentLocations, pos);
90
}
91
92
bool isWithinHotComment(const std::vector<HotComment>& hotComments, Position pos)
93
{
94
for (const HotComment& hotComment : hotComments)
95
{
96
if (hotComment.location.containsClosed(pos))
97
return true;
98
}
99
100
return false;
101
}
102
103
bool isWithinHotComment(const SourceModule& sourceModule, Position pos)
104
{
105
return isWithinHotComment(sourceModule.hotcomments, pos);
106
}
107
108
bool isWithinHotComment(const ParseResult& result, Position pos)
109
{
110
return isWithinHotComment(result.hotcomments, pos);
111
}
112
113
struct ClonePublicInterface : Substitution
114
{
115
NotNull<BuiltinTypes> builtinTypes;
116
NotNull<Module> module;
117
SolverMode solverMode;
118
bool internalTypeEscaped = false;
119
120
ClonePublicInterface(const TxnLog* log, NotNull<BuiltinTypes> builtinTypes, Module* module, SolverMode solverMode)
121
: Substitution(log, &module->interfaceTypes)
122
, builtinTypes(builtinTypes)
123
, module(module)
124
, solverMode(solverMode)
125
{
126
LUAU_ASSERT(module);
127
}
128
129
bool isNewSolver() const
130
{
131
return solverMode == SolverMode::New;
132
}
133
134
bool isDirty(TypeId ty) override
135
{
136
if (ty->owningArena == &module->internalTypes)
137
return true;
138
139
if (const FunctionType* ftv = get<FunctionType>(ty))
140
return ftv->level.level != 0;
141
if (const TableType* ttv = get<TableType>(ty))
142
return ttv->level.level != 0;
143
return false;
144
}
145
146
bool isDirty(TypePackId tp) override
147
{
148
return tp->owningArena == &module->internalTypes;
149
}
150
151
bool ignoreChildrenVisit(TypeId ty) override
152
{
153
if (ty->owningArena != &module->internalTypes)
154
return true;
155
156
return false;
157
}
158
159
bool ignoreChildrenVisit(TypePackId tp) override
160
{
161
if (tp->owningArena != &module->internalTypes)
162
return true;
163
164
return false;
165
}
166
167
TypeId clean(TypeId ty) override
168
{
169
TypeId result = clone(ty);
170
171
if (FunctionType* ftv = getMutable<FunctionType>(result))
172
{
173
if (ftv->generics.empty() && ftv->genericPacks.empty())
174
{
175
GenericTypeFinder marker;
176
marker.traverse(result);
177
178
if (!marker.found)
179
ftv->hasNoFreeOrGenericTypes = true;
180
}
181
182
ftv->level = TypeLevel{0, 0};
183
}
184
else if (TableType* ttv = getMutable<TableType>(result))
185
{
186
ttv->level = TypeLevel{0, 0};
187
if (isNewSolver())
188
{
189
ttv->scope = nullptr;
190
ttv->state = TableState::Sealed;
191
}
192
}
193
194
if (isNewSolver())
195
{
196
if (is<FreeType, BlockedType, PendingExpansionType>(ty))
197
{
198
internalTypeEscaped = true;
199
result = builtinTypes->errorType;
200
}
201
else if (auto genericty = getMutable<GenericType>(result))
202
{
203
genericty->scope = nullptr;
204
}
205
}
206
207
return result;
208
}
209
210
TypePackId clean(TypePackId tp) override
211
{
212
if (isNewSolver())
213
{
214
if (is<FreeTypePack, BlockedTypePack>(tp))
215
{
216
internalTypeEscaped = true;
217
return builtinTypes->errorTypePack;
218
}
219
220
auto clonedTp = clone(tp);
221
if (auto gtp = getMutable<GenericTypePack>(clonedTp))
222
gtp->scope = nullptr;
223
return clonedTp;
224
}
225
else
226
{
227
return clone(tp);
228
}
229
}
230
231
TypeId cloneType(TypeId ty)
232
{
233
std::optional<TypeId> result = substitute(ty);
234
if (result)
235
{
236
return *result;
237
}
238
else
239
{
240
241
module->errors.emplace_back(module->scopes[0].first, UnificationTooComplex{});
242
return builtinTypes->errorType;
243
}
244
}
245
246
TypePackId cloneTypePack(TypePackId tp)
247
{
248
std::optional<TypePackId> result = substitute(tp);
249
if (result)
250
{
251
return *result;
252
}
253
else
254
{
255
module->errors.emplace_back(module->scopes[0].first, UnificationTooComplex{});
256
return builtinTypes->errorTypePack;
257
}
258
}
259
260
TypeFun cloneTypeFun(const TypeFun& tf)
261
{
262
std::vector<GenericTypeDefinition> typeParams;
263
std::vector<GenericTypePackDefinition> typePackParams;
264
265
for (GenericTypeDefinition typeParam : tf.typeParams)
266
{
267
TypeId ty = cloneType(typeParam.ty);
268
std::optional<TypeId> defaultValue;
269
270
if (typeParam.defaultValue)
271
defaultValue = cloneType(*typeParam.defaultValue);
272
273
typeParams.push_back(GenericTypeDefinition{ty, defaultValue});
274
}
275
276
for (GenericTypePackDefinition typePackParam : tf.typePackParams)
277
{
278
TypePackId tp = cloneTypePack(typePackParam.tp);
279
std::optional<TypePackId> defaultValue;
280
281
if (typePackParam.defaultValue)
282
defaultValue = cloneTypePack(*typePackParam.defaultValue);
283
284
typePackParams.push_back(GenericTypePackDefinition{tp, defaultValue});
285
}
286
287
TypeId type = cloneType(tf.type);
288
289
return TypeFun{std::move(typeParams), std::move(typePackParams), type, tf.definitionLocation};
290
}
291
};
292
293
Module::~Module()
294
{
295
unfreeze(interfaceTypes);
296
unfreeze(internalTypes);
297
}
298
299
void Module::clonePublicInterface(NotNull<BuiltinTypes> builtinTypes, InternalErrorReporter& ice, SolverMode mode)
300
{
301
CloneState cloneState{builtinTypes};
302
303
ScopePtr moduleScope = getModuleScope();
304
305
TypePackId returnType = moduleScope->returnType;
306
std::optional<TypePackId> varargPack = mode == SolverMode::New ? std::nullopt : moduleScope->varargPack;
307
308
TxnLog log;
309
ClonePublicInterface clonePublicInterface{&log, builtinTypes, this, mode};
310
311
returnType = clonePublicInterface.cloneTypePack(returnType);
312
313
moduleScope->returnType = returnType;
314
if (varargPack)
315
{
316
varargPack = clonePublicInterface.cloneTypePack(*varargPack);
317
moduleScope->varargPack = varargPack;
318
}
319
320
for (auto& [name, tf] : moduleScope->exportedTypeBindings)
321
{
322
tf = clonePublicInterface.cloneTypeFun(tf);
323
}
324
325
for (auto& [name, ty] : declaredGlobals)
326
{
327
ty = clonePublicInterface.cloneType(ty);
328
}
329
330
for (auto& tf : typeFunctionAliases)
331
{
332
*tf = clonePublicInterface.cloneTypeFun(*tf);
333
}
334
335
if (clonePublicInterface.internalTypeEscaped)
336
{
337
errors.emplace_back(
338
Location{}, // Not amazing but the best we can do.
339
name,
340
InternalError{"An internal type is escaping this module; please report this bug at "
341
"https://github.com/luau-lang/luau/issues"}
342
);
343
}
344
345
// Copy external stuff over to Module itself
346
this->returnType = moduleScope->returnType;
347
this->exportedTypeBindings = moduleScope->exportedTypeBindings;
348
}
349
350
bool Module::hasModuleScope() const
351
{
352
return !scopes.empty();
353
}
354
355
ScopePtr Module::getModuleScope() const
356
{
357
LUAU_ASSERT(hasModuleScope());
358
return scopes.front().second;
359
}
360
361
} // namespace Luau
362
363