Internally these would treat the cast same as a normal conversion from int[7] to int[], which allows code at CTFE to erroneously succeed where it would raise a SEGV at run-time.
1 |
/**
|
|
2 |
* Implements the serialization of a lambda function.
|
|
3 |
*
|
|
4 |
* The serializationis computed by visiting the abstract syntax subtree of the given lambda function.
|
|
5 |
* The serialization is a string which contains the type of the parameters and the string
|
|
6 |
* represantation of the lambda expression.
|
|
7 |
*
|
|
8 |
* Copyright: Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
|
|
9 |
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
|
|
10 |
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
|
|
11 |
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/lamdbacomp.d, _lambdacomp.d)
|
|
12 |
* Documentation: https://dlang.org/phobos/dmd_lambdacomp.html
|
|
13 |
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/lambdacomp.d
|
|
14 |
*/
|
|
15 |
|
|
16 |
module dmd.lambdacomp; |
|
17 |
|
|
18 |
import core.stdc.stdio; |
|
19 |
import core.stdc.string; |
|
20 |
|
|
21 |
import dmd.declaration; |
|
22 |
import dmd.denum; |
|
23 |
import dmd.dsymbol; |
|
24 |
import dmd.dtemplate; |
|
25 |
import dmd.expression; |
|
26 |
import dmd.func; |
|
27 |
import dmd.dmangle; |
|
28 |
import dmd.mtype; |
|
29 |
import dmd.root.outbuffer; |
|
30 |
import dmd.root.rmem; |
|
31 |
import dmd.root.stringtable; |
|
32 |
import dmd.dscope; |
|
33 |
import dmd.statement; |
|
34 |
import dmd.tokens; |
|
35 |
import dmd.visitor; |
|
36 |
|
|
37 |
enum LOG = false; |
|
38 |
|
|
39 |
/**
|
|
40 |
* The type of the visited expression.
|
|
41 |
*/
|
|
42 |
private enum ExpType |
|
43 |
{
|
|
44 |
None, |
|
45 |
EnumDecl, |
|
46 |
Arg
|
|
47 |
}
|
|
48 |
|
|
49 |
/**
|
|
50 |
* Compares 2 lambda functions described by their serialization.
|
|
51 |
*
|
|
52 |
* Params:
|
|
53 |
* l1 = first lambda to be compared
|
|
54 |
* l2 = second lambda to be compared
|
|
55 |
* sc = the scope where the lambdas are compared
|
|
56 |
*
|
|
57 |
* Returns:
|
|
58 |
* `true` if the 2 lambda functions are equal, `false` otherwise
|
|
59 |
*/
|
|
60 |
bool isSameFuncLiteral(FuncLiteralDeclaration l1, FuncLiteralDeclaration l2, Scope* sc) |
|
61 |
{
|
|
62 | 1 |
bool result; |
63 | 1 |
if (auto ser1 = getSerialization(l1, sc)) |
64 |
{
|
|
65 |
//printf("l1 serialization: %.*s\n", cast(int)ser1.length, &ser1[0]);
|
|
66 | 1 |
if (auto ser2 = getSerialization(l2, sc)) |
67 |
{
|
|
68 |
//printf("l2 serialization: %.*s\n", cast(int)ser2.length, &ser2[0]);
|
|
69 | 1 |
if (ser1 == ser2) |
70 | 1 |
result = true; |
71 | 1 |
mem.xfree(cast(void*)ser2.ptr); |
72 |
}
|
|
73 | 1 |
mem.xfree(cast(void*)ser1.ptr); |
74 |
}
|
|
75 | 1 |
return result; |
76 |
}
|
|
77 |
|
|
78 |
/**
|
|
79 |
* Computes the string representation of a
|
|
80 |
* lambda function described by the subtree starting from a
|
|
81 |
* $(REF dmd, func, FuncLiteralDeclaration).
|
|
82 |
*
|
|
83 |
* Limitations: only IntegerExps, Enums and function
|
|
84 |
* arguments are supported in the lambda function body. The
|
|
85 |
* arguments may be of any type (basic types, user defined types),
|
|
86 |
* except template instantiations. If a function call, a local
|
|
87 |
* variable or a template instance is encountered, the
|
|
88 |
* serialization is dropped and the function is considered
|
|
89 |
* uncomparable.
|
|
90 |
*
|
|
91 |
* Params:
|
|
92 |
* fld = the starting AST node for the lambda function
|
|
93 |
* sc = the scope in which the lambda function is located
|
|
94 |
*
|
|
95 |
* Returns:
|
|
96 |
* The serialization of `fld` allocated with mem.
|
|
97 |
*/
|
|
98 |
private string getSerialization(FuncLiteralDeclaration fld, Scope* sc) |
|
99 |
{
|
|
100 | 1 |
scope serVisitor = new SerializeVisitor(fld.parent._scope); |
101 | 1 |
fld.accept(serVisitor); |
102 | 1 |
const len = serVisitor.buf.length; |
103 | 1 |
if (len == 0) |
104 | 1 |
return null; |
105 |
|
|
106 | 1 |
return cast(string)serVisitor.buf.extractSlice(); |
107 |
}
|
|
108 |
|
|
109 |
private extern (C++) class SerializeVisitor : SemanticTimeTransitiveVisitor |
|
110 |
{
|
|
111 |
private: |
|
112 |
StringTable!(const(char)[]) arg_hash; |
|
113 |
Scope* sc; |
|
114 |
ExpType et; |
|
115 |
Dsymbol d; |
|
116 |
|
|
117 |
public: |
|
118 |
OutBuffer buf; |
|
119 |
alias visit = SemanticTimeTransitiveVisitor.visit; |
|
120 |
|
|
121 | 1 |
this(Scope* sc) |
122 |
{
|
|
123 | 1 |
this.sc = sc; |
124 |
}
|
|
125 |
|
|
126 |
/**
|
|
127 |
* Entrypoint of the SerializeVisitor.
|
|
128 |
*
|
|
129 |
* Params:
|
|
130 |
* fld = the lambda function for which the serialization is computed
|
|
131 |
*/
|
|
132 |
override void visit(FuncLiteralDeclaration fld) |
|
133 |
{
|
|
134 | 1 |
assert(fld.type.ty != Terror); |
135 |
static if (LOG) |
|
136 |
printf("FuncLiteralDeclaration: %s\n", fld.toChars()); |
|
137 |
|
|
138 | 1 |
TypeFunction tf = cast(TypeFunction) fld.type; |
139 | 1 |
const dim = cast(uint) tf.parameterList.length; |
140 |
// Start the serialization by printing the number of
|
|
141 |
// arguments the lambda has.
|
|
142 | 1 |
buf.printf("%d:", dim); |
143 |
|
|
144 | 1 |
arg_hash._init(dim + 1); |
145 |
// For each argument
|
|
146 | 1 |
foreach (i, fparam; tf.parameterList) |
147 |
{
|
|
148 | 1 |
if (fparam.ident !is null) |
149 |
{
|
|
150 |
// the variable name is introduced into a hashtable
|
|
151 |
// where the key is the user defined name and the
|
|
152 |
// value is the cannonically name (arg0, arg1 ...)
|
|
153 | 1 |
auto key = fparam.ident.toString(); |
154 | 1 |
OutBuffer value; |
155 | 1 |
value.writestring("arg"); |
156 | 1 |
value.print(i); |
157 | 1 |
arg_hash.insert(key, value.extractSlice()); |
158 |
// and the type of the variable is serialized.
|
|
159 | 1 |
fparam.accept(this); |
160 |
}
|
|
161 |
}
|
|
162 |
|
|
163 |
// Now the function body can be serialized.
|
|
164 | 1 |
ReturnStatement rs = fld.fbody.endsWithReturnStatement(); |
165 | 1 |
if (rs && rs.exp) |
166 |
{
|
|
167 | 1 |
rs.exp.accept(this); |
168 |
}
|
|
169 |
else
|
|
170 |
{
|
|
171 |
buf.setsize(0); |
|
172 |
}
|
|
173 |
}
|
|
174 |
|
|
175 |
override void visit(DotIdExp exp) |
|
176 |
{
|
|
177 |
static if (LOG) |
|
178 |
printf("DotIdExp: %s\n", exp.toChars()); |
|
179 | 1 |
if (buf.length == 0) |
180 |
return; |
|
181 |
|
|
182 |
// First we need to see what kind of expression e1 is.
|
|
183 |
// It might an enum member (enum.value) or the field of
|
|
184 |
// an argument (argX.value) if the argument is an aggregate
|
|
185 |
// type. This is reported through the et variable.
|
|
186 | 1 |
exp.e1.accept(this); |
187 | 1 |
if (buf.length == 0) |
188 | 1 |
return; |
189 |
|
|
190 | 1 |
if (et == ExpType.EnumDecl) |
191 |
{
|
|
192 | 1 |
Dsymbol s = d.search(exp.loc, exp.ident); |
193 | 1 |
if (s) |
194 |
{
|
|
195 | 1 |
if (auto em = s.isEnumMember()) |
196 |
{
|
|
197 | 1 |
em.value.accept(this); |
198 |
}
|
|
199 | 1 |
et = ExpType.None; |
200 | 1 |
d = null; |
201 |
}
|
|
202 |
}
|
|
203 |
|
|
204 | 1 |
else if (et == ExpType.Arg) |
205 |
{
|
|
206 | 1 |
buf.setsize(buf.length -1); |
207 | 1 |
buf.writeByte('.'); |
208 | 1 |
buf.writestring(exp.ident.toString()); |
209 | 1 |
buf.writeByte('_'); |
210 |
}
|
|
211 |
}
|
|
212 |
|
|
213 |
bool checkArgument(const(char)* id) |
|
214 |
{
|
|
215 |
// The identifier may be an argument
|
|
216 | 1 |
auto stringtable_value = arg_hash.lookup(id, strlen(id)); |
217 | 1 |
if (stringtable_value) |
218 |
{
|
|
219 |
// In which case we need to update the serialization accordingly
|
|
220 | 1 |
const(char)[] gen_id = stringtable_value.value; |
221 | 1 |
buf.write(gen_id); |
222 | 1 |
buf.writeByte('_'); |
223 | 1 |
et = ExpType.Arg; |
224 | 1 |
return true; |
225 |
}
|
|
226 | 1 |
return false; |
227 |
}
|
|
228 |
|
|
229 |
override void visit(IdentifierExp exp) |
|
230 |
{
|
|
231 |
static if (LOG) |
|
232 |
printf("IdentifierExp: %s\n", exp.toChars()); |
|
233 |
|
|
234 | 1 |
if (buf.length == 0) |
235 | 1 |
return; |
236 |
|
|
237 | 1 |
auto id = exp.ident.toChars(); |
238 |
|
|
239 |
// If it's not an argument
|
|
240 | 1 |
if (!checkArgument(id)) |
241 |
{
|
|
242 |
// we must check what the identifier expression is.
|
|
243 | 1 |
Dsymbol scopesym; |
244 | 1 |
Dsymbol s = sc.search(exp.loc, exp.ident, &scopesym); |
245 | 1 |
if (s) |
246 |
{
|
|
247 | 1 |
auto v = s.isVarDeclaration(); |
248 |
// If it's a VarDeclaration, it must be a manifest constant
|
|
249 | 1 |
if (v && (v.storage_class & STC.manifest)) |
250 |
{
|
|
251 | 1 |
v.getConstInitializer.accept(this); |
252 |
}
|
|
253 | 1 |
else if (auto em = s.isEnumDeclaration()) |
254 |
{
|
|
255 | 1 |
d = em; |
256 | 1 |
et = ExpType.EnumDecl; |
257 |
}
|
|
258 | 1 |
else if (auto fd = s.isFuncDeclaration()) |
259 |
{
|
|
260 | 1 |
writeMangledName(fd); |
261 |
}
|
|
262 |
// For anything else, the function is deemed uncomparable
|
|
263 |
else
|
|
264 |
{
|
|
265 | 1 |
buf.setsize(0); |
266 |
}
|
|
267 |
}
|
|
268 |
// If it's an unknown symbol, consider the function incomparable
|
|
269 |
else
|
|
270 |
{
|
|
271 | 1 |
buf.setsize(0); |
272 |
}
|
|
273 |
}
|
|
274 |
}
|
|
275 |
|
|
276 |
override void visit(DotVarExp exp) |
|
277 |
{
|
|
278 |
static if (LOG) |
|
279 |
printf("DotVarExp: %s, var: %s, e1: %s\n", exp.toChars(), |
|
280 |
exp.var.toChars(), exp.e1.toChars()); |
|
281 |
|
|
282 | 1 |
exp.e1.accept(this); |
283 | 1 |
if (buf.length == 0) |
284 |
return; |
|
285 |
|
|
286 | 1 |
buf.setsize(buf.length -1); |
287 | 1 |
buf.writeByte('.'); |
288 | 1 |
buf.writestring(exp.var.toChars()); |
289 | 1 |
buf.writeByte('_'); |
290 |
}
|
|
291 |
|
|
292 |
override void visit(VarExp exp) |
|
293 |
{
|
|
294 |
static if (LOG) |
|
295 |
printf("VarExp: %s, var: %s\n", exp.toChars(), exp.var.toChars()); |
|
296 |
|
|
297 | 1 |
if (buf.length == 0) |
298 |
return; |
|
299 |
|
|
300 | 1 |
auto id = exp.var.ident.toChars(); |
301 | 1 |
if (!checkArgument(id)) |
302 |
{
|
|
303 |
buf.setsize(0); |
|
304 |
}
|
|
305 |
}
|
|
306 |
|
|
307 |
// serialize function calls
|
|
308 |
override void visit(CallExp exp) |
|
309 |
{
|
|
310 |
static if (LOG) |
|
311 |
printf("CallExp: %s\n", exp.toChars()); |
|
312 |
|
|
313 | 1 |
if (buf.length == 0) |
314 |
return; |
|
315 |
|
|
316 | 1 |
if (!exp.f) |
317 |
{
|
|
318 | 1 |
exp.e1.accept(this); |
319 |
}
|
|
320 |
else
|
|
321 |
{
|
|
322 | 1 |
writeMangledName(exp.f); |
323 |
}
|
|
324 |
|
|
325 | 1 |
buf.writeByte('('); |
326 | 1 |
foreach (arg; *(exp.arguments)) |
327 |
{
|
|
328 | 1 |
arg.accept(this); |
329 |
}
|
|
330 | 1 |
buf.writeByte(')'); |
331 |
}
|
|
332 |
|
|
333 |
override void visit(UnaExp exp) |
|
334 |
{
|
|
335 | 1 |
if (buf.length == 0) |
336 |
return; |
|
337 |
|
|
338 | 1 |
buf.writeByte('('); |
339 | 1 |
buf.writestring(Token.toString(exp.op)); |
340 | 1 |
exp.e1.accept(this); |
341 | 1 |
if (buf.length != 0) |
342 | 1 |
buf.writestring(")_"); |
343 |
}
|
|
344 |
|
|
345 |
override void visit(IntegerExp exp) |
|
346 |
{
|
|
347 | 1 |
if (buf.length == 0) |
348 |
return; |
|
349 |
|
|
350 | 1 |
buf.print(exp.toInteger()); |
351 | 1 |
buf.writeByte('_'); |
352 |
}
|
|
353 |
|
|
354 |
override void visit(RealExp exp) |
|
355 |
{
|
|
356 | 1 |
if (buf.length == 0) |
357 |
return; |
|
358 |
|
|
359 | 1 |
buf.writestring(exp.toChars()); |
360 | 1 |
buf.writeByte('_'); |
361 |
}
|
|
362 |
|
|
363 |
override void visit(BinExp exp) |
|
364 |
{
|
|
365 |
static if (LOG) |
|
366 |
printf("BinExp: %s\n", exp.toChars()); |
|
367 |
|
|
368 | 1 |
if (buf.length == 0) |
369 | 1 |
return; |
370 |
|
|
371 | 1 |
buf.writeByte('('); |
372 | 1 |
buf.writestring(Token.toChars(exp.op)); |
373 |
|
|
374 | 1 |
exp.e1.accept(this); |
375 | 1 |
if (buf.length == 0) |
376 |
return; |
|
377 |
|
|
378 | 1 |
exp.e2.accept(this); |
379 | 1 |
if (buf.length == 0) |
380 | 1 |
return; |
381 |
|
|
382 | 1 |
buf.writeByte(')'); |
383 |
}
|
|
384 |
|
|
385 |
override void visit(TypeBasic t) |
|
386 |
{
|
|
387 | 1 |
buf.writestring(t.dstring); |
388 | 1 |
buf.writeByte('_'); |
389 |
}
|
|
390 |
|
|
391 |
void writeMangledName(Dsymbol s) |
|
392 |
{
|
|
393 | 1 |
if (s) |
394 |
{
|
|
395 | 1 |
OutBuffer mangledName; |
396 | 1 |
mangleToBuffer(s, &mangledName); |
397 | 1 |
buf.writestring(mangledName[]); |
398 | 1 |
buf.writeByte('_'); |
399 |
}
|
|
400 |
else
|
|
401 |
buf.setsize(0); |
|
402 |
}
|
|
403 |
|
|
404 |
private bool checkTemplateInstance(T)(T t) |
|
405 |
if (is(T == TypeStruct) || is(T == TypeClass)) |
|
406 |
{
|
|
407 | 1 |
if (t.sym.parent && t.sym.parent.isTemplateInstance()) |
408 |
{
|
|
409 | 1 |
buf.setsize(0); |
410 | 1 |
return true; |
411 |
}
|
|
412 | 1 |
return false; |
413 |
}
|
|
414 |
|
|
415 |
override void visit(TypeStruct t) |
|
416 |
{
|
|
417 |
static if (LOG) |
|
418 |
printf("TypeStruct: %s\n", t.toChars); |
|
419 |
|
|
420 | 1 |
if (!checkTemplateInstance!TypeStruct(t)) |
421 | 1 |
writeMangledName(t.sym); |
422 |
}
|
|
423 |
|
|
424 |
override void visit(TypeClass t) |
|
425 |
{
|
|
426 |
static if (LOG) |
|
427 |
printf("TypeClass: %s\n", t.toChars()); |
|
428 |
|
|
429 | 1 |
if (!checkTemplateInstance!TypeClass(t)) |
430 | 1 |
writeMangledName(t.sym); |
431 |
}
|
|
432 |
|
|
433 |
override void visit(Parameter p) |
|
434 |
{
|
|
435 | 1 |
if (p.type.ty == Tident |
436 | 1 |
&& (cast(TypeIdentifier)p.type).ident.toString().length > 3 |
437 | 1 |
&& strncmp((cast(TypeIdentifier)p.type).ident.toChars(), "__T", 3) == 0) |
438 |
{
|
|
439 | 1 |
buf.writestring("none_"); |
440 |
}
|
|
441 |
else
|
|
442 | 1 |
visitType(p.type); |
443 |
}
|
|
444 |
|
|
445 |
override void visit(StructLiteralExp e) { |
|
446 |
static if (LOG) |
|
447 |
printf("StructLiteralExp: %s\n", e.toChars); |
|
448 |
|
|
449 | 1 |
auto ty = cast(TypeStruct)e.stype; |
450 | 1 |
if (ty) |
451 |
{
|
|
452 | 1 |
writeMangledName(ty.sym); |
453 | 1 |
auto dim = e.elements.dim; |
454 | 1 |
foreach (i; 0..dim) |
455 |
{
|
|
456 | 1 |
auto elem = (*e.elements)[i]; |
457 | 1 |
if (elem) |
458 | 1 |
elem.accept(this); |
459 |
else
|
|
460 |
buf.writestring("null_"); |
|
461 |
}
|
|
462 |
}
|
|
463 |
else
|
|
464 |
buf.setsize(0); |
|
465 |
}
|
|
466 |
|
|
467 | 1 |
override void visit(ArrayLiteralExp) { buf.setsize(0); } |
468 |
override void visit(AssocArrayLiteralExp) { buf.setsize(0); } |
|
469 |
override void visit(CompileExp) { buf.setsize(0); } |
|
470 |
override void visit(ComplexExp) { buf.setsize(0); } |
|
471 |
override void visit(DeclarationExp) { buf.setsize(0); } |
|
472 |
override void visit(DefaultInitExp) { buf.setsize(0); } |
|
473 |
override void visit(DsymbolExp) { buf.setsize(0); } |
|
474 |
override void visit(ErrorExp) { buf.setsize(0); } |
|
475 |
override void visit(FuncExp) { buf.setsize(0); } |
|
476 |
override void visit(HaltExp) { buf.setsize(0); } |
|
477 |
override void visit(IntervalExp) { buf.setsize(0); } |
|
478 |
override void visit(IsExp) { buf.setsize(0); } |
|
479 |
override void visit(NewAnonClassExp) { buf.setsize(0); } |
|
480 |
override void visit(NewExp) { buf.setsize(0); } |
|
481 |
override void visit(NullExp) { buf.setsize(0); } |
|
482 |
override void visit(ObjcClassReferenceExp) { buf.setsize(0); } |
|
483 |
override void visit(OverExp) { buf.setsize(0); } |
|
484 |
override void visit(ScopeExp) { buf.setsize(0); } |
|
485 |
override void visit(StringExp) { buf.setsize(0); } |
|
486 |
override void visit(SymbolExp) { buf.setsize(0); } |
|
487 |
override void visit(TemplateExp) { buf.setsize(0); } |
|
488 |
override void visit(ThisExp) { buf.setsize(0); } |
|
489 |
override void visit(TraitsExp) { buf.setsize(0); } |
|
490 |
override void visit(TupleExp) { buf.setsize(0); } |
|
491 |
override void visit(TypeExp) { buf.setsize(0); } |
|
492 |
override void visit(TypeidExp) { buf.setsize(0); } |
|
493 |
override void visit(VoidInitExp) { buf.setsize(0); } |
|
494 |
}
|
Read our documentation on viewing source code .