d: Merge dmd, druntime d8e3976a58, phobos 7a6e95688
[official-gcc.git] / gcc / d / dmd / lambdacomp.d
bloba1db8d5254852adde33d3e822ba984507f3e4d51
1 /**
2 * Implements the serialization of a lambda function.
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.
8 * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved
9 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
10 * License: $(LINK2 https://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
16 module dmd.lambdacomp;
18 import core.stdc.stdio;
19 import core.stdc.string;
21 import dmd.astenums;
22 import dmd.declaration;
23 import dmd.denum;
24 import dmd.dsymbol;
25 import dmd.dsymbolsem;
26 import dmd.dtemplate;
27 import dmd.expression;
28 import dmd.func;
29 import dmd.dmangle;
30 import dmd.hdrgen;
31 import dmd.mtype;
32 import dmd.common.outbuffer;
33 import dmd.root.rmem;
34 import dmd.root.stringtable;
35 import dmd.dscope;
36 import dmd.statement;
37 import dmd.tokens;
38 import dmd.visitor;
40 enum LOG = false;
42 /**
43 * The type of the visited expression.
45 private enum ExpType
47 None,
48 EnumDecl,
49 Arg
52 /**
53 * Compares 2 lambda functions described by their serialization.
55 * Params:
56 * l1 = first lambda to be compared
57 * l2 = second lambda to be compared
58 * sc = the scope where the lambdas are compared
60 * Returns:
61 * `true` if the 2 lambda functions are equal, `false` otherwise
63 bool isSameFuncLiteral(FuncLiteralDeclaration l1, FuncLiteralDeclaration l2, Scope* sc)
65 bool result;
66 if (auto ser1 = getSerialization(l1, sc))
68 //printf("l1 serialization: %.*s\n", cast(int)ser1.length, &ser1[0]);
69 if (auto ser2 = getSerialization(l2, sc))
71 //printf("l2 serialization: %.*s\n", cast(int)ser2.length, &ser2[0]);
72 if (ser1 == ser2)
73 result = true;
74 mem.xfree(cast(void*)ser2.ptr);
76 mem.xfree(cast(void*)ser1.ptr);
78 return result;
81 /**
82 * Computes the string representation of a
83 * lambda function described by the subtree starting from a
84 * $(REF dmd, func, FuncLiteralDeclaration).
86 * Limitations: only IntegerExps, Enums and function
87 * arguments are supported in the lambda function body. The
88 * arguments may be of any type (basic types, user defined types),
89 * except template instantiations. If a function call, a local
90 * variable or a template instance is encountered, the
91 * serialization is dropped and the function is considered
92 * uncomparable.
94 * Params:
95 * fld = the starting AST node for the lambda function
96 * sc = the scope in which the lambda function is located
98 * Returns:
99 * The serialization of `fld` allocated with mem.
101 private string getSerialization(FuncLiteralDeclaration fld, Scope* sc)
103 scope serVisitor = new SerializeVisitor(fld.parent._scope);
104 fld.accept(serVisitor);
105 const len = serVisitor.buf.length;
106 if (len == 0)
107 return null;
109 return cast(string)serVisitor.buf.extractSlice();
112 private extern (C++) class SerializeVisitor : SemanticTimeTransitiveVisitor
114 private:
115 StringTable!(const(char)[]) arg_hash;
116 Scope* sc;
117 ExpType et;
118 Dsymbol d;
120 public:
121 OutBuffer buf;
122 alias visit = SemanticTimeTransitiveVisitor.visit;
124 this(Scope* sc) scope
126 this.sc = sc;
130 * Entrypoint of the SerializeVisitor.
132 * Params:
133 * fld = the lambda function for which the serialization is computed
135 override void visit(FuncLiteralDeclaration fld)
137 assert(fld.type.ty != Terror);
138 static if (LOG)
139 printf("FuncLiteralDeclaration: %s\n", fld.toChars());
141 TypeFunction tf = cast(TypeFunction) fld.type;
142 const dim = cast(uint) tf.parameterList.length;
143 // Start the serialization by printing the number of
144 // arguments the lambda has.
145 buf.printf("%d:", dim);
147 arg_hash._init(dim + 1);
148 // For each argument
149 foreach (i, fparam; tf.parameterList)
151 if (fparam.ident !is null)
153 // the variable name is introduced into a hashtable
154 // where the key is the user defined name and the
155 // value is the cannonically name (arg0, arg1 ...)
156 auto key = fparam.ident.toString();
157 OutBuffer value;
158 value.writestring("arg");
159 value.print(i);
160 arg_hash.insert(key, value.extractSlice());
161 // and the type of the variable is serialized.
162 fparam.accept(this);
166 // Now the function body can be serialized.
167 ReturnStatement rs = fld.fbody.endsWithReturnStatement();
168 if (rs && rs.exp)
170 rs.exp.accept(this);
172 else
174 buf.setsize(0);
178 override void visit(DotIdExp exp)
180 static if (LOG)
181 printf("DotIdExp: %s\n", exp.toChars());
182 if (buf.length == 0)
183 return;
185 // First we need to see what kind of expression e1 is.
186 // It might an enum member (enum.value) or the field of
187 // an argument (argX.value) if the argument is an aggregate
188 // type. This is reported through the et variable.
189 exp.e1.accept(this);
190 if (buf.length == 0)
191 return;
193 if (et == ExpType.EnumDecl)
195 Dsymbol s = d.search(exp.loc, exp.ident);
196 if (s)
198 if (auto em = s.isEnumMember())
200 em.value.accept(this);
202 et = ExpType.None;
203 d = null;
207 else if (et == ExpType.Arg)
209 buf.setsize(buf.length -1);
210 buf.writeByte('.');
211 buf.writestring(exp.ident.toString());
212 buf.writeByte('_');
216 bool checkArgument(const(char)* id)
218 // The identifier may be an argument
219 auto stringtable_value = arg_hash.lookup(id, strlen(id));
220 if (stringtable_value)
222 // In which case we need to update the serialization accordingly
223 const(char)[] gen_id = stringtable_value.value;
224 buf.write(gen_id);
225 buf.writeByte('_');
226 et = ExpType.Arg;
227 return true;
229 return false;
232 override void visit(IdentifierExp exp)
234 static if (LOG)
235 printf("IdentifierExp: %s\n", exp.toChars());
237 if (buf.length == 0)
238 return;
240 auto id = exp.ident.toChars();
242 // If it's not an argument
243 if (!checkArgument(id))
245 // we must check what the identifier expression is.
246 Dsymbol scopesym;
247 Dsymbol s = sc.search(exp.loc, exp.ident, scopesym);
248 if (s)
250 auto v = s.isVarDeclaration();
251 // If it's a VarDeclaration, it must be a manifest constant
252 if (v && (v.storage_class & STC.manifest))
254 v.getConstInitializer.accept(this);
256 else if (auto em = s.isEnumDeclaration())
258 d = em;
259 et = ExpType.EnumDecl;
261 else if (auto fd = s.isFuncDeclaration())
263 writeMangledName(fd);
265 // For anything else, the function is deemed uncomparable
266 else
268 buf.setsize(0);
271 // If it's an unknown symbol, consider the function incomparable
272 else
274 buf.setsize(0);
279 override void visit(DotVarExp exp)
281 static if (LOG)
282 printf("DotVarExp: %s, var: %s, e1: %s\n", exp.toChars(),
283 exp.var.toChars(), exp.e1.toChars());
285 exp.e1.accept(this);
286 if (buf.length == 0)
287 return;
289 buf.setsize(buf.length -1);
290 buf.writeByte('.');
291 buf.writestring(exp.var.toChars());
292 buf.writeByte('_');
295 override void visit(VarExp exp)
297 static if (LOG)
298 printf("VarExp: %s, var: %s\n", exp.toChars(), exp.var.toChars());
300 if (buf.length == 0)
301 return;
303 auto id = exp.var.ident.toChars();
304 if (!checkArgument(id))
306 buf.setsize(0);
310 // serialize function calls
311 override void visit(CallExp exp)
313 static if (LOG)
314 printf("CallExp: %s\n", exp.toChars());
316 if (buf.length == 0)
317 return;
319 if (!exp.f)
321 exp.e1.accept(this);
323 else
325 writeMangledName(exp.f);
328 buf.writeByte('(');
329 foreach (arg; *(exp.arguments))
331 arg.accept(this);
333 buf.writeByte(')');
336 override void visit(UnaExp exp)
338 if (buf.length == 0)
339 return;
341 buf.writeByte('(');
342 buf.writestring(EXPtoString(exp.op));
343 exp.e1.accept(this);
344 if (buf.length != 0)
345 buf.writestring(")_");
348 override void visit(IntegerExp exp)
350 if (buf.length == 0)
351 return;
353 buf.print(exp.toInteger());
354 buf.writeByte('_');
357 override void visit(RealExp exp)
359 if (buf.length == 0)
360 return;
362 buf.writestring(exp.toChars());
363 buf.writeByte('_');
366 override void visit(BinExp exp)
368 static if (LOG)
369 printf("BinExp: %s\n", exp.toChars());
371 if (buf.length == 0)
372 return;
374 buf.writeByte('(');
375 buf.writestring(EXPtoString(exp.op).ptr);
377 exp.e1.accept(this);
378 if (buf.length == 0)
379 return;
381 exp.e2.accept(this);
382 if (buf.length == 0)
383 return;
385 buf.writeByte(')');
388 override void visit(TypeBasic t)
390 buf.writestring(t.dstring);
391 buf.writeByte('_');
394 void writeMangledName(Dsymbol s)
396 if (s)
398 OutBuffer mangledName;
399 mangleToBuffer(s, mangledName);
400 buf.writestring(mangledName[]);
401 buf.writeByte('_');
403 else
404 buf.setsize(0);
407 private bool checkTemplateInstance(T)(T t)
408 if (is(T == TypeStruct) || is(T == TypeClass))
410 if (t.sym.parent && t.sym.parent.isTemplateInstance())
412 buf.setsize(0);
413 return true;
415 return false;
418 override void visit(TypeStruct t)
420 static if (LOG)
421 printf("TypeStruct: %s\n", t.toChars);
423 if (!checkTemplateInstance!TypeStruct(t))
424 writeMangledName(t.sym);
427 override void visit(TypeClass t)
429 static if (LOG)
430 printf("TypeClass: %s\n", t.toChars());
432 if (!checkTemplateInstance!TypeClass(t))
433 writeMangledName(t.sym);
436 override void visit(Parameter p)
438 if (p.type.ty == Tident
439 && (cast(TypeIdentifier)p.type).ident.toString().length > 3
440 && strncmp((cast(TypeIdentifier)p.type).ident.toChars(), "__T", 3) == 0)
442 buf.writestring("none_");
444 else
445 visitType(p.type);
448 override void visit(StructLiteralExp e) {
449 static if (LOG)
450 printf("StructLiteralExp: %s\n", e.toChars);
452 auto ty = cast(TypeStruct)e.stype;
453 if (ty)
455 writeMangledName(ty.sym);
456 auto dim = e.elements.length;
457 foreach (i; 0..dim)
459 auto elem = (*e.elements)[i];
460 if (elem)
461 elem.accept(this);
462 else
463 buf.writestring("null_");
466 else
467 buf.setsize(0);
470 override void visit(ArrayLiteralExp) { buf.setsize(0); }
471 override void visit(AssocArrayLiteralExp) { buf.setsize(0); }
472 override void visit(MixinExp) { buf.setsize(0); }
473 override void visit(ComplexExp) { buf.setsize(0); }
474 override void visit(DeclarationExp) { buf.setsize(0); }
475 override void visit(DefaultInitExp) { buf.setsize(0); }
476 override void visit(DsymbolExp) { buf.setsize(0); }
477 override void visit(ErrorExp) { buf.setsize(0); }
478 override void visit(FuncExp) { buf.setsize(0); }
479 override void visit(HaltExp) { buf.setsize(0); }
480 override void visit(IntervalExp) { buf.setsize(0); }
481 override void visit(IsExp) { buf.setsize(0); }
482 override void visit(NewAnonClassExp) { buf.setsize(0); }
483 override void visit(NewExp) { buf.setsize(0); }
484 override void visit(NullExp) { buf.setsize(0); }
485 override void visit(ObjcClassReferenceExp) { buf.setsize(0); }
486 override void visit(OverExp) { buf.setsize(0); }
487 override void visit(ScopeExp) { buf.setsize(0); }
488 override void visit(StringExp) { buf.setsize(0); }
489 override void visit(SymbolExp) { buf.setsize(0); }
490 override void visit(TemplateExp) { buf.setsize(0); }
491 override void visit(ThisExp) { buf.setsize(0); }
492 override void visit(TraitsExp) { buf.setsize(0); }
493 override void visit(TupleExp) { buf.setsize(0); }
494 override void visit(TypeExp) { buf.setsize(0); }
495 override void visit(TypeidExp) { buf.setsize(0); }
496 override void visit(VoidInitExp) { buf.setsize(0); }