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