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 |
* Handle introspection functionality of the `__traits()` construct.
|
|
3 |
*
|
|
4 |
* Specification: $(LINK2 https://dlang.org/spec/traits.html, Traits)
|
|
5 |
*
|
|
6 |
* Copyright: Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
|
|
7 |
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
|
|
8 |
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
|
|
9 |
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/traits.d, _traits.d)
|
|
10 |
* Documentation: https://dlang.org/phobos/dmd_traits.html
|
|
11 |
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/traits.d
|
|
12 |
*/
|
|
13 |
|
|
14 |
module dmd.traits; |
|
15 |
|
|
16 |
import core.stdc.stdio; |
|
17 |
|
|
18 |
import dmd.aggregate; |
|
19 |
import dmd.arraytypes; |
|
20 |
import dmd.astcodegen; |
|
21 |
import dmd.attrib; |
|
22 |
import dmd.canthrow; |
|
23 |
import dmd.dclass; |
|
24 |
import dmd.declaration; |
|
25 |
import dmd.denum; |
|
26 |
import dmd.dimport; |
|
27 |
import dmd.dmodule; |
|
28 |
import dmd.dscope; |
|
29 |
import dmd.dsymbol; |
|
30 |
import dmd.dsymbolsem; |
|
31 |
import dmd.dtemplate; |
|
32 |
import dmd.errors; |
|
33 |
import dmd.expression; |
|
34 |
import dmd.expressionsem; |
|
35 |
import dmd.func; |
|
36 |
import dmd.globals; |
|
37 |
import dmd.hdrgen; |
|
38 |
import dmd.id; |
|
39 |
import dmd.identifier; |
|
40 |
import dmd.mtype; |
|
41 |
import dmd.nogc; |
|
42 |
import dmd.parse; |
|
43 |
import dmd.root.array; |
|
44 |
import dmd.root.speller; |
|
45 |
import dmd.root.stringtable; |
|
46 |
import dmd.target; |
|
47 |
import dmd.tokens; |
|
48 |
import dmd.typesem; |
|
49 |
import dmd.visitor; |
|
50 |
import dmd.root.rootobject; |
|
51 |
import dmd.root.outbuffer; |
|
52 |
import dmd.root.string; |
|
53 |
|
|
54 |
enum LOGSEMANTIC = false; |
|
55 |
|
|
56 |
/************************ TraitsExp ************************************/
|
|
57 |
|
|
58 |
/**************************************
|
|
59 |
* Convert `Expression` or `Type` to corresponding `Dsymbol`, additionally
|
|
60 |
* stripping off expression contexts.
|
|
61 |
*
|
|
62 |
* Some symbol related `__traits` ignore arguments expression contexts.
|
|
63 |
* For example:
|
|
64 |
* ----
|
|
65 |
* struct S { void f() {} }
|
|
66 |
* S s;
|
|
67 |
* pragma(msg, __traits(isNested, s.f));
|
|
68 |
* // s.f is `DotVarExp`, but `__traits(isNested)`` needs a `FuncDeclaration`.
|
|
69 |
* ----
|
|
70 |
*
|
|
71 |
* This is used for that common `__traits` behavior.
|
|
72 |
*
|
|
73 |
* Input:
|
|
74 |
* oarg object to get the symbol for
|
|
75 |
* Returns:
|
|
76 |
* Dsymbol the corresponding symbol for oarg
|
|
77 |
*/
|
|
78 |
private Dsymbol getDsymbolWithoutExpCtx(RootObject oarg) |
|
79 |
{
|
|
80 | 1 |
if (auto e = isExpression(oarg)) |
81 |
{
|
|
82 | 1 |
if (e.op == TOK.dotVariable) |
83 | 1 |
return (cast(DotVarExp)e).var; |
84 | 1 |
if (e.op == TOK.dotTemplateDeclaration) |
85 |
return (cast(DotTemplateExp)e).td; |
|
86 |
}
|
|
87 | 1 |
return getDsymbol(oarg); |
88 |
}
|
|
89 |
|
|
90 |
private const StringTable!bool traitsStringTable; |
|
91 |
|
|
92 |
shared static this() |
|
93 |
{
|
|
94 |
static immutable string[] names = |
|
95 |
[
|
|
96 |
"isAbstractClass", |
|
97 |
"isArithmetic", |
|
98 |
"isAssociativeArray", |
|
99 |
"isDisabled", |
|
100 |
"isDeprecated", |
|
101 |
"isFuture", |
|
102 |
"isFinalClass", |
|
103 |
"isPOD", |
|
104 |
"isNested", |
|
105 |
"isFloating", |
|
106 |
"isIntegral", |
|
107 |
"isScalar", |
|
108 |
"isStaticArray", |
|
109 |
"isUnsigned", |
|
110 |
"isVirtualFunction", |
|
111 |
"isVirtualMethod", |
|
112 |
"isAbstractFunction", |
|
113 |
"isFinalFunction", |
|
114 |
"isOverrideFunction", |
|
115 |
"isStaticFunction", |
|
116 |
"isModule", |
|
117 |
"isPackage", |
|
118 |
"isRef", |
|
119 |
"isOut", |
|
120 |
"isLazy", |
|
121 |
"isReturnOnStack", |
|
122 |
"hasMember", |
|
123 |
"identifier", |
|
124 |
"getProtection", |
|
125 |
"parent", |
|
126 |
"child", |
|
127 |
"getLinkage", |
|
128 |
"getMember", |
|
129 |
"getOverloads", |
|
130 |
"getVirtualFunctions", |
|
131 |
"getVirtualMethods", |
|
132 |
"classInstanceSize", |
|
133 |
"allMembers", |
|
134 |
"derivedMembers", |
|
135 |
"isSame", |
|
136 |
"compiles", |
|
137 |
"parameters", |
|
138 |
"getAliasThis", |
|
139 |
"getAttributes", |
|
140 |
"getFunctionAttributes", |
|
141 |
"getFunctionVariadicStyle", |
|
142 |
"getParameterStorageClasses", |
|
143 |
"getUnitTests", |
|
144 |
"getVirtualIndex", |
|
145 |
"getPointerBitmap", |
|
146 |
"isZeroInit", |
|
147 |
"getTargetInfo", |
|
148 |
"getLocation", |
|
149 |
"hasPostblit", |
|
150 |
"hasCopyConstructor", |
|
151 |
"isCopyable", |
|
152 |
];
|
|
153 |
|
|
154 | 1 |
StringTable!(bool)* stringTable = cast(StringTable!(bool)*) &traitsStringTable; |
155 | 1 |
stringTable._init(names.length); |
156 |
|
|
157 | 1 |
foreach (s; names) |
158 |
{
|
|
159 | 1 |
auto sv = stringTable.insert(s, true); |
160 | 1 |
assert(sv); |
161 |
}
|
|
162 |
}
|
|
163 |
|
|
164 |
/**
|
|
165 |
* get an array of size_t values that indicate possible pointer words in memory
|
|
166 |
* if interpreted as the type given as argument
|
|
167 |
* Returns: the size of the type in bytes, d_uns64.max on error
|
|
168 |
*/
|
|
169 |
d_uns64 getTypePointerBitmap(Loc loc, Type t, Array!(d_uns64)* data) |
|
170 |
{
|
|
171 | 1 |
d_uns64 sz; |
172 | 1 |
if (t.ty == Tclass && !(cast(TypeClass)t).sym.isInterfaceDeclaration()) |
173 | 1 |
sz = (cast(TypeClass)t).sym.AggregateDeclaration.size(loc); |
174 |
else
|
|
175 | 1 |
sz = t.size(loc); |
176 | 1 |
if (sz == SIZE_INVALID) |
177 |
return d_uns64.max; |
|
178 |
|
|
179 | 1 |
const sz_size_t = Type.tsize_t.size(loc); |
180 | 1 |
if (sz > sz.max - sz_size_t) |
181 |
{
|
|
182 |
error(loc, "size overflow for type `%s`", t.toChars()); |
|
183 |
return d_uns64.max; |
|
184 |
}
|
|
185 |
|
|
186 | 1 |
d_uns64 bitsPerWord = sz_size_t * 8; |
187 | 1 |
d_uns64 cntptr = (sz + sz_size_t - 1) / sz_size_t; |
188 | 1 |
d_uns64 cntdata = (cntptr + bitsPerWord - 1) / bitsPerWord; |
189 |
|
|
190 | 1 |
data.setDim(cast(size_t)cntdata); |
191 | 1 |
data.zero(); |
192 |
|
|
193 |
extern (C++) final class PointerBitmapVisitor : Visitor |
|
194 |
{
|
|
195 |
alias visit = Visitor.visit; |
|
196 |
public: |
|
197 | 1 |
extern (D) this(Array!(d_uns64)* _data, d_uns64 _sz_size_t) |
198 |
{
|
|
199 | 1 |
this.data = _data; |
200 | 1 |
this.sz_size_t = _sz_size_t; |
201 |
}
|
|
202 |
|
|
203 |
void setpointer(d_uns64 off) |
|
204 |
{
|
|
205 | 1 |
d_uns64 ptroff = off / sz_size_t; |
206 | 1 |
(*data)[cast(size_t)(ptroff / (8 * sz_size_t))] |= 1L << (ptroff % (8 * sz_size_t)); |
207 |
}
|
|
208 |
|
|
209 |
override void visit(Type t) |
|
210 |
{
|
|
211 | 1 |
Type tb = t.toBasetype(); |
212 | 1 |
if (tb != t) |
213 | 1 |
tb.accept(this); |
214 |
}
|
|
215 |
|
|
216 |
override void visit(TypeError t) |
|
217 |
{
|
|
218 |
visit(cast(Type)t); |
|
219 |
}
|
|
220 |
|
|
221 |
override void visit(TypeNext t) |
|
222 |
{
|
|
223 |
assert(0); |
|
224 |
}
|
|
225 |
|
|
226 |
override void visit(TypeBasic t) |
|
227 |
{
|
|
228 | 1 |
if (t.ty == Tvoid) |
229 | 1 |
setpointer(offset); |
230 |
}
|
|
231 |
|
|
232 |
override void visit(TypeVector t) |
|
233 |
{
|
|
234 |
}
|
|
235 |
|
|
236 |
override void visit(TypeArray t) |
|
237 |
{
|
|
238 |
assert(0); |
|
239 |
}
|
|
240 |
|
|
241 |
override void visit(TypeSArray t) |
|
242 |
{
|
|
243 | 1 |
d_uns64 arrayoff = offset; |
244 | 1 |
d_uns64 nextsize = t.next.size(); |
245 | 1 |
if (nextsize == SIZE_INVALID) |
246 |
error = true; |
|
247 | 1 |
d_uns64 dim = t.dim.toInteger(); |
248 | 1 |
for (d_uns64 i = 0; i < dim; i++) |
249 |
{
|
|
250 | 1 |
offset = arrayoff + i * nextsize; |
251 | 1 |
t.next.accept(this); |
252 |
}
|
|
253 | 1 |
offset = arrayoff; |
254 |
}
|
|
255 |
|
|
256 |
override void visit(TypeDArray t) |
|
257 |
{
|
|
258 | 1 |
setpointer(offset + sz_size_t); |
259 |
}
|
|
260 |
|
|
261 |
// dynamic array is {length,ptr}
|
|
262 |
override void visit(TypeAArray t) |
|
263 |
{
|
|
264 | 1 |
setpointer(offset); |
265 |
}
|
|
266 |
|
|
267 |
override void visit(TypePointer t) |
|
268 |
{
|
|
269 | 1 |
if (t.nextOf().ty != Tfunction) // don't mark function pointers |
270 | 1 |
setpointer(offset); |
271 |
}
|
|
272 |
|
|
273 |
override void visit(TypeReference t) |
|
274 |
{
|
|
275 |
setpointer(offset); |
|
276 |
}
|
|
277 |
|
|
278 |
override void visit(TypeClass t) |
|
279 |
{
|
|
280 | 1 |
setpointer(offset); |
281 |
}
|
|
282 |
|
|
283 |
override void visit(TypeFunction t) |
|
284 |
{
|
|
285 |
}
|
|
286 |
|
|
287 |
override void visit(TypeDelegate t) |
|
288 |
{
|
|
289 | 1 |
setpointer(offset); |
290 |
}
|
|
291 |
|
|
292 |
// delegate is {context, function}
|
|
293 |
override void visit(TypeQualified t) |
|
294 |
{
|
|
295 |
assert(0); |
|
296 |
}
|
|
297 |
|
|
298 |
// assume resolved
|
|
299 |
override void visit(TypeIdentifier t) |
|
300 |
{
|
|
301 |
assert(0); |
|
302 |
}
|
|
303 |
|
|
304 |
override void visit(TypeInstance t) |
|
305 |
{
|
|
306 |
assert(0); |
|
307 |
}
|
|
308 |
|
|
309 |
override void visit(TypeTypeof t) |
|
310 |
{
|
|
311 |
assert(0); |
|
312 |
}
|
|
313 |
|
|
314 |
override void visit(TypeReturn t) |
|
315 |
{
|
|
316 |
assert(0); |
|
317 |
}
|
|
318 |
|
|
319 |
override void visit(TypeEnum t) |
|
320 |
{
|
|
321 | 1 |
visit(cast(Type)t); |
322 |
}
|
|
323 |
|
|
324 |
override void visit(TypeTuple t) |
|
325 |
{
|
|
326 |
visit(cast(Type)t); |
|
327 |
}
|
|
328 |
|
|
329 |
override void visit(TypeSlice t) |
|
330 |
{
|
|
331 |
assert(0); |
|
332 |
}
|
|
333 |
|
|
334 |
override void visit(TypeNull t) |
|
335 |
{
|
|
336 |
// always a null pointer
|
|
337 |
}
|
|
338 |
|
|
339 |
override void visit(TypeStruct t) |
|
340 |
{
|
|
341 | 1 |
d_uns64 structoff = offset; |
342 | 1 |
foreach (v; t.sym.fields) |
343 |
{
|
|
344 | 1 |
offset = structoff + v.offset; |
345 | 1 |
if (v.type.ty == Tclass) |
346 | 1 |
setpointer(offset); |
347 |
else
|
|
348 | 1 |
v.type.accept(this); |
349 |
}
|
|
350 | 1 |
offset = structoff; |
351 |
}
|
|
352 |
|
|
353 |
// a "toplevel" class is treated as an instance, while TypeClass fields are treated as references
|
|
354 |
void visitClass(TypeClass t) |
|
355 |
{
|
|
356 | 1 |
d_uns64 classoff = offset; |
357 |
// skip vtable-ptr and monitor
|
|
358 | 1 |
if (t.sym.baseClass) |
359 | 1 |
visitClass(cast(TypeClass)t.sym.baseClass.type); |
360 | 1 |
foreach (v; t.sym.fields) |
361 |
{
|
|
362 | 1 |
offset = classoff + v.offset; |
363 | 1 |
v.type.accept(this); |
364 |
}
|
|
365 | 1 |
offset = classoff; |
366 |
}
|
|
367 |
|
|
368 |
Array!(d_uns64)* data; |
|
369 |
d_uns64 offset; |
|
370 |
d_uns64 sz_size_t; |
|
371 |
bool error; |
|
372 |
}
|
|
373 |
|
|
374 | 1 |
scope PointerBitmapVisitor pbv = new PointerBitmapVisitor(data, sz_size_t); |
375 | 1 |
if (t.ty == Tclass) |
376 | 1 |
pbv.visitClass(cast(TypeClass)t); |
377 |
else
|
|
378 | 1 |
t.accept(pbv); |
379 | 1 |
return pbv.error ? d_uns64.max : sz; |
380 |
}
|
|
381 |
|
|
382 |
/**
|
|
383 |
* get an array of size_t values that indicate possible pointer words in memory
|
|
384 |
* if interpreted as the type given as argument
|
|
385 |
* the first array element is the size of the type for independent interpretation
|
|
386 |
* of the array
|
|
387 |
* following elements bits represent one word (4/8 bytes depending on the target
|
|
388 |
* architecture). If set the corresponding memory might contain a pointer/reference.
|
|
389 |
*
|
|
390 |
* Returns: [T.sizeof, pointerbit0-31/63, pointerbit32/64-63/128, ...]
|
|
391 |
*/
|
|
392 |
private Expression pointerBitmap(TraitsExp e) |
|
393 |
{
|
|
394 | 1 |
if (!e.args || e.args.dim != 1) |
395 |
{
|
|
396 | 1 |
error(e.loc, "a single type expected for trait pointerBitmap"); |
397 | 1 |
return ErrorExp.get(); |
398 |
}
|
|
399 |
|
|
400 | 1 |
Type t = getType((*e.args)[0]); |
401 | 1 |
if (!t) |
402 |
{
|
|
403 |
error(e.loc, "`%s` is not a type", (*e.args)[0].toChars()); |
|
404 |
return ErrorExp.get(); |
|
405 |
}
|
|
406 |
|
|
407 | 1 |
Array!(d_uns64) data; |
408 | 1 |
d_uns64 sz = getTypePointerBitmap(e.loc, t, &data); |
409 | 1 |
if (sz == d_uns64.max) |
410 |
return ErrorExp.get(); |
|
411 |
|
|
412 | 1 |
auto exps = new Expressions(data.dim + 1); |
413 | 1 |
(*exps)[0] = new IntegerExp(e.loc, sz, Type.tsize_t); |
414 | 1 |
foreach (size_t i; 1 .. exps.dim) |
415 | 1 |
(*exps)[i] = new IntegerExp(e.loc, data[cast(size_t) (i - 1)], Type.tsize_t); |
416 |
|
|
417 | 1 |
auto ale = new ArrayLiteralExp(e.loc, Type.tsize_t.sarrayOf(data.dim + 1), exps); |
418 | 1 |
return ale; |
419 |
}
|
|
420 |
|
|
421 |
Expression semanticTraits(TraitsExp e, Scope* sc) |
|
422 |
{
|
|
423 |
static if (LOGSEMANTIC) |
|
424 |
{
|
|
425 |
printf("TraitsExp::semantic() %s\n", e.toChars()); |
|
426 |
}
|
|
427 |
|
|
428 | 1 |
if (e.ident != Id.compiles && |
429 | 1 |
e.ident != Id.isSame && |
430 | 1 |
e.ident != Id.identifier && |
431 | 1 |
e.ident != Id.getProtection && |
432 | 1 |
e.ident != Id.getAttributes) |
433 |
{
|
|
434 |
// Pretend we're in a deprecated scope so that deprecation messages
|
|
435 |
// aren't triggered when checking if a symbol is deprecated
|
|
436 | 1 |
const save = sc.stc; |
437 | 1 |
if (e.ident == Id.isDeprecated) |
438 | 1 |
sc.stc |= STC.deprecated_; |
439 | 1 |
if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 1)) |
440 |
{
|
|
441 | 1 |
sc.stc = save; |
442 | 1 |
return ErrorExp.get(); |
443 |
}
|
|
444 | 1 |
sc.stc = save; |
445 |
}
|
|
446 | 1 |
size_t dim = e.args ? e.args.dim : 0; |
447 |
|
|
448 |
Expression dimError(int expected) |
|
449 |
{
|
|
450 | 1 |
e.error("expected %d arguments for `%s` but had %d", expected, e.ident.toChars(), cast(int)dim); |
451 | 1 |
return ErrorExp.get(); |
452 |
}
|
|
453 |
|
|
454 |
static IntegerExp True() |
|
455 |
{
|
|
456 | 1 |
return IntegerExp.createBool(true); |
457 |
}
|
|
458 |
|
|
459 |
static IntegerExp False() |
|
460 |
{
|
|
461 | 1 |
return IntegerExp.createBool(false); |
462 |
}
|
|
463 |
|
|
464 |
/********
|
|
465 |
* Gets the function type from a given AST node
|
|
466 |
* if the node is a function of some sort.
|
|
467 |
* Params:
|
|
468 |
* o = an AST node to check for a `TypeFunction`
|
|
469 |
* fdp = if `o` is a FuncDeclaration then fdp is set to that, otherwise `null`
|
|
470 |
* Returns:
|
|
471 |
* a type node if `o` is a declaration of
|
|
472 |
* a delegate, function, function-pointer or a variable of the former.
|
|
473 |
* Otherwise, `null`.
|
|
474 |
*/
|
|
475 |
static TypeFunction toTypeFunction(RootObject o, out FuncDeclaration fdp) |
|
476 |
{
|
|
477 | 1 |
Type t; |
478 | 1 |
if (auto s = getDsymbolWithoutExpCtx(o)) |
479 |
{
|
|
480 | 1 |
if (auto fd = s.isFuncDeclaration()) |
481 |
{
|
|
482 | 1 |
t = fd.type; |
483 | 1 |
fdp = fd; |
484 |
}
|
|
485 | 1 |
else if (auto vd = s.isVarDeclaration()) |
486 | 1 |
t = vd.type; |
487 |
else
|
|
488 | 1 |
t = isType(o); |
489 |
}
|
|
490 |
else
|
|
491 | 1 |
t = isType(o); |
492 |
|
|
493 | 1 |
if (t) |
494 |
{
|
|
495 | 1 |
if (t.ty == Tfunction) |
496 | 1 |
return cast(TypeFunction)t; |
497 | 1 |
else if (t.ty == Tdelegate) |
498 | 1 |
return cast(TypeFunction)t.nextOf(); |
499 | 1 |
else if (t.ty == Tpointer && t.nextOf().ty == Tfunction) |
500 | 1 |
return cast(TypeFunction)t.nextOf(); |
501 |
}
|
|
502 |
|
|
503 | 1 |
return null; |
504 |
}
|
|
505 |
|
|
506 |
IntegerExp isX(T)(bool delegate(T) fp) |
|
507 |
{
|
|
508 | 1 |
if (!dim) |
509 | 1 |
return False(); |
510 | 1 |
foreach (o; *e.args) |
511 |
{
|
|
512 |
static if (is(T == Type)) |
|
513 | 1 |
auto y = getType(o); |
514 |
|
|
515 |
static if (is(T : Dsymbol)) |
|
516 |
{
|
|
517 | 1 |
auto s = getDsymbolWithoutExpCtx(o); |
518 | 1 |
if (!s) |
519 | 1 |
return False(); |
520 |
}
|
|
521 |
static if (is(T == Dsymbol)) |
|
522 |
alias y = s; |
|
523 |
static if (is(T == Declaration)) |
|
524 | 1 |
auto y = s.isDeclaration(); |
525 |
static if (is(T == FuncDeclaration)) |
|
526 | 1 |
auto y = s.isFuncDeclaration(); |
527 |
static if (is(T == EnumMember)) |
|
528 |
auto y = s.isEnumMember(); |
|
529 |
|
|
530 | 1 |
if (!y || !fp(y)) |
531 | 1 |
return False(); |
532 |
}
|
|
533 | 1 |
return True(); |
534 |
}
|
|
535 |
|
|
536 |
alias isTypeX = isX!Type; |
|
537 |
alias isDsymX = isX!Dsymbol; |
|
538 |
alias isDeclX = isX!Declaration; |
|
539 |
alias isFuncX = isX!FuncDeclaration; |
|
540 |
alias isEnumMemX = isX!EnumMember; |
|
541 |
|
|
542 |
Expression isPkgX(bool function(Package) fp) |
|
543 |
{
|
|
544 | 1 |
return isDsymX((Dsymbol sym) { |
545 | 1 |
Package p = resolveIsPackage(sym); |
546 | 1 |
return (p !is null) && fp(p); |
547 |
});
|
|
548 |
}
|
|
549 |
|
|
550 | 1 |
if (e.ident == Id.isArithmetic) |
551 |
{
|
|
552 | 1 |
return isTypeX(t => t.isintegral() || t.isfloating()); |
553 |
}
|
|
554 | 1 |
if (e.ident == Id.isFloating) |
555 |
{
|
|
556 | 1 |
return isTypeX(t => t.isfloating()); |
557 |
}
|
|
558 | 1 |
if (e.ident == Id.isIntegral) |
559 |
{
|
|
560 | 1 |
return isTypeX(t => t.isintegral()); |
561 |
}
|
|
562 | 1 |
if (e.ident == Id.isScalar) |
563 |
{
|
|
564 | 1 |
return isTypeX(t => t.isscalar()); |
565 |
}
|
|
566 | 1 |
if (e.ident == Id.isUnsigned) |
567 |
{
|
|
568 | 1 |
return isTypeX(t => t.isunsigned()); |
569 |
}
|
|
570 | 1 |
if (e.ident == Id.isAssociativeArray) |
571 |
{
|
|
572 | 1 |
return isTypeX(t => t.toBasetype().ty == Taarray); |
573 |
}
|
|
574 | 1 |
if (e.ident == Id.isDeprecated) |
575 |
{
|
|
576 | 1 |
if (global.params.vcomplex) |
577 |
{
|
|
578 | 1 |
if (isTypeX(t => t.iscomplex() || t.isimaginary()).isBool(true)) |
579 | 1 |
return True(); |
580 |
}
|
|
581 | 1 |
return isDsymX(t => t.isDeprecated()); |
582 |
}
|
|
583 | 1 |
if (e.ident == Id.isFuture) |
584 |
{
|
|
585 | 1 |
return isDeclX(t => t.isFuture()); |
586 |
}
|
|
587 | 1 |
if (e.ident == Id.isStaticArray) |
588 |
{
|
|
589 | 1 |
return isTypeX(t => t.toBasetype().ty == Tsarray); |
590 |
}
|
|
591 | 1 |
if (e.ident == Id.isAbstractClass) |
592 |
{
|
|
593 | 1 |
return isTypeX(t => t.toBasetype().ty == Tclass && |
594 | 1 |
(cast(TypeClass)t.toBasetype()).sym.isAbstract()); |
595 |
}
|
|
596 | 1 |
if (e.ident == Id.isFinalClass) |
597 |
{
|
|
598 | 1 |
return isTypeX(t => t.toBasetype().ty == Tclass && |
599 | 1 |
((cast(TypeClass)t.toBasetype()).sym.storage_class & STC.final_) != 0); |
600 |
}
|
|
601 | 1 |
if (e.ident == Id.isTemplate) |
602 |
{
|
|
603 | 1 |
if (dim != 1) |
604 |
return dimError(1); |
|
605 |
|
|
606 | 1 |
return isDsymX((s) |
607 |
{
|
|
608 | 1 |
if (!s.toAlias().isOverloadable()) |
609 | 1 |
return false; |
610 | 1 |
return overloadApply(s, |
611 | 1 |
sm => sm.isTemplateDeclaration() !is null) != 0; |
612 |
});
|
|
613 |
}
|
|
614 | 1 |
if (e.ident == Id.isPOD) |
615 |
{
|
|
616 | 1 |
if (dim != 1) |
617 | 1 |
return dimError(1); |
618 |
|
|
619 | 1 |
auto o = (*e.args)[0]; |
620 | 1 |
auto t = isType(o); |
621 | 1 |
if (!t) |
622 |
{
|
|
623 | 1 |
e.error("type expected as second argument of __traits `%s` instead of `%s`", |
624 |
e.ident.toChars(), o.toChars()); |
|
625 | 1 |
return ErrorExp.get(); |
626 |
}
|
|
627 |
|
|
628 | 1 |
Type tb = t.baseElemOf(); |
629 | 1 |
if (auto sd = tb.ty == Tstruct ? (cast(TypeStruct)tb).sym : null) |
630 |
{
|
|
631 | 1 |
return sd.isPOD() ? True() : False(); |
632 |
}
|
|
633 | 1 |
return True(); |
634 |
}
|
|
635 | 1 |
if (e.ident == Id.hasCopyConstructor || e.ident == Id.hasPostblit) |
636 |
{
|
|
637 | 1 |
if (dim != 1) |
638 | 1 |
return dimError(1); |
639 |
|
|
640 | 1 |
auto o = (*e.args)[0]; |
641 | 1 |
auto t = isType(o); |
642 | 1 |
if (!t) |
643 |
{
|
|
644 | 1 |
e.error("type expected as second argument of __traits `%s` instead of `%s`", |
645 |
e.ident.toChars(), o.toChars()); |
|
646 | 1 |
return ErrorExp.get(); |
647 |
}
|
|
648 |
|
|
649 | 1 |
Type tb = t.baseElemOf(); |
650 | 1 |
if (auto sd = tb.ty == Tstruct ? (cast(TypeStruct)tb).sym : null) |
651 |
{
|
|
652 | 1 |
return (e.ident == Id.hasPostblit) ? (sd.postblit ? True() : False()) |
653 | 1 |
: (sd.hasCopyCtor ? True() : False()); |
654 |
}
|
|
655 | 1 |
return False(); |
656 |
}
|
|
657 | 1 |
if (e.ident == Id.isCopyable) |
658 |
{
|
|
659 | 1 |
if (dim != 1) |
660 |
return dimError(1); |
|
661 |
|
|
662 | 1 |
auto o = (*e.args)[0]; |
663 | 1 |
auto t = isType(o); |
664 | 1 |
if (!t) |
665 |
{
|
|
666 |
e.error("type expected as second argument of __traits `%s` instead of `%s`", |
|
667 |
e.ident.toChars(), o.toChars()); |
|
668 |
return ErrorExp.get(); |
|
669 |
}
|
|
670 |
|
|
671 | 1 |
t = t.toBasetype(); // get the base in case `t` is an `enum` |
672 |
|
|
673 | 1 |
if (auto ts = t.isTypeStruct()) |
674 |
{
|
|
675 | 1 |
ts.sym.dsymbolSemantic(sc); |
676 |
}
|
|
677 |
|
|
678 | 1 |
return isCopyable(t) ? True() : False(); |
679 |
}
|
|
680 |
|
|
681 | 1 |
if (e.ident == Id.isNested) |
682 |
{
|
|
683 | 1 |
if (dim != 1) |
684 | 1 |
return dimError(1); |
685 |
|
|
686 | 1 |
auto o = (*e.args)[0]; |
687 | 1 |
auto s = getDsymbolWithoutExpCtx(o); |
688 | 1 |
if (!s) |
689 |
{
|
|
690 |
}
|
|
691 | 1 |
else if (auto ad = s.isAggregateDeclaration()) |
692 |
{
|
|
693 | 1 |
return ad.isNested() ? True() : False(); |
694 |
}
|
|
695 | 1 |
else if (auto fd = s.isFuncDeclaration()) |
696 |
{
|
|
697 | 1 |
return fd.isNested() ? True() : False(); |
698 |
}
|
|
699 |
|
|
700 | 1 |
e.error("aggregate or function expected instead of `%s`", o.toChars()); |
701 | 1 |
return ErrorExp.get(); |
702 |
}
|
|
703 | 1 |
if (e.ident == Id.isDisabled) |
704 |
{
|
|
705 | 1 |
if (dim != 1) |
706 |
return dimError(1); |
|
707 |
|
|
708 | 1 |
return isDeclX(f => f.isDisabled()); |
709 |
}
|
|
710 | 1 |
if (e.ident == Id.isAbstractFunction) |
711 |
{
|
|
712 | 1 |
if (dim != 1) |
713 | 1 |
return dimError(1); |
714 |
|
|
715 | 1 |
return isFuncX(f => f.isAbstract()); |
716 |
}
|
|
717 | 1 |
if (e.ident == Id.isVirtualFunction) |
718 |
{
|
|
719 | 1 |
if (dim != 1) |
720 | 1 |
return dimError(1); |
721 |
|
|
722 | 1 |
return isFuncX(f => f.isVirtual()); |
723 |
}
|
|
724 | 1 |
if (e.ident == Id.isVirtualMethod) |
725 |
{
|
|
726 | 1 |
if (dim != 1) |
727 | 1 |
return dimError(1); |
728 |
|
|
729 | 1 |
return isFuncX(f => f.isVirtualMethod()); |
730 |
}
|
|
731 | 1 |
if (e.ident == Id.isFinalFunction) |
732 |
{
|
|
733 | 1 |
if (dim != 1) |
734 | 1 |
return dimError(1); |
735 |
|
|
736 | 1 |
return isFuncX(f => f.isFinalFunc()); |
737 |
}
|
|
738 | 1 |
if (e.ident == Id.isOverrideFunction) |
739 |
{
|
|
740 | 1 |
if (dim != 1) |
741 | 1 |
return dimError(1); |
742 |
|
|
743 | 1 |
return isFuncX(f => f.isOverride()); |
744 |
}
|
|
745 | 1 |
if (e.ident == Id.isStaticFunction) |
746 |
{
|
|
747 | 1 |
if (dim != 1) |
748 | 1 |
return dimError(1); |
749 |
|
|
750 | 1 |
return isFuncX(f => !f.needThis() && !f.isNested()); |
751 |
}
|
|
752 | 1 |
if (e.ident == Id.isModule) |
753 |
{
|
|
754 | 1 |
if (dim != 1) |
755 | 1 |
return dimError(1); |
756 |
|
|
757 | 1 |
return isPkgX(p => p.isModule() || p.isPackageMod()); |
758 |
}
|
|
759 | 1 |
if (e.ident == Id.isPackage) |
760 |
{
|
|
761 | 1 |
if (dim != 1) |
762 | 1 |
return dimError(1); |
763 |
|
|
764 | 1 |
return isPkgX(p => p.isModule() is null); |
765 |
}
|
|
766 | 1 |
if (e.ident == Id.isRef) |
767 |
{
|
|
768 | 1 |
if (dim != 1) |
769 | 1 |
return dimError(1); |
770 |
|
|
771 | 1 |
return isDeclX(d => d.isRef()); |
772 |
}
|
|
773 | 1 |
if (e.ident == Id.isOut) |
774 |
{
|
|
775 | 1 |
if (dim != 1) |
776 | 1 |
return dimError(1); |
777 |
|
|
778 | 1 |
return isDeclX(d => d.isOut()); |
779 |
}
|
|
780 | 1 |
if (e.ident == Id.isLazy) |
781 |
{
|
|
782 | 1 |
if (dim != 1) |
783 | 1 |
return dimError(1); |
784 |
|
|
785 | 1 |
return isDeclX(d => (d.storage_class & STC.lazy_) != 0); |
786 |
}
|
|
787 | 1 |
if (e.ident == Id.identifier) |
788 |
{
|
|
789 |
// Get identifier for symbol as a string literal
|
|
790 |
/* Specify 0 for bit 0 of the flags argument to semanticTiargs() so that
|
|
791 |
* a symbol should not be folded to a constant.
|
|
792 |
* Bit 1 means don't convert Parameter to Type if Parameter has an identifier
|
|
793 |
*/
|
|
794 | 1 |
if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 2)) |
795 |
return ErrorExp.get(); |
|
796 | 1 |
if (dim != 1) |
797 | 1 |
return dimError(1); |
798 |
|
|
799 | 1 |
auto o = (*e.args)[0]; |
800 | 1 |
Identifier id; |
801 | 1 |
if (auto po = isParameter(o)) |
802 |
{
|
|
803 | 1 |
if (!po.ident) |
804 |
{
|
|
805 |
e.error("argument `%s` has no identifier", po.type.toChars()); |
|
806 |
return ErrorExp.get(); |
|
807 |
}
|
|
808 | 1 |
id = po.ident; |
809 |
}
|
|
810 |
else
|
|
811 |
{
|
|
812 | 1 |
Dsymbol s = getDsymbolWithoutExpCtx(o); |
813 | 1 |
if (!s || !s.ident) |
814 |
{
|
|
815 |
e.error("argument `%s` has no identifier", o.toChars()); |
|
816 |
return ErrorExp.get(); |
|
817 |
}
|
|
818 | 1 |
id = s.ident; |
819 |
}
|
|
820 |
|
|
821 | 1 |
auto se = new StringExp(e.loc, id.toString()); |
822 | 1 |
return se.expressionSemantic(sc); |
823 |
}
|
|
824 | 1 |
if (e.ident == Id.getProtection) |
825 |
{
|
|
826 | 1 |
if (dim != 1) |
827 | 1 |
return dimError(1); |
828 |
|
|
829 | 1 |
Scope* sc2 = sc.push(); |
830 | 1 |
sc2.flags = sc.flags | SCOPE.noaccesscheck | SCOPE.ignoresymbolvisibility; |
831 | 1 |
bool ok = TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1); |
832 | 1 |
sc2.pop(); |
833 | 1 |
if (!ok) |
834 |
return ErrorExp.get(); |
|
835 |
|
|
836 | 1 |
auto o = (*e.args)[0]; |
837 | 1 |
auto s = getDsymbolWithoutExpCtx(o); |
838 | 1 |
if (!s) |
839 |
{
|
|
840 | 1 |
if (!isError(o)) |
841 | 1 |
e.error("argument `%s` has no protection", o.toChars()); |
842 | 1 |
return ErrorExp.get(); |
843 |
}
|
|
844 | 1 |
if (s.semanticRun == PASS.init) |
845 | 1 |
s.dsymbolSemantic(null); |
846 |
|
|
847 | 1 |
auto protName = protectionToString(s.prot().kind); // TODO: How about package(names) |
848 | 1 |
assert(protName); |
849 | 1 |
auto se = new StringExp(e.loc, protName); |
850 | 1 |
return se.expressionSemantic(sc); |
851 |
}
|
|
852 | 1 |
if (e.ident == Id.parent) |
853 |
{
|
|
854 | 1 |
if (dim != 1) |
855 | 1 |
return dimError(1); |
856 |
|
|
857 | 1 |
auto o = (*e.args)[0]; |
858 | 1 |
auto s = getDsymbolWithoutExpCtx(o); |
859 | 1 |
if (s) |
860 |
{
|
|
861 |
// https://issues.dlang.org/show_bug.cgi?id=12496
|
|
862 |
// Consider:
|
|
863 |
// class T1
|
|
864 |
// {
|
|
865 |
// class C(uint value) { }
|
|
866 |
// }
|
|
867 |
// __traits(parent, T1.C!2)
|
|
868 | 1 |
if (auto ad = s.isAggregateDeclaration()) // `s` is `C` |
869 |
{
|
|
870 | 1 |
if (ad.isNested()) // `C` is nested |
871 |
{
|
|
872 | 1 |
if (auto p = s.toParent()) // `C`'s parent is `C!2`, believe it or not |
873 |
{
|
|
874 | 1 |
if (p.isTemplateInstance()) // `C!2` is a template instance |
875 |
{
|
|
876 | 1 |
s = p; // `C!2`'s parent is `T1` |
877 | 1 |
auto td = (cast(TemplateInstance)p).tempdecl; |
878 | 1 |
if (td) |
879 | 1 |
s = td; // get the declaration context just in case there's two contexts |
880 |
}
|
|
881 |
}
|
|
882 |
}
|
|
883 |
}
|
|
884 |
|
|
885 | 1 |
if (auto fd = s.isFuncDeclaration()) // https://issues.dlang.org/show_bug.cgi?id=8943 |
886 | 1 |
s = fd.toAliasFunc(); |
887 | 1 |
if (!s.isImport()) // https://issues.dlang.org/show_bug.cgi?id=8922 |
888 | 1 |
s = s.toParent(); |
889 |
}
|
|
890 | 1 |
if (!s || s.isImport()) |
891 |
{
|
|
892 | 1 |
e.error("argument `%s` has no parent", o.toChars()); |
893 | 1 |
return ErrorExp.get(); |
894 |
}
|
|
895 |
|
|
896 | 1 |
if (auto f = s.isFuncDeclaration()) |
897 |
{
|
|
898 | 1 |
if (auto td = getFuncTemplateDecl(f)) |
899 |
{
|
|
900 | 1 |
if (td.overroot) // if not start of overloaded list of TemplateDeclaration's |
901 |
td = td.overroot; // then get the start |
|
902 | 1 |
Expression ex = new TemplateExp(e.loc, td, f); |
903 | 1 |
ex = ex.expressionSemantic(sc); |
904 | 1 |
return ex; |
905 |
}
|
|
906 | 1 |
if (auto fld = f.isFuncLiteralDeclaration()) |
907 |
{
|
|
908 |
// Directly translate to VarExp instead of FuncExp
|
|
909 | 1 |
Expression ex = new VarExp(e.loc, fld, true); |
910 | 1 |
return ex.expressionSemantic(sc); |
911 |
}
|
|
912 |
}
|
|
913 | 1 |
return symbolToExp(s, e.loc, sc, false); |
914 |
}
|
|
915 | 1 |
if (e.ident == Id.child) |
916 |
{
|
|
917 | 1 |
if (dim != 2) |
918 | 1 |
return dimError(2); |
919 |
|
|
920 | 1 |
Expression ex; |
921 | 1 |
auto op = (*e.args)[0]; |
922 | 1 |
if (auto symp = getDsymbol(op)) |
923 | 1 |
ex = new DsymbolExp(e.loc, symp); |
924 | 1 |
else if (auto exp = op.isExpression()) |
925 | 1 |
ex = exp; |
926 |
else
|
|
927 |
{
|
|
928 | 1 |
e.error("symbol or expression expected as first argument of __traits `child` instead of `%s`", op.toChars()); |
929 | 1 |
return ErrorExp.get(); |
930 |
}
|
|
931 |
|
|
932 | 1 |
ex = ex.expressionSemantic(sc); |
933 | 1 |
auto oc = (*e.args)[1]; |
934 | 1 |
auto symc = getDsymbol(oc); |
935 | 1 |
if (!symc) |
936 |
{
|
|
937 | 1 |
e.error("symbol expected as second argument of __traits `child` instead of `%s`", oc.toChars()); |
938 | 1 |
return ErrorExp.get(); |
939 |
}
|
|
940 |
|
|
941 | 1 |
if (auto d = symc.isDeclaration()) |
942 | 1 |
ex = new DotVarExp(e.loc, ex, d); |
943 | 1 |
else if (auto td = symc.isTemplateDeclaration()) |
944 | 1 |
ex = new DotExp(e.loc, ex, new TemplateExp(e.loc, td)); |
945 | 1 |
else if (auto ti = symc.isScopeDsymbol()) |
946 | 1 |
ex = new DotExp(e.loc, ex, new ScopeExp(e.loc, ti)); |
947 |
else
|
|
948 |
assert(0); |
|
949 |
|
|
950 | 1 |
ex = ex.expressionSemantic(sc); |
951 | 1 |
return ex; |
952 |
}
|
|
953 | 1 |
if (e.ident == Id.hasMember || |
954 | 1 |
e.ident == Id.getMember || |
955 | 1 |
e.ident == Id.getOverloads || |
956 | 1 |
e.ident == Id.getVirtualMethods || |
957 | 1 |
e.ident == Id.getVirtualFunctions) |
958 |
{
|
|
959 | 1 |
if (dim != 2 && !(dim == 3 && e.ident == Id.getOverloads)) |
960 | 1 |
return dimError(2); |
961 |
|
|
962 | 1 |
auto o = (*e.args)[0]; |
963 | 1 |
auto ex = isExpression((*e.args)[1]); |
964 | 1 |
if (!ex) |
965 |
{
|
|
966 |
e.error("expression expected as second argument of __traits `%s`", e.ident.toChars()); |
|
967 |
return ErrorExp.get(); |
|
968 |
}
|
|
969 | 1 |
ex = ex.ctfeInterpret(); |
970 |
|
|
971 | 1 |
bool includeTemplates = false; |
972 | 1 |
if (dim == 3 && e.ident == Id.getOverloads) |
973 |
{
|
|
974 | 1 |
auto b = isExpression((*e.args)[2]); |
975 | 1 |
b = b.ctfeInterpret(); |
976 | 1 |
if (!b.type.equals(Type.tbool)) |
977 |
{
|
|
978 | 1 |
e.error("`bool` expected as third argument of `__traits(getOverloads)`, not `%s` of type `%s`", b.toChars(), b.type.toChars()); |
979 | 1 |
return ErrorExp.get(); |
980 |
}
|
|
981 | 1 |
includeTemplates = b.isBool(true); |
982 |
}
|
|
983 |
|
|
984 | 1 |
StringExp se = ex.toStringExp(); |
985 | 1 |
if (!se || se.len == 0) |
986 |
{
|
|
987 |
e.error("string expected as second argument of __traits `%s` instead of `%s`", e.ident.toChars(), ex.toChars()); |
|
988 |
return ErrorExp.get(); |
|
989 |
}
|
|
990 | 1 |
se = se.toUTF8(sc); |
991 |
|
|
992 | 1 |
if (se.sz != 1) |
993 |
{
|
|
994 |
e.error("string must be chars"); |
|
995 |
return ErrorExp.get(); |
|
996 |
}
|
|
997 | 1 |
auto id = Identifier.idPool(se.peekString()); |
998 |
|
|
999 |
/* Prefer a Type, because getDsymbol(Type) can lose type modifiers.
|
|
1000 |
Then a Dsymbol, because it might need some runtime contexts.
|
|
1001 |
*/
|
|
1002 |
|
|
1003 | 1 |
Dsymbol sym = getDsymbol(o); |
1004 | 1 |
if (auto t = isType(o)) |
1005 | 1 |
ex = typeDotIdExp(e.loc, t, id); |
1006 | 1 |
else if (sym) |
1007 |
{
|
|
1008 | 1 |
if (e.ident == Id.hasMember) |
1009 |
{
|
|
1010 | 1 |
if (auto sm = sym.search(e.loc, id)) |
1011 | 1 |
return True(); |
1012 |
}
|
|
1013 | 1 |
ex = new DsymbolExp(e.loc, sym); |
1014 | 1 |
ex = new DotIdExp(e.loc, ex, id); |
1015 |
}
|
|
1016 | 1 |
else if (auto ex2 = isExpression(o)) |
1017 | 1 |
ex = new DotIdExp(e.loc, ex2, id); |
1018 |
else
|
|
1019 |
{
|
|
1020 |
e.error("invalid first argument"); |
|
1021 |
return ErrorExp.get(); |
|
1022 |
}
|
|
1023 |
|
|
1024 |
// ignore symbol visibility and disable access checks for these traits
|
|
1025 | 1 |
Scope* scx = sc.push(); |
1026 | 1 |
scx.flags |= SCOPE.ignoresymbolvisibility | SCOPE.noaccesscheck; |
1027 | 1 |
scope (exit) scx.pop(); |
1028 |
|
|
1029 | 1 |
if (e.ident == Id.hasMember) |
1030 |
{
|
|
1031 |
/* Take any errors as meaning it wasn't found
|
|
1032 |
*/
|
|
1033 | 1 |
ex = ex.trySemantic(scx); |
1034 | 1 |
return ex ? True() : False(); |
1035 |
}
|
|
1036 | 1 |
else if (e.ident == Id.getMember) |
1037 |
{
|
|
1038 | 1 |
if (ex.op == TOK.dotIdentifier) |
1039 |
// Prevent semantic() from replacing Symbol with its initializer
|
|
1040 | 1 |
(cast(DotIdExp)ex).wantsym = true; |
1041 | 1 |
ex = ex.expressionSemantic(scx); |
1042 | 1 |
return ex; |
1043 |
}
|
|
1044 | 1 |
else if (e.ident == Id.getVirtualFunctions || |
1045 | 1 |
e.ident == Id.getVirtualMethods || |
1046 | 1 |
e.ident == Id.getOverloads) |
1047 |
{
|
|
1048 | 1 |
uint errors = global.errors; |
1049 | 1 |
Expression eorig = ex; |
1050 | 1 |
ex = ex.expressionSemantic(scx); |
1051 | 1 |
if (errors < global.errors) |
1052 | 1 |
e.error("`%s` cannot be resolved", eorig.toChars()); |
1053 |
|
|
1054 |
/* Create tuple of functions of ex
|
|
1055 |
*/
|
|
1056 | 1 |
auto exps = new Expressions(); |
1057 | 1 |
Dsymbol f; |
1058 | 1 |
if (ex.op == TOK.variable) |
1059 |
{
|
|
1060 | 1 |
VarExp ve = cast(VarExp)ex; |
1061 | 1 |
f = ve.var.isFuncDeclaration(); |
1062 | 1 |
ex = null; |
1063 |
}
|
|
1064 | 1 |
else if (ex.op == TOK.dotVariable) |
1065 |
{
|
|
1066 | 1 |
DotVarExp dve = cast(DotVarExp)ex; |
1067 | 1 |
f = dve.var.isFuncDeclaration(); |
1068 | 1 |
if (dve.e1.op == TOK.dotType || dve.e1.op == TOK.this_) |
1069 | 1 |
ex = null; |
1070 |
else
|
|
1071 | 1 |
ex = dve.e1; |
1072 |
}
|
|
1073 | 1 |
else if (ex.op == TOK.template_) |
1074 |
{
|
|
1075 | 1 |
VarExp ve = cast(VarExp)ex; |
1076 | 1 |
auto td = ve.var.isTemplateDeclaration(); |
1077 | 1 |
f = td; |
1078 | 1 |
if (td && td.funcroot) |
1079 | 1 |
f = td.funcroot; |
1080 | 1 |
ex = null; |
1081 |
}
|
|
1082 | 1 |
else if (ex.op == TOK.dotTemplateDeclaration) |
1083 |
{
|
|
1084 | 1 |
DotTemplateExp dte = cast(DotTemplateExp)ex; |
1085 | 1 |
auto td = dte.td; |
1086 | 1 |
f = td; |
1087 | 1 |
if (td && td.funcroot) |
1088 | 1 |
f = td.funcroot; |
1089 | 1 |
ex = null; |
1090 | 1 |
if (dte.e1.op != TOK.dotType && dte.e1.op != TOK.this_) |
1091 | 1 |
ex = dte.e1; |
1092 |
}
|
|
1093 | 1 |
bool[string] funcTypeHash; |
1094 |
|
|
1095 |
/* Compute the function signature and insert it in the
|
|
1096 |
* hashtable, if not present. This is needed so that
|
|
1097 |
* traits(getOverlods, F3, "visit") does not count `int visit(int)`
|
|
1098 |
* twice in the following example:
|
|
1099 |
*
|
|
1100 |
* =============================================
|
|
1101 |
* interface F1 { int visit(int);}
|
|
1102 |
* interface F2 { int visit(int); void visit(); }
|
|
1103 |
* interface F3 : F2, F1 {}
|
|
1104 |
*==============================================
|
|
1105 |
*/
|
|
1106 |
void insertInterfaceInheritedFunction(FuncDeclaration fd, Expression e) |
|
1107 |
{
|
|
1108 | 1 |
auto signature = fd.type.toString(); |
1109 |
//printf("%s - %s\n", fd.toChars, signature);
|
|
1110 | 1 |
if (signature !in funcTypeHash) |
1111 |
{
|
|
1112 | 1 |
funcTypeHash[signature] = true; |
1113 | 1 |
exps.push(e); |
1114 |
}
|
|
1115 |
}
|
|
1116 |
|
|
1117 |
int dg(Dsymbol s) |
|
1118 |
{
|
|
1119 | 1 |
auto fd = s.isFuncDeclaration(); |
1120 | 1 |
if (!fd) |
1121 |
{
|
|
1122 | 1 |
if (includeTemplates) |
1123 |
{
|
|
1124 | 1 |
if (auto td = s.isTemplateDeclaration()) |
1125 |
{
|
|
1126 |
// if td is part of an overload set we must take a copy
|
|
1127 |
// which shares the same `instances` cache but without
|
|
1128 |
// `overroot` and `overnext` set to avoid overload
|
|
1129 |
// behaviour in the result.
|
|
1130 | 1 |
if (td.overnext !is null) |
1131 |
{
|
|
1132 | 1 |
if (td.instances is null) |
1133 |
{
|
|
1134 |
// create an empty AA just to copy it
|
|
1135 | 1 |
scope ti = new TemplateInstance(Loc.initial, Id.empty, null); |
1136 | 1 |
auto tib = TemplateInstanceBox(ti); |
1137 | 1 |
td.instances[tib] = null; |
1138 | 1 |
td.instances.clear(); |
1139 |
}
|
|
1140 | 1 |
td = cast(TemplateDeclaration) td.syntaxCopy(null); |
1141 |
import core.stdc.string : memcpy; |
|
1142 | 1 |
memcpy(cast(void*) td, cast(void*) s, |
1143 |
__traits(classInstanceSize, TemplateDeclaration)); |
|
1144 | 1 |
td.overroot = null; |
1145 | 1 |
td.overnext = null; |
1146 |
}
|
|
1147 | 1 |
exps.push(new DsymbolExp(Loc.initial, td, false)); |
1148 |
}
|
|
1149 |
}
|
|
1150 | 1 |
return 0; |
1151 |
}
|
|
1152 | 1 |
if (e.ident == Id.getVirtualFunctions && !fd.isVirtual()) |
1153 |
return 0; |
|
1154 | 1 |
if (e.ident == Id.getVirtualMethods && !fd.isVirtualMethod()) |
1155 | 1 |
return 0; |
1156 |
|
|
1157 | 1 |
auto fa = new FuncAliasDeclaration(fd.ident, fd, false); |
1158 | 1 |
fa.protection = fd.protection; |
1159 |
|
|
1160 | 1 |
auto e = ex ? new DotVarExp(Loc.initial, ex, fa, false) |
1161 | 1 |
: new DsymbolExp(Loc.initial, fa, false); |
1162 |
|
|
1163 |
// if the parent is an interface declaration
|
|
1164 |
// we must check for functions with the same signature
|
|
1165 |
// in different inherited interfaces
|
|
1166 | 1 |
if (sym && sym.isInterfaceDeclaration()) |
1167 | 1 |
insertInterfaceInheritedFunction(fd, e); |
1168 |
else
|
|
1169 | 1 |
exps.push(e); |
1170 | 1 |
return 0; |
1171 |
}
|
|
1172 |
|
|
1173 | 1 |
InterfaceDeclaration ifd = null; |
1174 | 1 |
if (sym) |
1175 | 1 |
ifd = sym.isInterfaceDeclaration(); |
1176 |
// If the symbol passed as a parameter is an
|
|
1177 |
// interface that inherits other interfaces
|
|
1178 | 1 |
overloadApply(f, &dg); |
1179 | 1 |
if (ifd && ifd.interfaces && f) |
1180 |
{
|
|
1181 |
// check the overloads of each inherited interface individually
|
|
1182 | 1 |
foreach (bc; ifd.interfaces) |
1183 |
{
|
|
1184 | 1 |
if (auto fd = bc.sym.search(e.loc, f.ident)) |
1185 | 1 |
overloadApply(fd, &dg); |
1186 |
}
|
|
1187 |
}
|
|
1188 |
|
|
1189 | 1 |
auto tup = new TupleExp(e.loc, exps); |
1190 | 1 |
return tup.expressionSemantic(scx); |
1191 |
}
|
|
1192 |
else
|
|
1193 |
assert(0); |
|
1194 |
}
|
|
1195 | 1 |
if (e.ident == Id.classInstanceSize) |
1196 |
{
|
|
1197 | 1 |
if (dim != 1) |
1198 | 1 |
return dimError(1); |
1199 |
|
|
1200 | 1 |
auto o = (*e.args)[0]; |
1201 | 1 |
auto s = getDsymbol(o); |
1202 | 1 |
auto cd = s ? s.isClassDeclaration() : null; |
1203 | 1 |
if (!cd) |
1204 |
{
|
|
1205 |
e.error("first argument is not a class"); |
|
1206 |
return ErrorExp.get(); |
|
1207 |
}
|
|
1208 | 1 |
if (cd.sizeok != Sizeok.done) |
1209 |
{
|
|
1210 | 1 |
cd.size(e.loc); |
1211 |
}
|
|
1212 | 1 |
if (cd.sizeok != Sizeok.done) |
1213 |
{
|
|
1214 |
e.error("%s `%s` is forward referenced", cd.kind(), cd.toChars()); |
|
1215 |
return ErrorExp.get(); |
|
1216 |
}
|
|
1217 |
|
|
1218 | 1 |
return new IntegerExp(e.loc, cd.structsize, Type.tsize_t); |
1219 |
}
|
|
1220 | 1 |
if (e.ident == Id.getAliasThis) |
1221 |
{
|
|
1222 | 1 |
if (dim != 1) |
1223 | 1 |
return dimError(1); |
1224 |
|
|
1225 | 1 |
auto o = (*e.args)[0]; |
1226 | 1 |
auto s = getDsymbol(o); |
1227 | 1 |
auto ad = s ? s.isAggregateDeclaration() : null; |
1228 |
|
|
1229 | 1 |
auto exps = new Expressions(); |
1230 | 1 |
if (ad && ad.aliasthis) |
1231 | 1 |
exps.push(new StringExp(e.loc, ad.aliasthis.ident.toString())); |
1232 | 1 |
Expression ex = new TupleExp(e.loc, exps); |
1233 | 1 |
ex = ex.expressionSemantic(sc); |
1234 | 1 |
return ex; |
1235 |
}
|
|
1236 | 1 |
if (e.ident == Id.getAttributes) |
1237 |
{
|
|
1238 |
/* Specify 0 for bit 0 of the flags argument to semanticTiargs() so that
|
|
1239 |
* a symbol should not be folded to a constant.
|
|
1240 |
* Bit 1 means don't convert Parameter to Type if Parameter has an identifier
|
|
1241 |
*/
|
|
1242 | 1 |
if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 3)) |
1243 | 1 |
return ErrorExp.get(); |
1244 |
|
|
1245 | 1 |
if (dim != 1) |
1246 | 1 |
return dimError(1); |
1247 |
|
|
1248 | 1 |
auto o = (*e.args)[0]; |
1249 | 1 |
auto po = isParameter(o); |
1250 | 1 |
auto s = getDsymbolWithoutExpCtx(o); |
1251 | 1 |
UserAttributeDeclaration udad = null; |
1252 | 1 |
if (po) |
1253 |
{
|
|
1254 | 1 |
udad = po.userAttribDecl; |
1255 |
}
|
|
1256 | 1 |
else if (s) |
1257 |
{
|
|
1258 | 1 |
if (s.isImport()) |
1259 |
{
|
|
1260 |
s = s.isImport().mod; |
|
1261 |
}
|
|
1262 |
//printf("getAttributes %s, attrs = %p, scope = %p\n", s.toChars(), s.userAttribDecl, s.scope);
|
|
1263 | 1 |
udad = s.userAttribDecl; |
1264 |
}
|
|
1265 |
else
|
|
1266 |
{
|
|
1267 |
version (none) |
|
1268 |
{
|
|
1269 |
Expression x = isExpression(o); |
|
1270 |
Type t = isType(o); |
|
1271 |
if (x) |
|
1272 |
printf("e = %s %s\n", Token.toChars(x.op), x.toChars()); |
|
1273 |
if (t) |
|
1274 |
printf("t = %d %s\n", t.ty, t.toChars()); |
|
1275 |
}
|
|
1276 | 1 |
e.error("first argument is not a symbol"); |
1277 | 1 |
return ErrorExp.get(); |
1278 |
}
|
|
1279 |
|
|
1280 | 1 |
auto exps = udad ? udad.getAttributes() : new Expressions(); |
1281 | 1 |
auto tup = new TupleExp(e.loc, exps); |
1282 | 1 |
return tup.expressionSemantic(sc); |
1283 |
}
|
|
1284 | 1 |
if (e.ident == Id.getFunctionAttributes) |
1285 |
{
|
|
1286 |
/* Extract all function attributes as a tuple (const/shared/inout/pure/nothrow/etc) except UDAs.
|
|
1287 |
* https://dlang.org/spec/traits.html#getFunctionAttributes
|
|
1288 |
*/
|
|
1289 | 1 |
if (dim != 1) |
1290 | 1 |
return dimError(1); |
1291 |
|
|
1292 | 1 |
FuncDeclaration fd; |
1293 | 1 |
TypeFunction tf = toTypeFunction((*e.args)[0], fd); |
1294 |
|
|
1295 | 1 |
if (!tf) |
1296 |
{
|
|
1297 |
e.error("first argument is not a function"); |
|
1298 |
return ErrorExp.get(); |
|
1299 |
}
|
|
1300 |
|
|
1301 | 1 |
auto mods = new Expressions(); |
1302 |
|
|
1303 |
void addToMods(string str) |
|
1304 |
{
|
|
1305 | 1 |
mods.push(new StringExp(Loc.initial, str)); |
1306 |
}
|
|
1307 | 1 |
tf.modifiersApply(&addToMods); |
1308 | 1 |
tf.attributesApply(&addToMods, TRUSTformatSystem); |
1309 |
|
|
1310 | 1 |
auto tup = new TupleExp(e.loc, mods); |
1311 | 1 |
return tup.expressionSemantic(sc); |
1312 |
}
|
|
1313 | 1 |
if (e.ident == Id.isReturnOnStack) |
1314 |
{
|
|
1315 |
/* Extract as a boolean if function return value is on the stack
|
|
1316 |
* https://dlang.org/spec/traits.html#isReturnOnStack
|
|
1317 |
*/
|
|
1318 | 1 |
if (dim != 1) |
1319 | 1 |
return dimError(1); |
1320 |
|
|
1321 | 1 |
RootObject o = (*e.args)[0]; |
1322 | 1 |
FuncDeclaration fd; |
1323 | 1 |
TypeFunction tf = toTypeFunction(o, fd); |
1324 |
|
|
1325 | 1 |
if (!tf) |
1326 |
{
|
|
1327 | 1 |
e.error("argument to `__traits(isReturnOnStack, %s)` is not a function", o.toChars()); |
1328 | 1 |
return ErrorExp.get(); |
1329 |
}
|
|
1330 |
|
|
1331 | 1 |
bool value = target.isReturnOnStack(tf, fd && fd.needThis()); |
1332 | 1 |
return IntegerExp.createBool(value); |
1333 |
}
|
|
1334 | 1 |
if (e.ident == Id.getFunctionVariadicStyle) |
1335 |
{
|
|
1336 |
/* Accept a symbol or a type. Returns one of the following:
|
|
1337 |
* "none" not a variadic function
|
|
1338 |
* "argptr" extern(D) void dstyle(...), use `__argptr` and `__arguments`
|
|
1339 |
* "stdarg" extern(C) void cstyle(int, ...), use core.stdc.stdarg
|
|
1340 |
* "typesafe" void typesafe(T[] ...)
|
|
1341 |
*/
|
|
1342 |
// get symbol linkage as a string
|
|
1343 | 1 |
if (dim != 1) |
1344 |
return dimError(1); |
|
1345 |
|
|
1346 | 1 |
LINK link; |
1347 | 1 |
VarArg varargs; |
1348 | 1 |
auto o = (*e.args)[0]; |
1349 |
|
|
1350 | 1 |
FuncDeclaration fd; |
1351 | 1 |
TypeFunction tf = toTypeFunction(o, fd); |
1352 |
|
|
1353 | 1 |
if (tf) |
1354 |
{
|
|
1355 | 1 |
link = tf.linkage; |
1356 | 1 |
varargs = tf.parameterList.varargs; |
1357 |
}
|
|
1358 |
else
|
|
1359 |
{
|
|
1360 | 1 |
if (!fd) |
1361 |
{
|
|
1362 | 1 |
e.error("argument to `__traits(getFunctionVariadicStyle, %s)` is not a function", o.toChars()); |
1363 | 1 |
return ErrorExp.get(); |
1364 |
}
|
|
1365 |
link = fd.linkage; |
|
1366 |
varargs = fd.getParameterList().varargs; |
|
1367 |
}
|
|
1368 | 1 |
string style; |
1369 | 1 |
final switch (varargs) |
1370 |
{
|
|
1371 | 1 |
case VarArg.none: style = "none"; break; |
1372 | 1 |
case VarArg.variadic: style = (link == LINK.d) |
1373 | 1 |
? "argptr" |
1374 | 1 |
: "stdarg"; break; |
1375 | 1 |
case VarArg.typesafe: style = "typesafe"; break; |
1376 |
}
|
|
1377 | 1 |
auto se = new StringExp(e.loc, style); |
1378 | 1 |
return se.expressionSemantic(sc); |
1379 |
}
|
|
1380 | 1 |
if (e.ident == Id.getParameterStorageClasses) |
1381 |
{
|
|
1382 |
/* Accept a function symbol or a type, followed by a parameter index.
|
|
1383 |
* Returns a tuple of strings of the parameter's storage classes.
|
|
1384 |
*/
|
|
1385 |
// get symbol linkage as a string
|
|
1386 | 1 |
if (dim != 2) |
1387 | 1 |
return dimError(2); |
1388 |
|
|
1389 | 1 |
auto o = (*e.args)[0]; |
1390 | 1 |
auto o1 = (*e.args)[1]; |
1391 |
|
|
1392 | 1 |
FuncDeclaration fd; |
1393 | 1 |
TypeFunction tf = toTypeFunction(o, fd); |
1394 |
|
|
1395 | 1 |
ParameterList fparams; |
1396 | 1 |
if (tf) |
1397 | 1 |
fparams = tf.parameterList; |
1398 | 1 |
else if (fd) |
1399 |
fparams = fd.getParameterList(); |
|
1400 |
else
|
|
1401 |
{
|
|
1402 | 1 |
e.error("first argument to `__traits(getParameterStorageClasses, %s, %s)` is not a function", |
1403 |
o.toChars(), o1.toChars()); |
|
1404 | 1 |
return ErrorExp.get(); |
1405 |
}
|
|
1406 |
|
|
1407 | 1 |
StorageClass stc; |
1408 |
|
|
1409 |
// Set stc to storage class of the ith parameter
|
|
1410 | 1 |
auto ex = isExpression((*e.args)[1]); |
1411 | 1 |
if (!ex) |
1412 |
{
|
|
1413 | 1 |
e.error("expression expected as second argument of `__traits(getParameterStorageClasses, %s, %s)`", |
1414 |
o.toChars(), o1.toChars()); |
|
1415 | 1 |
return ErrorExp.get(); |
1416 |
}
|
|
1417 | 1 |
ex = ex.ctfeInterpret(); |
1418 | 1 |
auto ii = ex.toUInteger(); |
1419 | 1 |
if (ii >= fparams.length) |
1420 |
{
|
|
1421 | 1 |
e.error("parameter index must be in range 0..%u not %s", cast(uint)fparams.length, ex.toChars()); |
1422 | 1 |
return ErrorExp.get(); |
1423 |
}
|
|
1424 |
|
|
1425 | 1 |
uint n = cast(uint)ii; |
1426 | 1 |
Parameter p = fparams[n]; |
1427 | 1 |
stc = p.storageClass; |
1428 |
|
|
1429 |
// This mirrors hdrgen.visit(Parameter p)
|
|
1430 | 1 |
if (p.type && p.type.mod & MODFlags.shared_) |
1431 |
stc &= ~STC.shared_; |
|
1432 |
|
|
1433 | 1 |
auto exps = new Expressions; |
1434 |
|
|
1435 |
void push(string s) |
|
1436 |
{
|
|
1437 | 1 |
exps.push(new StringExp(e.loc, s)); |
1438 |
}
|
|
1439 |
|
|
1440 | 1 |
if (stc & STC.auto_) |
1441 |
push("auto"); |
|
1442 | 1 |
if (stc & STC.return_) |
1443 | 1 |
push("return"); |
1444 |
|
|
1445 | 1 |
if (stc & STC.out_) |
1446 | 1 |
push("out"); |
1447 | 1 |
else if (stc & STC.ref_) |
1448 | 1 |
push("ref"); |
1449 | 1 |
else if (stc & STC.in_) |
1450 | 1 |
push("in"); |
1451 | 1 |
else if (stc & STC.lazy_) |
1452 | 1 |
push("lazy"); |
1453 | 1 |
else if (stc & STC.alias_) |
1454 |
push("alias"); |
|
1455 |
|
|
1456 | 1 |
if (stc & STC.const_) |
1457 |
push("const"); |
|
1458 | 1 |
if (stc & STC.immutable_) |
1459 |
push("immutable"); |
|
1460 | 1 |
if (stc & STC.wild) |
1461 |
push("inout"); |
|
1462 | 1 |
if (stc & STC.shared_) |
1463 |
push("shared"); |
|
1464 | 1 |
if (stc & STC.scope_ && !(stc & STC.scopeinferred)) |
1465 | 1 |
push("scope"); |
1466 |
|
|
1467 | 1 |
auto tup = new TupleExp(e.loc, exps); |
1468 | 1 |
return tup.expressionSemantic(sc); |
1469 |
}
|
|
1470 | 1 |
if (e.ident == Id.getLinkage) |
1471 |
{
|
|
1472 |
// get symbol linkage as a string
|
|
1473 | 1 |
if (dim != 1) |
1474 | 1 |
return dimError(1); |
1475 |
|
|
1476 | 1 |
LINK link; |
1477 | 1 |
auto o = (*e.args)[0]; |
1478 |
|
|
1479 | 1 |
FuncDeclaration fd; |
1480 | 1 |
TypeFunction tf = toTypeFunction(o, fd); |
1481 |
|
|
1482 | 1 |
if (tf) |
1483 | 1 |
link = tf.linkage; |
1484 |
else
|
|
1485 |
{
|
|
1486 | 1 |
auto s = getDsymbol(o); |
1487 | 1 |
Declaration d; |
1488 | 1 |
AggregateDeclaration agg; |
1489 | 1 |
if (!s || ((d = s.isDeclaration()) is null && (agg = s.isAggregateDeclaration()) is null)) |
1490 |
{
|
|
1491 | 1 |
e.error("argument to `__traits(getLinkage, %s)` is not a declaration", o.toChars()); |
1492 | 1 |
return ErrorExp.get(); |
1493 |
}
|
|
1494 |
|
|
1495 | 1 |
if (d !is null) |
1496 | 1 |
link = d.linkage; |
1497 |
else
|
|
1498 |
{
|
|
1499 |
// Resolves forward references
|
|
1500 | 1 |
if (agg.sizeok != Sizeok.done) |
1501 |
{
|
|
1502 | 1 |
agg.size(e.loc); |
1503 | 1 |
if (agg.sizeok != Sizeok.done) |
1504 |
{
|
|
1505 |
e.error("%s `%s` is forward referenced", agg.kind(), agg.toChars()); |
|
1506 |
return ErrorExp.get(); |
|
1507 |
}
|
|
1508 |
}
|
|
1509 |
|
|
1510 | 1 |
final switch (agg.classKind) |
1511 |
{
|
|
1512 | 1 |
case ClassKind.d: |
1513 | 1 |
link = LINK.d; |
1514 | 1 |
break; |
1515 | 1 |
case ClassKind.cpp: |
1516 | 1 |
link = LINK.cpp; |
1517 | 1 |
break; |
1518 |
case ClassKind.objc: |
|
1519 |
link = LINK.objc; |
|
1520 |
break; |
|
1521 |
}
|
|
1522 |
}
|
|
1523 |
}
|
|
1524 | 1 |
auto linkage = linkageToChars(link); |
1525 | 1 |
auto se = new StringExp(e.loc, linkage.toDString()); |
1526 | 1 |
return se.expressionSemantic(sc); |
1527 |
}
|
|
1528 | 1 |
if (e.ident == Id.allMembers || |
1529 | 1 |
e.ident == Id.derivedMembers) |
1530 |
{
|
|
1531 | 1 |
if (dim != 1) |
1532 | 1 |
return dimError(1); |
1533 |
|
|
1534 | 1 |
auto o = (*e.args)[0]; |
1535 | 1 |
auto s = getDsymbol(o); |
1536 | 1 |
if (!s) |
1537 |
{
|
|
1538 | 1 |
e.error("In expression `%s` `%s` can't have members", e.toChars(), o.toChars()); |
1539 | 1 |
e.errorSupplemental("`%s` must evaluate to either a module, a struct, an union, a class, an interface or a template instantiation", o.toChars()); |
1540 |
|
|
1541 | 1 |
return ErrorExp.get(); |
1542 |
}
|
|
1543 | 1 |
if (auto imp = s.isImport()) |
1544 |
{
|
|
1545 |
// https://issues.dlang.org/show_bug.cgi?id=9692
|
|
1546 | 1 |
s = imp.mod; |
1547 |
}
|
|
1548 |
|
|
1549 | 1 |
auto sds = s.isScopeDsymbol(); |
1550 | 1 |
if (!sds || sds.isTemplateDeclaration()) |
1551 |
{
|
|
1552 | 1 |
e.error("In expression `%s` %s `%s` has no members", e.toChars(), s.kind(), s.toChars()); |
1553 | 1 |
e.errorSupplemental("`%s` must evaluate to either a module, a struct, an union, a class, an interface or a template instantiation", s.toChars()); |
1554 | 1 |
return ErrorExp.get(); |
1555 |
}
|
|
1556 |
|
|
1557 | 1 |
auto idents = new Identifiers(); |
1558 |
|
|
1559 |
int pushIdentsDg(size_t n, Dsymbol sm) |
|
1560 |
{
|
|
1561 | 1 |
if (!sm) |
1562 |
return 1; |
|
1563 |
|
|
1564 |
// skip local symbols, such as static foreach loop variables
|
|
1565 | 1 |
if (auto decl = sm.isDeclaration()) |
1566 |
{
|
|
1567 | 1 |
if (decl.storage_class & STC.local) |
1568 |
{
|
|
1569 | 1 |
return 0; |
1570 |
}
|
|
1571 |
}
|
|
1572 |
|
|
1573 |
// https://issues.dlang.org/show_bug.cgi?id=20915
|
|
1574 |
// skip version and debug identifiers
|
|
1575 | 1 |
if (sm.isVersionSymbol() || sm.isDebugSymbol()) |
1576 | 1 |
return 0; |
1577 |
|
|
1578 |
//printf("\t[%i] %s %s\n", i, sm.kind(), sm.toChars());
|
|
1579 | 1 |
if (sm.ident) |
1580 |
{
|
|
1581 |
// https://issues.dlang.org/show_bug.cgi?id=10096
|
|
1582 |
// https://issues.dlang.org/show_bug.cgi?id=10100
|
|
1583 |
// Skip over internal members in __traits(allMembers)
|
|
1584 | 1 |
if ((sm.isCtorDeclaration() && sm.ident != Id.ctor) || |
1585 | 1 |
(sm.isDtorDeclaration() && sm.ident != Id.dtor) || |
1586 | 1 |
(sm.isPostBlitDeclaration() && sm.ident != Id.postblit) || |
1587 | 1 |
sm.isInvariantDeclaration() || |
1588 | 1 |
sm.isUnitTestDeclaration()) |
1589 |
|
|
1590 |
{
|
|
1591 | 1 |
return 0; |
1592 |
}
|
|
1593 | 1 |
if (sm.ident == Id.empty) |
1594 |
{
|
|
1595 | 1 |
return 0; |
1596 |
}
|
|
1597 | 1 |
if (sm.isTypeInfoDeclaration()) // https://issues.dlang.org/show_bug.cgi?id=15177 |
1598 | 1 |
return 0; |
1599 | 1 |
if (!sds.isModule() && sm.isImport()) // https://issues.dlang.org/show_bug.cgi?id=17057 |
1600 | 1 |
return 0; |
1601 |
|
|
1602 |
//printf("\t%s\n", sm.ident.toChars());
|
|
1603 |
|
|
1604 |
/* Skip if already present in idents[]
|
|
1605 |
*/
|
|
1606 | 1 |
foreach (id; *idents) |
1607 |
{
|
|
1608 | 1 |
if (id == sm.ident) |
1609 | 1 |
return 0; |
1610 |
|
|
1611 |
// Avoid using strcmp in the first place due to the performance impact in an O(N^2) loop.
|
|
1612 |
debug
|
|
1613 |
{
|
|
1614 |
import core.stdc.string : strcmp; |
|
1615 | 1 |
assert(strcmp(id.toChars(), sm.ident.toChars()) != 0); |
1616 |
}
|
|
1617 |
}
|
|
1618 | 1 |
idents.push(sm.ident); |
1619 |
}
|
|
1620 | 1 |
else if (auto ed = sm.isEnumDeclaration()) |
1621 |
{
|
|
1622 | 1 |
ScopeDsymbol._foreach(null, ed.members, &pushIdentsDg); |
1623 |
}
|
|
1624 | 1 |
return 0; |
1625 |
}
|
|
1626 |
|
|
1627 | 1 |
ScopeDsymbol._foreach(sc, sds.members, &pushIdentsDg); |
1628 | 1 |
auto cd = sds.isClassDeclaration(); |
1629 | 1 |
if (cd && e.ident == Id.allMembers) |
1630 |
{
|
|
1631 | 1 |
if (cd.semanticRun < PASS.semanticdone) |
1632 | 1 |
cd.dsymbolSemantic(null); // https://issues.dlang.org/show_bug.cgi?id=13668 |
1633 |
// Try to resolve forward reference
|
|
1634 |
|
|
1635 |
void pushBaseMembersDg(ClassDeclaration cd) |
|
1636 |
{
|
|
1637 | 1 |
for (size_t i = 0; i < cd.baseclasses.dim; i++) |
1638 |
{
|
|
1639 | 1 |
auto cb = (*cd.baseclasses)[i].sym; |
1640 | 1 |
assert(cb); |
1641 | 1 |
ScopeDsymbol._foreach(null, cb.members, &pushIdentsDg); |
1642 | 1 |
if (cb.baseclasses.dim) |
1643 | 1 |
pushBaseMembersDg(cb); |
1644 |
}
|
|
1645 |
}
|
|
1646 |
|
|
1647 | 1 |
pushBaseMembersDg(cd); |
1648 |
}
|
|
1649 |
|
|
1650 |
// Turn Identifiers into StringExps reusing the allocated array
|
|
1651 | 1 |
assert(Expressions.sizeof == Identifiers.sizeof); |
1652 | 1 |
auto exps = cast(Expressions*)idents; |
1653 | 1 |
foreach (i, id; *idents) |
1654 |
{
|
|
1655 | 1 |
auto se = new StringExp(e.loc, id.toString()); |
1656 | 1 |
(*exps)[i] = se; |
1657 |
}
|
|
1658 |
|
|
1659 |
/* Making this a tuple is more flexible, as it can be statically unrolled.
|
|
1660 |
* To make an array literal, enclose __traits in [ ]:
|
|
1661 |
* [ __traits(allMembers, ...) ]
|
|
1662 |
*/
|
|
1663 | 1 |
Expression ex = new TupleExp(e.loc, exps); |
1664 | 1 |
ex = ex.expressionSemantic(sc); |
1665 | 1 |
return ex; |
1666 |
}
|
|
1667 | 1 |
if (e.ident == Id.compiles) |
1668 |
{
|
|
1669 |
/* Determine if all the objects - types, expressions, or symbols -
|
|
1670 |
* compile without error
|
|
1671 |
*/
|
|
1672 | 1 |
if (!dim) |
1673 | 1 |
return False(); |
1674 |
|
|
1675 | 1 |
foreach (o; *e.args) |
1676 |
{
|
|
1677 | 1 |
uint errors = global.startGagging(); |
1678 | 1 |
Scope* sc2 = sc.push(); |
1679 | 1 |
sc2.tinst = null; |
1680 | 1 |
sc2.minst = null; |
1681 | 1 |
sc2.flags = (sc.flags & ~(SCOPE.ctfe | SCOPE.condition)) | SCOPE.compile | SCOPE.fullinst; |
1682 |
|
|
1683 | 1 |
bool err = false; |
1684 |
|
|
1685 | 1 |
auto t = isType(o); |
1686 | 1 |
while (t) |
1687 |
{
|
|
1688 | 1 |
if (auto tm = t.isTypeMixin()) |
1689 |
{
|
|
1690 |
/* The mixin string could be a type or an expression.
|
|
1691 |
* Have to try compiling it to see.
|
|
1692 |
*/
|
|
1693 | 1 |
OutBuffer buf; |
1694 | 1 |
if (expressionsToString(buf, sc, tm.exps)) |
1695 |
{
|
|
1696 |
err = true; |
|
1697 |
break; |
|
1698 |
}
|
|
1699 | 1 |
const olderrors = global.errors; |
1700 | 1 |
const len = buf.length; |
1701 | 1 |
buf.writeByte(0); |
1702 | 1 |
const str = buf.extractSlice()[0 .. len]; |
1703 | 1 |
scope p = new Parser!ASTCodegen(e.loc, sc._module, str, false); |
1704 | 1 |
p.nextToken(); |
1705 |
//printf("p.loc.linnum = %d\n", p.loc.linnum);
|
|
1706 |
|
|
1707 | 1 |
o = p.parseTypeOrAssignExp(TOK.endOfFile); |
1708 | 1 |
if (olderrors != global.errors || p.token.value != TOK.endOfFile) |
1709 |
{
|
|
1710 | 1 |
err = true; |
1711 | 1 |
break; |
1712 |
}
|
|
1713 | 1 |
t = o.isType(); |
1714 |
}
|
|
1715 |
else
|
|
1716 | 1 |
break; |
1717 |
}
|
|
1718 |
|
|
1719 | 1 |
if (!err) |
1720 |
{
|
|
1721 | 1 |
auto ex = t ? t.typeToExpression() : isExpression(o); |
1722 | 1 |
if (!ex && t) |
1723 |
{
|
|
1724 | 1 |
Dsymbol s; |
1725 | 1 |
t.resolve(e.loc, sc2, &ex, &t, &s); |
1726 | 1 |
if (t) |
1727 |
{
|
|
1728 | 1 |
t.typeSemantic(e.loc, sc2); |
1729 | 1 |
if (t.ty == Terror) |
1730 | 1 |
err = true; |
1731 |
}
|
|
1732 | 1 |
else if (s && s.errors) |
1733 |
err = true; |
|
1734 |
}
|
|
1735 | 1 |
if (ex) |
1736 |
{
|
|
1737 | 1 |
ex = ex.expressionSemantic(sc2); |
1738 | 1 |
ex = resolvePropertiesOnly(sc2, ex); |
1739 | 1 |
ex = ex.optimize(WANTvalue); |
1740 | 1 |
if (sc2.func && sc2.func.type.ty == Tfunction) |
1741 |
{
|
|
1742 | 1 |
const tf = cast(TypeFunction)sc2.func.type; |
1743 | 1 |
err |= tf.isnothrow && canThrow(ex, sc2.func, false); |
1744 |
}
|
|
1745 | 1 |
ex = checkGC(sc2, ex); |
1746 | 1 |
if (ex.op == TOK.error) |
1747 | 1 |
err = true; |
1748 |
}
|
|
1749 |
}
|
|
1750 |
|
|
1751 |
// Carefully detach the scope from the parent and throw it away as
|
|
1752 |
// we only need it to evaluate the expression
|
|
1753 |
// https://issues.dlang.org/show_bug.cgi?id=15428
|
|
1754 | 1 |
sc2.detach(); |
1755 |
|
|
1756 | 1 |
if (global.endGagging(errors) || err) |
1757 |
{
|
|
1758 | 1 |
return False(); |
1759 |
}
|
|
1760 |
}
|
|
1761 | 1 |
return True(); |
1762 |
}
|
|
1763 | 1 |
if (e.ident == Id.isSame) |
1764 |
{
|
|
1765 |
/* Determine if two symbols are the same
|
|
1766 |
*/
|
|
1767 | 1 |
if (dim != 2) |
1768 |
return dimError(2); |
|
1769 |
|
|
1770 |
// https://issues.dlang.org/show_bug.cgi?id=20761
|
|
1771 |
// tiarg semantic may expand in place the list of arguments, for example:
|
|
1772 |
//
|
|
1773 |
// before tiarg sema: __traits(isSame, seq!(0,0), seq!(1,1))
|
|
1774 |
// after : __traits(isSame, 0, 0, 1, 1)
|
|
1775 |
//
|
|
1776 |
// so we split in two lists
|
|
1777 | 1 |
Objects ob1; |
1778 | 1 |
ob1.push((*e.args)[0]); |
1779 | 1 |
Objects ob2; |
1780 | 1 |
ob2.push((*e.args)[1]); |
1781 | 1 |
if (!TemplateInstance.semanticTiargs(e.loc, sc, &ob1, 0)) |
1782 |
return ErrorExp.get(); |
|
1783 | 1 |
if (!TemplateInstance.semanticTiargs(e.loc, sc, &ob2, 0)) |
1784 |
return ErrorExp.get(); |
|
1785 | 1 |
if (ob1.dim != ob2.dim) |
1786 | 1 |
return False(); |
1787 | 1 |
foreach (immutable i; 0 .. ob1.dim) |
1788 | 1 |
if (!ob1[i].isSame(ob2[i], sc)) |
1789 | 1 |
return False(); |
1790 | 1 |
return True(); |
1791 |
}
|
|
1792 | 1 |
if (e.ident == Id.getUnitTests) |
1793 |
{
|
|
1794 | 1 |
if (dim != 1) |
1795 | 1 |
return dimError(1); |
1796 |
|
|
1797 | 1 |
auto o = (*e.args)[0]; |
1798 | 1 |
auto s = getDsymbolWithoutExpCtx(o); |
1799 | 1 |
if (!s) |
1800 |
{
|
|
1801 |
e.error("argument `%s` to __traits(getUnitTests) must be a module or aggregate", |
|
1802 |
o.toChars()); |
|
1803 |
return ErrorExp.get(); |
|
1804 |
}
|
|
1805 | 1 |
if (auto imp = s.isImport()) // https://issues.dlang.org/show_bug.cgi?id=10990 |
1806 | 1 |
s = imp.mod; |
1807 |
|
|
1808 | 1 |
auto sds = s.isScopeDsymbol(); |
1809 | 1 |
if (!sds) |
1810 |
{
|
|
1811 |
e.error("argument `%s` to __traits(getUnitTests) must be a module or aggregate, not a %s", |
|
1812 |
s.toChars(), s.kind()); |
|
1813 |
return ErrorExp.get(); |
|
1814 |
}
|
|
1815 |
|
|
1816 | 1 |
auto exps = new Expressions(); |
1817 | 1 |
if (global.params.useUnitTests) |
1818 |
{
|
|
1819 | 1 |
bool[void*] uniqueUnitTests; |
1820 |
|
|
1821 |
void symbolDg(Dsymbol s) |
|
1822 |
{
|
|
1823 | 1 |
if (auto ad = s.isAttribDeclaration()) |
1824 |
{
|
|
1825 | 1 |
ad.include(null).foreachDsymbol(&symbolDg); |
1826 |
}
|
|
1827 | 1 |
else if (auto ud = s.isUnitTestDeclaration()) |
1828 |
{
|
|
1829 | 1 |
if (cast(void*)ud in uniqueUnitTests) |
1830 |
return; |
|
1831 |
|
|
1832 | 1 |
uniqueUnitTests[cast(void*)ud] = true; |
1833 |
|
|
1834 | 1 |
auto ad = new FuncAliasDeclaration(ud.ident, ud, false); |
1835 | 1 |
ad.protection = ud.protection; |
1836 |
|
|
1837 | 1 |
auto e = new DsymbolExp(Loc.initial, ad, false); |
1838 | 1 |
exps.push(e); |
1839 |
}
|
|
1840 |
}
|
|
1841 |
|
|
1842 | 1 |
sds.members.foreachDsymbol(&symbolDg); |
1843 |
}
|
|
1844 | 1 |
auto te = new TupleExp(e.loc, exps); |
1845 | 1 |
return te.expressionSemantic(sc); |
1846 |
}
|
|
1847 | 1 |
if (e.ident == Id.getVirtualIndex) |
1848 |
{
|
|
1849 | 1 |
if (dim != 1) |
1850 | 1 |
return dimError(1); |
1851 |
|
|
1852 | 1 |
auto o = (*e.args)[0]; |
1853 | 1 |
auto s = getDsymbolWithoutExpCtx(o); |
1854 |
|
|
1855 | 1 |
auto fd = s ? s.isFuncDeclaration() : null; |
1856 | 1 |
if (!fd) |
1857 |
{
|
|
1858 |
e.error("first argument to __traits(getVirtualIndex) must be a function"); |
|
1859 |
return ErrorExp.get(); |
|
1860 |
}
|
|
1861 |
|
|
1862 | 1 |
fd = fd.toAliasFunc(); // Necessary to support multiple overloads. |
1863 | 1 |
return new IntegerExp(e.loc, fd.vtblIndex, Type.tptrdiff_t); |
1864 |
}
|
|
1865 | 1 |
if (e.ident == Id.getPointerBitmap) |
1866 |
{
|
|
1867 | 1 |
return pointerBitmap(e); |
1868 |
}
|
|
1869 | 1 |
if (e.ident == Id.isZeroInit) |
1870 |
{
|
|
1871 | 1 |
if (dim != 1) |
1872 |
return dimError(1); |
|
1873 |
|
|
1874 | 1 |
auto o = (*e.args)[0]; |
1875 | 1 |
Type t = isType(o); |
1876 | 1 |
if (!t) |
1877 |
{
|
|
1878 | 1 |
e.error("type expected as second argument of __traits `%s` instead of `%s`", |
1879 |
e.ident.toChars(), o.toChars()); |
|
1880 | 1 |
return ErrorExp.get(); |
1881 |
}
|
|
1882 |
|
|
1883 | 1 |
Type tb = t.baseElemOf(); |
1884 | 1 |
return tb.isZeroInit(e.loc) ? True() : False(); |
1885 |
}
|
|
1886 | 1 |
if (e.ident == Id.getTargetInfo) |
1887 |
{
|
|
1888 | 1 |
if (dim != 1) |
1889 | 1 |
return dimError(1); |
1890 |
|
|
1891 | 1 |
auto ex = isExpression((*e.args)[0]); |
1892 | 1 |
StringExp se = ex ? ex.ctfeInterpret().toStringExp() : null; |
1893 | 1 |
if (!ex || !se || se.len == 0) |
1894 |
{
|
|
1895 | 1 |
e.error("string expected as argument of __traits `%s` instead of `%s`", e.ident.toChars(), ex.toChars()); |
1896 | 1 |
return ErrorExp.get(); |
1897 |
}
|
|
1898 | 1 |
se = se.toUTF8(sc); |
1899 |
|
|
1900 | 1 |
const slice = se.peekString(); |
1901 | 1 |
Expression r = target.getTargetInfo(slice.ptr, e.loc); // BUG: reliance on terminating 0 |
1902 | 1 |
if (!r) |
1903 |
{
|
|
1904 | 1 |
e.error("`getTargetInfo` key `\"%.*s\"` not supported by this implementation", |
1905 |
cast(int)slice.length, slice.ptr); |
|
1906 | 1 |
return ErrorExp.get(); |
1907 |
}
|
|
1908 | 1 |
return r.expressionSemantic(sc); |
1909 |
}
|
|
1910 | 1 |
if (e.ident == Id.getLocation) |
1911 |
{
|
|
1912 | 1 |
if (dim != 1) |
1913 |
return dimError(1); |
|
1914 | 1 |
auto arg0 = (*e.args)[0]; |
1915 | 1 |
Dsymbol s = getDsymbolWithoutExpCtx(arg0); |
1916 | 1 |
if (!s || !s.loc.isValid()) |
1917 |
{
|
|
1918 | 1 |
e.error("can only get the location of a symbol, not `%s`", arg0.toChars()); |
1919 | 1 |
return ErrorExp.get(); |
1920 |
}
|
|
1921 |
|
|
1922 | 1 |
const fd = s.isFuncDeclaration(); |
1923 |
// FIXME:td.overnext is always set, even when using an index on it
|
|
1924 |
//const td = s.isTemplateDeclaration();
|
|
1925 | 1 |
if ((fd && fd.overnext) /*|| (td && td.overnext)*/) |
1926 |
{
|
|
1927 | 1 |
e.error("cannot get location of an overload set, " ~ |
1928 |
"use `__traits(getOverloads, ..., \"%s\"%s)[N]` " ~ |
|
1929 |
"to get the Nth overload", |
|
1930 |
arg0.toChars(), /*td ? ", true".ptr :*/ "".ptr); |
|
1931 | 1 |
return ErrorExp.get(); |
1932 |
}
|
|
1933 |
|
|
1934 | 1 |
auto exps = new Expressions(3); |
1935 | 1 |
(*exps)[0] = new StringExp(e.loc, s.loc.filename.toDString()); |
1936 | 1 |
(*exps)[1] = new IntegerExp(e.loc, s.loc.linnum,Type.tint32); |
1937 | 1 |
(*exps)[2] = new IntegerExp(e.loc, s.loc.charnum,Type.tint32); |
1938 | 1 |
auto tup = new TupleExp(e.loc, exps); |
1939 | 1 |
return tup.expressionSemantic(sc); |
1940 |
}
|
|
1941 |
|
|
1942 |
static const(char)[] trait_search_fp(const(char)[] seed, ref int cost) |
|
1943 |
{
|
|
1944 |
//printf("trait_search_fp('%s')\n", seed);
|
|
1945 | 1 |
if (!seed.length) |
1946 |
return null; |
|
1947 | 1 |
cost = 0; |
1948 | 1 |
const sv = traitsStringTable.lookup(seed); |
1949 | 1 |
return sv ? sv.toString() : null; |
1950 |
}
|
|
1951 |
|
|
1952 | 1 |
if (auto sub = speller!trait_search_fp(e.ident.toString())) |
1953 | 1 |
e.error("unrecognized trait `%s`, did you mean `%.*s`?", e.ident.toChars(), cast(int) sub.length, sub.ptr); |
1954 |
else
|
|
1955 | 1 |
e.error("unrecognized trait `%s`", e.ident.toChars()); |
1956 | 1 |
return ErrorExp.get(); |
1957 |
}
|
|
1958 |
|
|
1959 |
/// compare arguments of __traits(isSame)
|
|
1960 |
private bool isSame(RootObject o1, RootObject o2, Scope* sc) |
|
1961 |
{
|
|
1962 |
static FuncLiteralDeclaration isLambda(RootObject oarg) |
|
1963 |
{
|
|
1964 | 1 |
if (auto t = isDsymbol(oarg)) |
1965 |
{
|
|
1966 | 1 |
if (auto td = t.isTemplateDeclaration()) |
1967 |
{
|
|
1968 | 1 |
if (td.members && td.members.dim == 1) |
1969 |
{
|
|
1970 | 1 |
if (auto fd = (*td.members)[0].isFuncLiteralDeclaration()) |
1971 | 1 |
return fd; |
1972 |
}
|
|
1973 |
}
|
|
1974 |
}
|
|
1975 | 1 |
else if (auto ea = isExpression(oarg)) |
1976 |
{
|
|
1977 | 1 |
if (ea.op == TOK.function_) |
1978 |
{
|
|
1979 | 1 |
if (auto fe = cast(FuncExp)ea) |
1980 | 1 |
return fe.fd; |
1981 |
}
|
|
1982 |
}
|
|
1983 | 1 |
return null; |
1984 |
}
|
|
1985 |
|
|
1986 | 1 |
auto l1 = isLambda(o1); |
1987 | 1 |
auto l2 = isLambda(o2); |
1988 |
|
|
1989 | 1 |
if (l1 && l2) |
1990 |
{
|
|
1991 |
import dmd.lambdacomp : isSameFuncLiteral; |
|
1992 | 1 |
if (isSameFuncLiteral(l1, l2, sc)) |
1993 | 1 |
return true; |
1994 |
}
|
|
1995 |
|
|
1996 |
// issue 12001, allow isSame, <BasicType>, <BasicType>
|
|
1997 | 1 |
Type t1 = isType(o1); |
1998 | 1 |
Type t2 = isType(o2); |
1999 | 1 |
if (t1 && t2 && t1.equals(t2)) |
2000 | 1 |
return true; |
2001 |
|
|
2002 | 1 |
auto s1 = getDsymbol(o1); |
2003 | 1 |
auto s2 = getDsymbol(o2); |
2004 |
//printf("isSame: %s, %s\n", o1.toChars(), o2.toChars());
|
|
2005 |
version (none) |
|
2006 |
{
|
|
2007 |
printf("o1: %p\n", o1); |
|
2008 |
printf("o2: %p\n", o2); |
|
2009 |
if (!s1) |
|
2010 |
{
|
|
2011 |
if (auto ea = isExpression(o1)) |
|
2012 |
printf("%s\n", ea.toChars()); |
|
2013 |
if (auto ta = isType(o1)) |
|
2014 |
printf("%s\n", ta.toChars()); |
|
2015 |
return false; |
|
2016 |
}
|
|
2017 |
else
|
|
2018 |
printf("%s %s\n", s1.kind(), s1.toChars()); |
|
2019 |
}
|
|
2020 | 1 |
if (!s1 && !s2) |
2021 |
{
|
|
2022 | 1 |
auto ea1 = isExpression(o1); |
2023 | 1 |
auto ea2 = isExpression(o2); |
2024 | 1 |
if (ea1 && ea2) |
2025 |
{
|
|
2026 | 1 |
if (ea1.equals(ea2)) |
2027 | 1 |
return true; |
2028 |
}
|
|
2029 |
}
|
|
2030 | 1 |
if (!s1 || !s2) |
2031 | 1 |
return false; |
2032 |
|
|
2033 | 1 |
s1 = s1.toAlias(); |
2034 | 1 |
s2 = s2.toAlias(); |
2035 |
|
|
2036 | 1 |
if (auto fa1 = s1.isFuncAliasDeclaration()) |
2037 |
s1 = fa1.toAliasFunc(); |
|
2038 | 1 |
if (auto fa2 = s2.isFuncAliasDeclaration()) |
2039 | 1 |
s2 = fa2.toAliasFunc(); |
2040 |
|
|
2041 |
// https://issues.dlang.org/show_bug.cgi?id=11259
|
|
2042 |
// compare import symbol to a package symbol
|
|
2043 |
static bool cmp(Dsymbol s1, Dsymbol s2) |
|
2044 |
{
|
|
2045 | 1 |
auto imp = s1.isImport(); |
2046 | 1 |
return imp && imp.pkg && imp.pkg == s2.isPackage(); |
2047 |
}
|
|
2048 |
|
|
2049 | 1 |
if (cmp(s1,s2) || cmp(s2,s1)) |
2050 | 1 |
return true; |
2051 |
|
|
2052 | 1 |
if (s1 == s2) |
2053 | 1 |
return true; |
2054 |
|
|
2055 |
// https://issues.dlang.org/show_bug.cgi?id=18771
|
|
2056 |
// OverloadSets are equal if they contain the same functions
|
|
2057 | 1 |
auto overSet1 = s1.isOverloadSet(); |
2058 | 1 |
if (!overSet1) |
2059 | 1 |
return false; |
2060 |
|
|
2061 | 1 |
auto overSet2 = s2.isOverloadSet(); |
2062 | 1 |
if (!overSet2) |
2063 |
return false; |
|
2064 |
|
|
2065 | 1 |
if (overSet1.a.dim != overSet2.a.dim) |
2066 |
return false; |
|
2067 |
|
|
2068 |
// OverloadSets contain array of Dsymbols => O(n*n)
|
|
2069 |
// to compare for equality as the order of overloads
|
|
2070 |
// might not be the same
|
|
2071 |
Lnext: |
|
2072 | 1 |
foreach(overload1; overSet1.a) |
2073 |
{
|
|
2074 | 1 |
foreach(overload2; overSet2.a) |
2075 |
{
|
|
2076 | 1 |
if (overload1 == overload2) |
2077 | 1 |
continue Lnext; |
2078 |
}
|
|
2079 |
return false; |
|
2080 |
}
|
|
2081 | 1 |
return true; |
2082 |
}
|
Read our documentation on viewing source code .