]>
Commit | Line | Data |
---|---|---|
5fee5ec3 IB |
1 | /** |
2 | * The base class for a D symbol, which can be a module, variable, function, enum, etc. | |
3 | * | |
c43b5909 IB |
4 | * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved |
5 | * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) | |
6 | * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) | |
5fee5ec3 IB |
7 | * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dsymbol.d, _dsymbol.d) |
8 | * Documentation: https://dlang.org/phobos/dmd_dsymbol.html | |
9 | * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dsymbol.d | |
10 | */ | |
11 | ||
12 | module dmd.dsymbol; | |
13 | ||
14 | import core.stdc.stdarg; | |
15 | import core.stdc.stdio; | |
16 | import core.stdc.string; | |
17 | import core.stdc.stdlib; | |
18 | ||
19 | import dmd.aggregate; | |
20 | import dmd.aliasthis; | |
21 | import dmd.arraytypes; | |
22 | import dmd.attrib; | |
23 | import dmd.astenums; | |
24 | import dmd.ast_node; | |
25 | import dmd.gluelayer; | |
26 | import dmd.dclass; | |
27 | import dmd.declaration; | |
28 | import dmd.denum; | |
29 | import dmd.dimport; | |
30 | import dmd.dmodule; | |
31 | import dmd.dversion; | |
32 | import dmd.dscope; | |
33 | import dmd.dstruct; | |
34 | import dmd.dsymbolsem; | |
35 | import dmd.dtemplate; | |
36 | import dmd.errors; | |
37 | import dmd.expression; | |
38 | import dmd.expressionsem; | |
39 | import dmd.func; | |
40 | import dmd.globals; | |
41 | import dmd.id; | |
42 | import dmd.identifier; | |
43 | import dmd.init; | |
44 | import dmd.lexer; | |
45 | import dmd.mtype; | |
46 | import dmd.nspace; | |
47 | import dmd.opover; | |
48 | import dmd.root.aav; | |
49 | import dmd.root.rmem; | |
50 | import dmd.root.rootobject; | |
51 | import dmd.root.speller; | |
52 | import dmd.root.string; | |
53 | import dmd.statement; | |
6384eff5 | 54 | import dmd.staticassert; |
5fee5ec3 IB |
55 | import dmd.tokens; |
56 | import dmd.visitor; | |
57 | ||
58 | /*************************************** | |
59 | * Calls dg(Dsymbol *sym) for each Dsymbol. | |
60 | * If dg returns !=0, stops and returns that value else returns 0. | |
61 | * Params: | |
62 | * symbols = Dsymbols | |
63 | * dg = delegate to call for each Dsymbol | |
64 | * Returns: | |
65 | * last value returned by dg() | |
66 | * | |
67 | * See_Also: $(REF each, dmd, root, array) | |
68 | */ | |
69 | int foreachDsymbol(Dsymbols* symbols, scope int delegate(Dsymbol) dg) | |
70 | { | |
71 | assert(dg); | |
72 | if (symbols) | |
73 | { | |
74 | /* Do not use foreach, as the size of the array may expand during iteration | |
75 | */ | |
76 | for (size_t i = 0; i < symbols.dim; ++i) | |
77 | { | |
78 | Dsymbol s = (*symbols)[i]; | |
79 | const result = dg(s); | |
80 | if (result) | |
81 | return result; | |
82 | } | |
83 | } | |
84 | return 0; | |
85 | } | |
86 | ||
87 | /*************************************** | |
88 | * Calls dg(Dsymbol *sym) for each Dsymbol. | |
89 | * Params: | |
90 | * symbols = Dsymbols | |
91 | * dg = delegate to call for each Dsymbol | |
92 | * | |
93 | * See_Also: $(REF each, dmd, root, array) | |
94 | */ | |
95 | void foreachDsymbol(Dsymbols* symbols, scope void delegate(Dsymbol) dg) | |
96 | { | |
97 | assert(dg); | |
98 | if (symbols) | |
99 | { | |
100 | /* Do not use foreach, as the size of the array may expand during iteration | |
101 | */ | |
102 | for (size_t i = 0; i < symbols.dim; ++i) | |
103 | { | |
104 | Dsymbol s = (*symbols)[i]; | |
105 | dg(s); | |
106 | } | |
107 | } | |
108 | } | |
109 | ||
110 | ||
111 | struct Ungag | |
112 | { | |
113 | uint oldgag; | |
114 | ||
31350635 | 115 | extern (D) this(uint old) nothrow |
5fee5ec3 IB |
116 | { |
117 | this.oldgag = old; | |
118 | } | |
119 | ||
31350635 | 120 | extern (C++) ~this() nothrow |
5fee5ec3 IB |
121 | { |
122 | global.gag = oldgag; | |
123 | } | |
124 | } | |
125 | ||
126 | struct Visibility | |
127 | { | |
128 | /// | |
129 | enum Kind : ubyte | |
130 | { | |
131 | undefined, | |
132 | none, // no access | |
133 | private_, | |
134 | package_, | |
135 | protected_, | |
136 | public_, | |
137 | export_, | |
138 | } | |
139 | ||
140 | Kind kind; | |
141 | Package pkg; | |
142 | ||
143 | extern (D): | |
144 | ||
145 | this(Visibility.Kind kind) pure nothrow @nogc @safe | |
146 | { | |
147 | this.kind = kind; | |
148 | } | |
149 | ||
150 | /** | |
151 | * Checks if `this` is less or more visible than `other` | |
152 | * | |
153 | * Params: | |
154 | * other = Visibility to compare `this` to. | |
155 | * | |
156 | * Returns: | |
157 | * A value `< 0` if `this` is less visible than `other`, | |
158 | * a value `> 0` if `this` is more visible than `other`, | |
159 | * and `0` if they are at the same level. | |
160 | * Note that `package` visibility with different packages | |
161 | * will also return `0`. | |
162 | */ | |
163 | int opCmp(const Visibility other) const pure nothrow @nogc @safe | |
164 | { | |
165 | return this.kind - other.kind; | |
166 | } | |
167 | ||
168 | /// | |
169 | unittest | |
170 | { | |
171 | assert(Visibility(Visibility.Kind.public_) > Visibility(Visibility.Kind.private_)); | |
172 | assert(Visibility(Visibility.Kind.private_) < Visibility(Visibility.Kind.protected_)); | |
173 | assert(Visibility(Visibility.Kind.package_) >= Visibility(Visibility.Kind.package_)); | |
174 | } | |
175 | ||
176 | /** | |
177 | * Checks if `this` is absolutely identical visibility attribute to `other` | |
178 | */ | |
179 | bool opEquals(ref const Visibility other) const | |
180 | { | |
181 | if (this.kind == other.kind) | |
182 | { | |
183 | if (this.kind == Visibility.Kind.package_) | |
184 | return this.pkg == other.pkg; | |
185 | return true; | |
186 | } | |
187 | return false; | |
188 | } | |
189 | } | |
190 | ||
0fb57034 | 191 | enum PASS : ubyte |
5fee5ec3 | 192 | { |
235d5a96 | 193 | initial, // initial state |
5fee5ec3 IB |
194 | semantic, // semantic() started |
195 | semanticdone, // semantic() done | |
196 | semantic2, // semantic2() started | |
197 | semantic2done, // semantic2() done | |
198 | semantic3, // semantic3() started | |
199 | semantic3done, // semantic3() done | |
200 | inline, // inline started | |
201 | inlinedone, // inline done | |
202 | obj, // toObjFile() run | |
203 | } | |
204 | ||
205 | // Search options | |
206 | enum : int | |
207 | { | |
208 | IgnoreNone = 0x00, // default | |
209 | IgnorePrivateImports = 0x01, // don't search private imports | |
210 | IgnoreErrors = 0x02, // don't give error messages | |
211 | IgnoreAmbiguous = 0x04, // return NULL if ambiguous | |
212 | SearchLocalsOnly = 0x08, // only look at locals (don't search imports) | |
213 | SearchImportsOnly = 0x10, // only look in imports | |
214 | SearchUnqualifiedModule = 0x20, // the module scope search is unqualified, | |
215 | // meaning don't search imports in that scope, | |
216 | // because qualified module searches search | |
217 | // their imports | |
218 | IgnoreSymbolVisibility = 0x80, // also find private and package protected symbols | |
219 | TagNameSpace = 0x100, // search ImportC tag symbol table | |
220 | } | |
221 | ||
222 | /*********************************************************** | |
223 | * Struct/Class/Union field state. | |
224 | * Used for transitory information when setting field offsets, such | |
225 | * as bit fields. | |
226 | */ | |
227 | struct FieldState | |
228 | { | |
0fb57034 | 229 | uint offset; /// byte offset for next field |
5fee5ec3 | 230 | |
0fb57034 IB |
231 | uint fieldOffset; /// byte offset for the start of the bit field |
232 | uint fieldSize; /// byte size of field | |
233 | uint fieldAlign; /// byte alignment of field | |
5fee5ec3 | 234 | uint bitOffset; /// bit offset for field |
0fb57034 | 235 | |
5fee5ec3 IB |
236 | bool inFlight; /// bit field is in flight |
237 | } | |
238 | ||
239 | /*********************************************************** | |
240 | */ | |
241 | extern (C++) class Dsymbol : ASTNode | |
242 | { | |
243 | Identifier ident; | |
244 | Dsymbol parent; | |
245 | /// C++ namespace this symbol belongs to | |
246 | CPPNamespaceDeclaration cppnamespace; | |
247 | Symbol* csym; // symbol for code generator | |
5fee5ec3 IB |
248 | const Loc loc; // where defined |
249 | Scope* _scope; // !=null means context to use for semantic() | |
250 | const(char)* prettystring; // cached value of toPrettyChars() | |
251 | bool errors; // this symbol failed to pass semantic() | |
235d5a96 | 252 | PASS semanticRun = PASS.initial; |
5fee5ec3 IB |
253 | ushort localNum; /// perturb mangled name to avoid collisions with those in FuncDeclaration.localsymtab |
254 | ||
255 | DeprecatedDeclaration depdecl; // customized deprecation message | |
256 | UserAttributeDeclaration userAttribDecl; // user defined attributes | |
257 | ||
31350635 | 258 | final extern (D) this() nothrow |
5fee5ec3 IB |
259 | { |
260 | //printf("Dsymbol::Dsymbol(%p)\n", this); | |
261 | loc = Loc(null, 0, 0); | |
262 | } | |
263 | ||
31350635 | 264 | final extern (D) this(Identifier ident) nothrow |
5fee5ec3 IB |
265 | { |
266 | //printf("Dsymbol::Dsymbol(%p, ident)\n", this); | |
267 | this.loc = Loc(null, 0, 0); | |
268 | this.ident = ident; | |
269 | } | |
270 | ||
31350635 | 271 | final extern (D) this(const ref Loc loc, Identifier ident) nothrow |
5fee5ec3 IB |
272 | { |
273 | //printf("Dsymbol::Dsymbol(%p, ident)\n", this); | |
274 | this.loc = loc; | |
275 | this.ident = ident; | |
276 | } | |
277 | ||
31350635 | 278 | static Dsymbol create(Identifier ident) nothrow |
5fee5ec3 IB |
279 | { |
280 | return new Dsymbol(ident); | |
281 | } | |
282 | ||
283 | override const(char)* toChars() const | |
284 | { | |
285 | return ident ? ident.toChars() : "__anonymous"; | |
286 | } | |
287 | ||
288 | // helper to print fully qualified (template) arguments | |
289 | const(char)* toPrettyCharsHelper() | |
290 | { | |
291 | return toChars(); | |
292 | } | |
293 | ||
294 | final const(Loc) getLoc() | |
295 | { | |
296 | if (!loc.isValid()) // avoid bug 5861. | |
297 | if (const m = getModule()) | |
298 | return Loc(m.srcfile.toChars(), 0, 0); | |
299 | return loc; | |
300 | } | |
301 | ||
302 | final const(char)* locToChars() | |
303 | { | |
304 | return getLoc().toChars(); | |
305 | } | |
306 | ||
307 | override bool equals(const RootObject o) const | |
308 | { | |
309 | if (this == o) | |
310 | return true; | |
311 | if (o.dyncast() != DYNCAST.dsymbol) | |
312 | return false; | |
313 | auto s = cast(Dsymbol)o; | |
314 | // Overload sets don't have an ident | |
315 | // Function-local declarations may have identical names | |
316 | // if they are declared in different scopes | |
317 | if (s && ident && s.ident && ident.equals(s.ident) && localNum == s.localNum) | |
318 | return true; | |
319 | return false; | |
320 | } | |
321 | ||
322 | final bool isAnonymous() const | |
323 | { | |
324 | return ident is null || ident.isAnonymous; | |
325 | } | |
326 | ||
327 | extern(D) private const(char)[] prettyFormatHelper() | |
328 | { | |
329 | const cstr = toPrettyChars(); | |
330 | return '`' ~ cstr.toDString() ~ "`\0"; | |
331 | } | |
332 | ||
333 | static if (__VERSION__ < 2092) | |
334 | { | |
335 | final void error(const ref Loc loc, const(char)* format, ...) | |
336 | { | |
337 | va_list ap; | |
338 | va_start(ap, format); | |
339 | .verror(loc, format, ap, kind(), prettyFormatHelper().ptr); | |
340 | va_end(ap); | |
341 | } | |
342 | ||
343 | final void error(const(char)* format, ...) | |
344 | { | |
345 | va_list ap; | |
346 | va_start(ap, format); | |
347 | const loc = getLoc(); | |
348 | .verror(loc, format, ap, kind(), prettyFormatHelper().ptr); | |
349 | va_end(ap); | |
350 | } | |
351 | ||
352 | final void deprecation(const ref Loc loc, const(char)* format, ...) | |
353 | { | |
354 | va_list ap; | |
355 | va_start(ap, format); | |
356 | .vdeprecation(loc, format, ap, kind(), prettyFormatHelper().ptr); | |
357 | va_end(ap); | |
358 | } | |
359 | ||
360 | final void deprecation(const(char)* format, ...) | |
361 | { | |
362 | va_list ap; | |
363 | va_start(ap, format); | |
364 | const loc = getLoc(); | |
365 | .vdeprecation(loc, format, ap, kind(), prettyFormatHelper().ptr); | |
366 | va_end(ap); | |
367 | } | |
368 | } | |
369 | else | |
370 | { | |
371 | pragma(printf) final void error(const ref Loc loc, const(char)* format, ...) | |
372 | { | |
373 | va_list ap; | |
374 | va_start(ap, format); | |
375 | .verror(loc, format, ap, kind(), prettyFormatHelper().ptr); | |
376 | va_end(ap); | |
377 | } | |
378 | ||
379 | pragma(printf) final void error(const(char)* format, ...) | |
380 | { | |
381 | va_list ap; | |
382 | va_start(ap, format); | |
383 | const loc = getLoc(); | |
384 | .verror(loc, format, ap, kind(), prettyFormatHelper().ptr); | |
385 | va_end(ap); | |
386 | } | |
387 | ||
388 | pragma(printf) final void deprecation(const ref Loc loc, const(char)* format, ...) | |
389 | { | |
390 | va_list ap; | |
391 | va_start(ap, format); | |
392 | .vdeprecation(loc, format, ap, kind(), prettyFormatHelper().ptr); | |
393 | va_end(ap); | |
394 | } | |
395 | ||
396 | pragma(printf) final void deprecation(const(char)* format, ...) | |
397 | { | |
398 | va_list ap; | |
399 | va_start(ap, format); | |
400 | const loc = getLoc(); | |
401 | .vdeprecation(loc, format, ap, kind(), prettyFormatHelper().ptr); | |
402 | va_end(ap); | |
403 | } | |
404 | } | |
405 | ||
406 | final bool checkDeprecated(const ref Loc loc, Scope* sc) | |
407 | { | |
408 | if (global.params.useDeprecated == DiagnosticReporting.off) | |
409 | return false; | |
410 | if (!this.isDeprecated()) | |
411 | return false; | |
412 | // Don't complain if we're inside a deprecated symbol's scope | |
413 | if (sc.isDeprecated()) | |
414 | return false; | |
415 | // Don't complain if we're inside a template constraint | |
416 | // https://issues.dlang.org/show_bug.cgi?id=21831 | |
417 | if (sc.flags & SCOPE.constraint) | |
418 | return false; | |
419 | ||
420 | const(char)* message = null; | |
421 | for (Dsymbol p = this; p; p = p.parent) | |
422 | { | |
423 | message = p.depdecl ? p.depdecl.getMessage() : null; | |
424 | if (message) | |
425 | break; | |
426 | } | |
427 | if (message) | |
428 | deprecation(loc, "is deprecated - %s", message); | |
429 | else | |
430 | deprecation(loc, "is deprecated"); | |
431 | ||
432 | if (auto ti = sc.parent ? sc.parent.isInstantiated() : null) | |
433 | ti.printInstantiationTrace(Classification.deprecation); | |
434 | else if (auto ti = sc.parent ? sc.parent.isTemplateInstance() : null) | |
435 | ti.printInstantiationTrace(Classification.deprecation); | |
436 | ||
437 | return true; | |
438 | } | |
439 | ||
440 | /********************************** | |
441 | * Determine which Module a Dsymbol is in. | |
442 | */ | |
443 | final Module getModule() | |
444 | { | |
445 | //printf("Dsymbol::getModule()\n"); | |
446 | if (TemplateInstance ti = isInstantiated()) | |
447 | return ti.tempdecl.getModule(); | |
448 | Dsymbol s = this; | |
449 | while (s) | |
450 | { | |
451 | //printf("\ts = %s '%s'\n", s.kind(), s.toPrettyChars()); | |
452 | Module m = s.isModule(); | |
453 | if (m) | |
454 | return m; | |
455 | s = s.parent; | |
456 | } | |
457 | return null; | |
458 | } | |
459 | ||
fd43568c IB |
460 | /************************************** |
461 | * Does this Dsymbol come from a C file? | |
462 | * Returns: | |
463 | * true if it does | |
464 | */ | |
465 | final bool isCsymbol() | |
466 | { | |
467 | if (Module m = getModule()) | |
fbdaa581 | 468 | return m.filetype == FileType.c; |
fd43568c IB |
469 | return false; |
470 | } | |
471 | ||
5fee5ec3 IB |
472 | /********************************** |
473 | * Determine which Module a Dsymbol is in, as far as access rights go. | |
474 | */ | |
475 | final Module getAccessModule() | |
476 | { | |
477 | //printf("Dsymbol::getAccessModule()\n"); | |
478 | if (TemplateInstance ti = isInstantiated()) | |
479 | return ti.tempdecl.getAccessModule(); | |
480 | Dsymbol s = this; | |
481 | while (s) | |
482 | { | |
483 | //printf("\ts = %s '%s'\n", s.kind(), s.toPrettyChars()); | |
484 | Module m = s.isModule(); | |
485 | if (m) | |
486 | return m; | |
487 | TemplateInstance ti = s.isTemplateInstance(); | |
488 | if (ti && ti.enclosing) | |
489 | { | |
490 | /* Because of local template instantiation, the parent isn't where the access | |
491 | * rights come from - it's the template declaration | |
492 | */ | |
493 | s = ti.tempdecl; | |
494 | } | |
495 | else | |
496 | s = s.parent; | |
497 | } | |
498 | return null; | |
499 | } | |
500 | ||
501 | /** | |
502 | * `pastMixin` returns the enclosing symbol if this is a template mixin. | |
503 | * | |
504 | * `pastMixinAndNspace` does likewise, additionally skipping over Nspaces that | |
505 | * are mangleOnly. | |
506 | * | |
507 | * See also `parent`, `toParent` and `toParent2`. | |
508 | */ | |
509 | final inout(Dsymbol) pastMixin() inout | |
510 | { | |
511 | //printf("Dsymbol::pastMixin() %s\n", toChars()); | |
512 | if (!isTemplateMixin() && !isForwardingAttribDeclaration() && !isForwardingScopeDsymbol()) | |
513 | return this; | |
514 | if (!parent) | |
515 | return null; | |
516 | return parent.pastMixin(); | |
517 | } | |
518 | ||
519 | /********************************** | |
520 | * `parent` field returns a lexically enclosing scope symbol this is a member of. | |
521 | * | |
522 | * `toParent()` returns a logically enclosing scope symbol this is a member of. | |
523 | * It skips over TemplateMixin's. | |
524 | * | |
525 | * `toParent2()` returns an enclosing scope symbol this is living at runtime. | |
526 | * It skips over both TemplateInstance's and TemplateMixin's. | |
527 | * It's used when looking for the 'this' pointer of the enclosing function/class. | |
528 | * | |
529 | * `toParentDecl()` similar to `toParent2()` but always follows the template declaration scope | |
530 | * instead of the instantiation scope. | |
531 | * | |
532 | * `toParentLocal()` similar to `toParentDecl()` but follows the instantiation scope | |
533 | * if a template declaration is non-local i.e. global or static. | |
534 | * | |
535 | * Examples: | |
536 | * --- | |
537 | * module mod; | |
538 | * template Foo(alias a) { mixin Bar!(); } | |
539 | * mixin template Bar() { | |
540 | * public { // VisibilityDeclaration | |
541 | * void baz() { a = 2; } | |
542 | * } | |
543 | * } | |
544 | * void test() { | |
545 | * int v = 1; | |
546 | * alias foo = Foo!(v); | |
547 | * foo.baz(); | |
548 | * assert(v == 2); | |
549 | * } | |
550 | * | |
551 | * // s == FuncDeclaration('mod.test.Foo!().Bar!().baz()') | |
552 | * // s.parent == TemplateMixin('mod.test.Foo!().Bar!()') | |
553 | * // s.toParent() == TemplateInstance('mod.test.Foo!()') | |
554 | * // s.toParent2() == FuncDeclaration('mod.test') | |
555 | * // s.toParentDecl() == Module('mod') | |
556 | * // s.toParentLocal() == FuncDeclaration('mod.test') | |
557 | * --- | |
558 | */ | |
559 | final inout(Dsymbol) toParent() inout | |
560 | { | |
561 | return parent ? parent.pastMixin() : null; | |
562 | } | |
563 | ||
564 | /// ditto | |
565 | final inout(Dsymbol) toParent2() inout | |
566 | { | |
567 | if (!parent || !parent.isTemplateInstance && !parent.isForwardingAttribDeclaration() && !parent.isForwardingScopeDsymbol()) | |
568 | return parent; | |
569 | return parent.toParent2; | |
570 | } | |
571 | ||
572 | /// ditto | |
573 | final inout(Dsymbol) toParentDecl() inout | |
574 | { | |
575 | return toParentDeclImpl(false); | |
576 | } | |
577 | ||
578 | /// ditto | |
579 | final inout(Dsymbol) toParentLocal() inout | |
580 | { | |
581 | return toParentDeclImpl(true); | |
582 | } | |
583 | ||
584 | private inout(Dsymbol) toParentDeclImpl(bool localOnly) inout | |
585 | { | |
586 | auto p = toParent(); | |
587 | if (!p || !p.isTemplateInstance()) | |
588 | return p; | |
589 | auto ti = p.isTemplateInstance(); | |
590 | if (ti.tempdecl && (!localOnly || !(cast(TemplateDeclaration)ti.tempdecl).isstatic)) | |
591 | return ti.tempdecl.toParentDeclImpl(localOnly); | |
592 | return parent.toParentDeclImpl(localOnly); | |
593 | } | |
594 | ||
595 | /** | |
596 | * Returns the declaration scope scope of `this` unless any of the symbols | |
597 | * `p1` or `p2` resides in its enclosing instantiation scope then the | |
598 | * latter is returned. | |
599 | */ | |
600 | final Dsymbol toParentP(Dsymbol p1, Dsymbol p2 = null) | |
601 | { | |
602 | return followInstantiationContext(p1, p2) ? toParent2() : toParentLocal(); | |
603 | } | |
604 | ||
605 | final inout(TemplateInstance) isInstantiated() inout | |
606 | { | |
607 | if (!parent) | |
608 | return null; | |
609 | auto ti = parent.isTemplateInstance(); | |
610 | if (ti && !ti.isTemplateMixin()) | |
611 | return ti; | |
612 | return parent.isInstantiated(); | |
613 | } | |
614 | ||
615 | /*** | |
616 | * Returns true if any of the symbols `p1` or `p2` resides in the enclosing | |
617 | * instantiation scope of `this`. | |
618 | */ | |
619 | final bool followInstantiationContext(Dsymbol p1, Dsymbol p2 = null) | |
620 | { | |
621 | static bool has2This(Dsymbol s) | |
622 | { | |
623 | if (auto f = s.isFuncDeclaration()) | |
235d5a96 | 624 | return f.hasDualContext(); |
5fee5ec3 IB |
625 | if (auto ad = s.isAggregateDeclaration()) |
626 | return ad.vthis2 !is null; | |
627 | return false; | |
628 | } | |
629 | ||
630 | if (has2This(this)) | |
631 | { | |
632 | assert(p1); | |
633 | auto outer = toParent(); | |
634 | while (outer) | |
635 | { | |
636 | auto ti = outer.isTemplateInstance(); | |
637 | if (!ti) | |
638 | break; | |
639 | foreach (oarg; *ti.tiargs) | |
640 | { | |
641 | auto sa = getDsymbol(oarg); | |
642 | if (!sa) | |
643 | continue; | |
644 | sa = sa.toAlias().toParent2(); | |
645 | if (!sa) | |
646 | continue; | |
647 | if (sa == p1) | |
648 | return true; | |
649 | else if (p2 && sa == p2) | |
650 | return true; | |
651 | } | |
652 | outer = ti.tempdecl.toParent(); | |
653 | } | |
654 | return false; | |
655 | } | |
656 | return false; | |
657 | } | |
658 | ||
659 | // Check if this function is a member of a template which has only been | |
660 | // instantiated speculatively, eg from inside is(typeof()). | |
661 | // Return the speculative template instance it is part of, | |
662 | // or NULL if not speculative. | |
663 | final inout(TemplateInstance) isSpeculative() inout | |
664 | { | |
665 | if (!parent) | |
666 | return null; | |
667 | auto ti = parent.isTemplateInstance(); | |
668 | if (ti && ti.gagged) | |
669 | return ti; | |
670 | if (!parent.toParent()) | |
671 | return null; | |
672 | return parent.isSpeculative(); | |
673 | } | |
674 | ||
675 | final Ungag ungagSpeculative() const | |
676 | { | |
677 | uint oldgag = global.gag; | |
678 | if (global.gag && !isSpeculative() && !toParent2().isFuncDeclaration()) | |
679 | global.gag = 0; | |
680 | return Ungag(oldgag); | |
681 | } | |
682 | ||
683 | // kludge for template.isSymbol() | |
684 | override final DYNCAST dyncast() const | |
685 | { | |
686 | return DYNCAST.dsymbol; | |
687 | } | |
688 | ||
689 | /************************************* | |
690 | * Do syntax copy of an array of Dsymbol's. | |
691 | */ | |
692 | extern (D) static Dsymbols* arraySyntaxCopy(Dsymbols* a) | |
693 | { | |
694 | Dsymbols* b = null; | |
695 | if (a) | |
696 | { | |
697 | b = a.copy(); | |
698 | for (size_t i = 0; i < b.dim; i++) | |
699 | { | |
700 | (*b)[i] = (*b)[i].syntaxCopy(null); | |
701 | } | |
702 | } | |
703 | return b; | |
704 | } | |
705 | ||
706 | Identifier getIdent() | |
707 | { | |
708 | return ident; | |
709 | } | |
710 | ||
711 | const(char)* toPrettyChars(bool QualifyTypes = false) | |
712 | { | |
713 | if (prettystring && !QualifyTypes) | |
714 | return prettystring; | |
715 | ||
716 | //printf("Dsymbol::toPrettyChars() '%s'\n", toChars()); | |
717 | if (!parent) | |
718 | { | |
719 | auto s = toChars(); | |
720 | if (!QualifyTypes) | |
721 | prettystring = s; | |
722 | return s; | |
723 | } | |
724 | ||
725 | // Computer number of components | |
726 | size_t complength = 0; | |
727 | for (Dsymbol p = this; p; p = p.parent) | |
728 | ++complength; | |
729 | ||
730 | // Allocate temporary array comp[] | |
731 | alias T = const(char)[]; | |
732 | auto compptr = cast(T*)Mem.check(malloc(complength * T.sizeof)); | |
733 | auto comp = compptr[0 .. complength]; | |
734 | ||
735 | // Fill in comp[] and compute length of final result | |
736 | size_t length = 0; | |
737 | int i; | |
738 | for (Dsymbol p = this; p; p = p.parent) | |
739 | { | |
740 | const s = QualifyTypes ? p.toPrettyCharsHelper() : p.toChars(); | |
741 | const len = strlen(s); | |
742 | comp[i] = s[0 .. len]; | |
743 | ++i; | |
744 | length += len + 1; | |
745 | } | |
746 | ||
747 | auto s = cast(char*)mem.xmalloc_noscan(length); | |
748 | auto q = s + length - 1; | |
749 | *q = 0; | |
750 | foreach (j; 0 .. complength) | |
751 | { | |
752 | const t = comp[j].ptr; | |
753 | const len = comp[j].length; | |
754 | q -= len; | |
755 | memcpy(q, t, len); | |
756 | if (q == s) | |
757 | break; | |
758 | *--q = '.'; | |
759 | } | |
760 | free(comp.ptr); | |
761 | if (!QualifyTypes) | |
762 | prettystring = s; | |
763 | return s; | |
764 | } | |
765 | ||
766 | const(char)* kind() const pure nothrow @nogc @safe | |
767 | { | |
768 | return "symbol"; | |
769 | } | |
770 | ||
771 | /********************************* | |
772 | * If this symbol is really an alias for another, | |
773 | * return that other. | |
774 | * If needed, semantic() is invoked due to resolve forward reference. | |
775 | */ | |
776 | Dsymbol toAlias() | |
777 | { | |
778 | return this; | |
779 | } | |
780 | ||
781 | /********************************* | |
782 | * Resolve recursive tuple expansion in eponymous template. | |
783 | */ | |
784 | Dsymbol toAlias2() | |
785 | { | |
786 | return toAlias(); | |
787 | } | |
788 | ||
789 | void addMember(Scope* sc, ScopeDsymbol sds) | |
790 | { | |
791 | //printf("Dsymbol::addMember('%s')\n", toChars()); | |
792 | //printf("Dsymbol::addMember(this = %p, '%s' scopesym = '%s')\n", this, toChars(), sds.toChars()); | |
793 | //printf("Dsymbol::addMember(this = %p, '%s' sds = %p, sds.symtab = %p)\n", this, toChars(), sds, sds.symtab); | |
794 | parent = sds; | |
795 | if (isAnonymous()) // no name, so can't add it to symbol table | |
796 | return; | |
797 | ||
798 | if (!sds.symtabInsert(this)) // if name is already defined | |
799 | { | |
800 | if (isAliasDeclaration() && !_scope) | |
801 | setScope(sc); | |
802 | Dsymbol s2 = sds.symtabLookup(this,ident); | |
31350635 IB |
803 | /* https://issues.dlang.org/show_bug.cgi?id=17434 |
804 | * | |
805 | * If we are trying to add an import to the symbol table | |
806 | * that has already been introduced, then keep the one with | |
807 | * larger visibility. This is fine for imports because if | |
808 | * we have multiple imports of the same file, if a single one | |
809 | * is public then the symbol is reachable. | |
810 | */ | |
811 | if (auto i1 = isImport()) | |
812 | { | |
813 | if (auto i2 = s2.isImport()) | |
814 | { | |
815 | if (sc.explicitVisibility && sc.visibility > i2.visibility) | |
816 | sds.symtab.update(this); | |
817 | } | |
818 | } | |
5fee5ec3 IB |
819 | |
820 | // If using C tag/prototype/forward declaration rules | |
7e287503 | 821 | if (sc.flags & SCOPE.Cfile && !this.isImport()) |
0fb57034 IB |
822 | { |
823 | if (handleTagSymbols(*sc, this, s2, sds)) | |
824 | return; | |
825 | if (handleSymbolRedeclarations(*sc, this, s2, sds)) | |
5fee5ec3 IB |
826 | return; |
827 | ||
0fb57034 IB |
828 | sds.multiplyDefined(Loc.initial, this, s2); // ImportC doesn't allow overloading |
829 | errors = true; | |
830 | return; | |
831 | } | |
832 | ||
5fee5ec3 IB |
833 | if (!s2.overloadInsert(this)) |
834 | { | |
835 | sds.multiplyDefined(Loc.initial, this, s2); | |
836 | errors = true; | |
837 | } | |
838 | } | |
839 | if (sds.isAggregateDeclaration() || sds.isEnumDeclaration()) | |
840 | { | |
ae56e2da IB |
841 | if (ident == Id.__sizeof || |
842 | !(sc && sc.flags & SCOPE.Cfile) && (ident == Id.__xalignof || ident == Id._mangleof)) | |
5fee5ec3 IB |
843 | { |
844 | error("`.%s` property cannot be redefined", ident.toChars()); | |
845 | errors = true; | |
846 | } | |
847 | } | |
848 | } | |
849 | ||
850 | /************************************* | |
851 | * Set scope for future semantic analysis so we can | |
852 | * deal better with forward references. | |
853 | */ | |
854 | void setScope(Scope* sc) | |
855 | { | |
856 | //printf("Dsymbol::setScope() %p %s, %p stc = %llx\n", this, toChars(), sc, sc.stc); | |
857 | if (!sc.nofree) | |
858 | sc.setNoFree(); // may need it even after semantic() finishes | |
859 | _scope = sc; | |
860 | if (sc.depdecl) | |
861 | depdecl = sc.depdecl; | |
862 | if (!userAttribDecl) | |
863 | userAttribDecl = sc.userAttribDecl; | |
864 | } | |
865 | ||
866 | void importAll(Scope* sc) | |
867 | { | |
868 | } | |
869 | ||
870 | /********************************************* | |
871 | * Search for ident as member of s. | |
872 | * Params: | |
873 | * loc = location to print for error messages | |
874 | * ident = identifier to search for | |
875 | * flags = IgnoreXXXX | |
876 | * Returns: | |
877 | * null if not found | |
878 | */ | |
879 | Dsymbol search(const ref Loc loc, Identifier ident, int flags = IgnoreNone) | |
880 | { | |
881 | //printf("Dsymbol::search(this=%p,%s, ident='%s')\n", this, toChars(), ident.toChars()); | |
882 | return null; | |
883 | } | |
884 | ||
885 | extern (D) final Dsymbol search_correct(Identifier ident) | |
886 | { | |
887 | /*************************************************** | |
888 | * Search for symbol with correct spelling. | |
889 | */ | |
890 | extern (D) Dsymbol symbol_search_fp(const(char)[] seed, out int cost) | |
891 | { | |
892 | /* If not in the lexer's string table, it certainly isn't in the symbol table. | |
893 | * Doing this first is a lot faster. | |
894 | */ | |
895 | if (!seed.length) | |
896 | return null; | |
897 | Identifier id = Identifier.lookup(seed); | |
898 | if (!id) | |
899 | return null; | |
900 | cost = 0; // all the same cost | |
901 | Dsymbol s = this; | |
902 | Module.clearCache(); | |
903 | return s.search(Loc.initial, id, IgnoreErrors); | |
904 | } | |
905 | ||
906 | if (global.gag) | |
907 | return null; // don't do it for speculative compiles; too time consuming | |
908 | // search for exact name first | |
909 | if (auto s = search(Loc.initial, ident, IgnoreErrors)) | |
910 | return s; | |
911 | return speller!symbol_search_fp(ident.toString()); | |
912 | } | |
913 | ||
914 | /*************************************** | |
915 | * Search for identifier id as a member of `this`. | |
916 | * `id` may be a template instance. | |
917 | * | |
918 | * Params: | |
919 | * loc = location to print the error messages | |
920 | * sc = the scope where the symbol is located | |
921 | * id = the id of the symbol | |
922 | * flags = the search flags which can be `SearchLocalsOnly` or `IgnorePrivateImports` | |
923 | * | |
924 | * Returns: | |
925 | * symbol found, NULL if not | |
926 | */ | |
927 | extern (D) final Dsymbol searchX(const ref Loc loc, Scope* sc, RootObject id, int flags) | |
928 | { | |
929 | //printf("Dsymbol::searchX(this=%p,%s, ident='%s')\n", this, toChars(), ident.toChars()); | |
930 | Dsymbol s = toAlias(); | |
931 | Dsymbol sm; | |
932 | if (Declaration d = s.isDeclaration()) | |
933 | { | |
934 | if (d.inuse) | |
935 | { | |
936 | .error(loc, "circular reference to `%s`", d.toPrettyChars()); | |
937 | return null; | |
938 | } | |
939 | } | |
940 | switch (id.dyncast()) | |
941 | { | |
942 | case DYNCAST.identifier: | |
943 | sm = s.search(loc, cast(Identifier)id, flags); | |
944 | break; | |
945 | case DYNCAST.dsymbol: | |
946 | { | |
947 | // It's a template instance | |
948 | //printf("\ttemplate instance id\n"); | |
949 | Dsymbol st = cast(Dsymbol)id; | |
950 | TemplateInstance ti = st.isTemplateInstance(); | |
951 | sm = s.search(loc, ti.name); | |
952 | if (!sm) | |
5fee5ec3 | 953 | return null; |
5fee5ec3 IB |
954 | sm = sm.toAlias(); |
955 | TemplateDeclaration td = sm.isTemplateDeclaration(); | |
956 | if (!td) | |
957 | { | |
958 | .error(loc, "`%s.%s` is not a template, it is a %s", s.toPrettyChars(), ti.name.toChars(), sm.kind()); | |
959 | return null; | |
960 | } | |
961 | ti.tempdecl = td; | |
962 | if (!ti.semanticRun) | |
963 | ti.dsymbolSemantic(sc); | |
964 | sm = ti.toAlias(); | |
965 | break; | |
966 | } | |
967 | case DYNCAST.type: | |
968 | case DYNCAST.expression: | |
969 | default: | |
970 | assert(0); | |
971 | } | |
972 | return sm; | |
973 | } | |
974 | ||
975 | bool overloadInsert(Dsymbol s) | |
976 | { | |
977 | //printf("Dsymbol::overloadInsert('%s')\n", s.toChars()); | |
978 | return false; | |
979 | } | |
980 | ||
981 | /********************************* | |
982 | * Returns: | |
983 | * SIZE_INVALID when the size cannot be determined | |
984 | */ | |
fbdaa581 | 985 | uinteger_t size(const ref Loc loc) |
5fee5ec3 | 986 | { |
5eb9927a | 987 | error("symbol `%s` has no size", toChars()); |
5fee5ec3 IB |
988 | return SIZE_INVALID; |
989 | } | |
990 | ||
991 | bool isforwardRef() | |
992 | { | |
993 | return false; | |
994 | } | |
995 | ||
996 | // is a 'this' required to access the member | |
997 | inout(AggregateDeclaration) isThis() inout | |
998 | { | |
999 | return null; | |
1000 | } | |
1001 | ||
1002 | // is Dsymbol exported? | |
1003 | bool isExport() const | |
1004 | { | |
1005 | return false; | |
1006 | } | |
1007 | ||
1008 | // is Dsymbol imported? | |
1009 | bool isImportedSymbol() const | |
1010 | { | |
1011 | return false; | |
1012 | } | |
1013 | ||
1014 | // is Dsymbol deprecated? | |
1015 | bool isDeprecated() @safe @nogc pure nothrow const | |
1016 | { | |
1017 | return false; | |
1018 | } | |
1019 | ||
1020 | bool isOverloadable() const | |
1021 | { | |
1022 | return false; | |
1023 | } | |
1024 | ||
1025 | // is this a LabelDsymbol()? | |
1026 | LabelDsymbol isLabel() | |
1027 | { | |
1028 | return null; | |
1029 | } | |
1030 | ||
1031 | /// Returns an AggregateDeclaration when toParent() is that. | |
1032 | final inout(AggregateDeclaration) isMember() inout | |
1033 | { | |
1034 | //printf("Dsymbol::isMember() %s\n", toChars()); | |
1035 | auto p = toParent(); | |
1036 | //printf("parent is %s %s\n", p.kind(), p.toChars()); | |
1037 | return p ? p.isAggregateDeclaration() : null; | |
1038 | } | |
1039 | ||
1040 | /// Returns an AggregateDeclaration when toParent2() is that. | |
1041 | final inout(AggregateDeclaration) isMember2() inout | |
1042 | { | |
1043 | //printf("Dsymbol::isMember2() '%s'\n", toChars()); | |
1044 | auto p = toParent2(); | |
1045 | //printf("parent is %s %s\n", p.kind(), p.toChars()); | |
1046 | return p ? p.isAggregateDeclaration() : null; | |
1047 | } | |
1048 | ||
1049 | /// Returns an AggregateDeclaration when toParentDecl() is that. | |
1050 | final inout(AggregateDeclaration) isMemberDecl() inout | |
1051 | { | |
1052 | //printf("Dsymbol::isMemberDecl() '%s'\n", toChars()); | |
1053 | auto p = toParentDecl(); | |
1054 | //printf("parent is %s %s\n", p.kind(), p.toChars()); | |
1055 | return p ? p.isAggregateDeclaration() : null; | |
1056 | } | |
1057 | ||
1058 | /// Returns an AggregateDeclaration when toParentLocal() is that. | |
1059 | final inout(AggregateDeclaration) isMemberLocal() inout | |
1060 | { | |
1061 | //printf("Dsymbol::isMemberLocal() '%s'\n", toChars()); | |
1062 | auto p = toParentLocal(); | |
1063 | //printf("parent is %s %s\n", p.kind(), p.toChars()); | |
1064 | return p ? p.isAggregateDeclaration() : null; | |
1065 | } | |
1066 | ||
1067 | // is this a member of a ClassDeclaration? | |
1068 | final ClassDeclaration isClassMember() | |
1069 | { | |
1070 | auto ad = isMember(); | |
1071 | return ad ? ad.isClassDeclaration() : null; | |
1072 | } | |
1073 | ||
1074 | // is this a type? | |
1075 | Type getType() | |
1076 | { | |
1077 | return null; | |
1078 | } | |
1079 | ||
1080 | // need a 'this' pointer? | |
1081 | bool needThis() | |
1082 | { | |
1083 | return false; | |
1084 | } | |
1085 | ||
1086 | /************************************* | |
1087 | */ | |
1088 | Visibility visible() pure nothrow @nogc @safe | |
1089 | { | |
1090 | return Visibility(Visibility.Kind.public_); | |
1091 | } | |
1092 | ||
1093 | /************************************** | |
1094 | * Copy the syntax. | |
1095 | * Used for template instantiations. | |
1096 | * If s is NULL, allocate the new object, otherwise fill it in. | |
1097 | */ | |
1098 | Dsymbol syntaxCopy(Dsymbol s) | |
1099 | { | |
1100 | printf("%s %s\n", kind(), toChars()); | |
1101 | assert(0); | |
1102 | } | |
1103 | ||
1104 | /************************************** | |
1105 | * Determine if this symbol is only one. | |
1106 | * Returns: | |
1107 | * false, *ps = NULL: There are 2 or more symbols | |
1108 | * true, *ps = NULL: There are zero symbols | |
1109 | * true, *ps = symbol: The one and only one symbol | |
1110 | */ | |
1111 | bool oneMember(Dsymbol* ps, Identifier ident) | |
1112 | { | |
1113 | //printf("Dsymbol::oneMember()\n"); | |
1114 | *ps = this; | |
1115 | return true; | |
1116 | } | |
1117 | ||
1118 | /***************************************** | |
1119 | * Same as Dsymbol::oneMember(), but look at an array of Dsymbols. | |
1120 | */ | |
1121 | extern (D) static bool oneMembers(Dsymbols* members, Dsymbol* ps, Identifier ident) | |
1122 | { | |
1123 | //printf("Dsymbol::oneMembers() %d\n", members ? members.dim : 0); | |
1124 | Dsymbol s = null; | |
1125 | if (!members) | |
1126 | { | |
1127 | *ps = null; | |
1128 | return true; | |
1129 | } | |
1130 | ||
1131 | for (size_t i = 0; i < members.dim; i++) | |
1132 | { | |
1133 | Dsymbol sx = (*members)[i]; | |
1134 | bool x = sx.oneMember(ps, ident); | |
1135 | //printf("\t[%d] kind %s = %d, s = %p\n", i, sx.kind(), x, *ps); | |
1136 | if (!x) | |
1137 | { | |
1138 | //printf("\tfalse 1\n"); | |
1139 | assert(*ps is null); | |
1140 | return false; | |
1141 | } | |
1142 | if (*ps) | |
1143 | { | |
1144 | assert(ident); | |
1145 | if (!(*ps).ident || !(*ps).ident.equals(ident)) | |
1146 | continue; | |
1147 | if (!s) | |
1148 | s = *ps; | |
1149 | else if (s.isOverloadable() && (*ps).isOverloadable()) | |
1150 | { | |
1151 | // keep head of overload set | |
1152 | FuncDeclaration f1 = s.isFuncDeclaration(); | |
1153 | FuncDeclaration f2 = (*ps).isFuncDeclaration(); | |
1154 | if (f1 && f2) | |
1155 | { | |
1156 | assert(!f1.isFuncAliasDeclaration()); | |
1157 | assert(!f2.isFuncAliasDeclaration()); | |
1158 | for (; f1 != f2; f1 = f1.overnext0) | |
1159 | { | |
1160 | if (f1.overnext0 is null) | |
1161 | { | |
1162 | f1.overnext0 = f2; | |
1163 | break; | |
1164 | } | |
1165 | } | |
1166 | } | |
1167 | } | |
1168 | else // more than one symbol | |
1169 | { | |
1170 | *ps = null; | |
1171 | //printf("\tfalse 2\n"); | |
1172 | return false; | |
1173 | } | |
1174 | } | |
1175 | } | |
1176 | *ps = s; // s is the one symbol, null if none | |
1177 | //printf("\ttrue\n"); | |
1178 | return true; | |
1179 | } | |
1180 | ||
1181 | void setFieldOffset(AggregateDeclaration ad, ref FieldState fieldState, bool isunion) | |
1182 | { | |
1183 | } | |
1184 | ||
1185 | /***************************************** | |
1186 | * Is Dsymbol a variable that contains pointers? | |
1187 | */ | |
1188 | bool hasPointers() | |
1189 | { | |
1190 | //printf("Dsymbol::hasPointers() %s\n", toChars()); | |
1191 | return false; | |
1192 | } | |
1193 | ||
1194 | bool hasStaticCtorOrDtor() | |
1195 | { | |
1196 | //printf("Dsymbol::hasStaticCtorOrDtor() %s\n", toChars()); | |
1197 | return false; | |
1198 | } | |
1199 | ||
1200 | void addLocalClass(ClassDeclarations*) | |
1201 | { | |
1202 | } | |
1203 | ||
1204 | void addObjcSymbols(ClassDeclarations* classes, ClassDeclarations* categories) | |
1205 | { | |
1206 | } | |
1207 | ||
1208 | void checkCtorConstInit() | |
1209 | { | |
1210 | } | |
1211 | ||
1212 | /**************************************** | |
1213 | * Add documentation comment to Dsymbol. | |
1214 | * Ignore NULL comments. | |
1215 | */ | |
1216 | void addComment(const(char)* comment) | |
1217 | { | |
7e287503 IB |
1218 | if (!comment || !*comment) |
1219 | return; | |
1220 | ||
1221 | //printf("addComment '%s' to Dsymbol %p '%s'\n", comment, this, toChars()); | |
1222 | void* h = cast(void*)this; // just the pointer is the key | |
1223 | auto p = h in commentHashTable; | |
1224 | if (!p) | |
1225 | { | |
1226 | commentHashTable[h] = comment; | |
1227 | return; | |
1228 | } | |
1229 | if (strcmp(*p, comment) != 0) | |
5fee5ec3 IB |
1230 | { |
1231 | // Concatenate the two | |
7e287503 | 1232 | *p = Lexer.combineComments((*p).toDString(), comment.toDString(), true); |
5fee5ec3 IB |
1233 | } |
1234 | } | |
1235 | ||
7e287503 IB |
1236 | /// get documentation comment for this Dsymbol |
1237 | final const(char)* comment() | |
1238 | { | |
1239 | //printf("getcomment: %p '%s'\n", this, this.toChars()); | |
1240 | if (auto p = cast(void*)this in commentHashTable) | |
1241 | { | |
1242 | //printf("comment: '%s'\n", *p); | |
1243 | return *p; | |
1244 | } | |
1245 | return null; | |
1246 | } | |
1247 | ||
1248 | /* Shell around addComment() to avoid disruption for the moment */ | |
1249 | final void comment(const(char)* comment) { addComment(comment); } | |
1250 | ||
1251 | private extern (D) __gshared const(char)*[void*] commentHashTable; | |
1252 | ||
1253 | ||
1254 | /********************************** | |
1255 | * Get ddoc unittest associated with this symbol. | |
1256 | * (only use this with ddoc) | |
1257 | * Returns: ddoc unittest, null if none | |
1258 | */ | |
1259 | final UnitTestDeclaration ddocUnittest() | |
1260 | { | |
1261 | if (auto p = cast(void*)this in ddocUnittestHashTable) | |
1262 | return *p; | |
1263 | return null; | |
1264 | } | |
1265 | ||
1266 | /********************************** | |
1267 | * Set ddoc unittest associated with this symbol. | |
1268 | */ | |
1269 | final void ddocUnittest(UnitTestDeclaration utd) | |
1270 | { | |
1271 | ddocUnittestHashTable[cast(void*)this] = utd; | |
1272 | } | |
1273 | ||
1274 | private extern (D) __gshared UnitTestDeclaration[void*] ddocUnittestHashTable; | |
1275 | ||
1276 | ||
5fee5ec3 IB |
1277 | /**************************************** |
1278 | * Returns true if this symbol is defined in a non-root module without instantiation. | |
1279 | */ | |
1280 | final bool inNonRoot() | |
1281 | { | |
1282 | Dsymbol s = parent; | |
1283 | for (; s; s = s.toParent()) | |
1284 | { | |
1285 | if (auto ti = s.isTemplateInstance()) | |
1286 | { | |
1287 | return false; | |
1288 | } | |
1289 | if (auto m = s.isModule()) | |
1290 | { | |
1291 | if (!m.isRoot()) | |
1292 | return true; | |
1293 | break; | |
1294 | } | |
1295 | } | |
1296 | return false; | |
1297 | } | |
1298 | ||
7e287503 IB |
1299 | /** |
1300 | * Deinitializes the global state of the compiler. | |
1301 | * | |
1302 | * This can be used to restore the state set by `_init` to its original | |
1303 | * state. | |
1304 | */ | |
1305 | static void deinitialize() | |
1306 | { | |
1307 | commentHashTable = commentHashTable.init; | |
1308 | ddocUnittestHashTable = ddocUnittestHashTable.init; | |
1309 | } | |
1310 | ||
5fee5ec3 IB |
1311 | /************ |
1312 | */ | |
1313 | override void accept(Visitor v) | |
1314 | { | |
1315 | v.visit(this); | |
1316 | } | |
1317 | ||
1318 | pure nothrow @safe @nogc: | |
1319 | ||
1320 | // Eliminate need for dynamic_cast | |
1321 | inout(Package) isPackage() inout { return null; } | |
1322 | inout(Module) isModule() inout { return null; } | |
1323 | inout(EnumMember) isEnumMember() inout { return null; } | |
1324 | inout(TemplateDeclaration) isTemplateDeclaration() inout { return null; } | |
1325 | inout(TemplateInstance) isTemplateInstance() inout { return null; } | |
1326 | inout(TemplateMixin) isTemplateMixin() inout { return null; } | |
1327 | inout(ForwardingAttribDeclaration) isForwardingAttribDeclaration() inout { return null; } | |
1328 | inout(Nspace) isNspace() inout { return null; } | |
1329 | inout(Declaration) isDeclaration() inout { return null; } | |
1330 | inout(StorageClassDeclaration) isStorageClassDeclaration() inout { return null; } | |
1331 | inout(ExpressionDsymbol) isExpressionDsymbol() inout { return null; } | |
1332 | inout(AliasAssign) isAliasAssign() inout { return null; } | |
1333 | inout(ThisDeclaration) isThisDeclaration() inout { return null; } | |
1334 | inout(BitFieldDeclaration) isBitFieldDeclaration() inout { return null; } | |
1335 | inout(TypeInfoDeclaration) isTypeInfoDeclaration() inout { return null; } | |
1336 | inout(TupleDeclaration) isTupleDeclaration() inout { return null; } | |
1337 | inout(AliasDeclaration) isAliasDeclaration() inout { return null; } | |
1338 | inout(AggregateDeclaration) isAggregateDeclaration() inout { return null; } | |
1339 | inout(FuncDeclaration) isFuncDeclaration() inout { return null; } | |
1340 | inout(FuncAliasDeclaration) isFuncAliasDeclaration() inout { return null; } | |
1341 | inout(OverDeclaration) isOverDeclaration() inout { return null; } | |
1342 | inout(FuncLiteralDeclaration) isFuncLiteralDeclaration() inout { return null; } | |
1343 | inout(CtorDeclaration) isCtorDeclaration() inout { return null; } | |
1344 | inout(PostBlitDeclaration) isPostBlitDeclaration() inout { return null; } | |
1345 | inout(DtorDeclaration) isDtorDeclaration() inout { return null; } | |
1346 | inout(StaticCtorDeclaration) isStaticCtorDeclaration() inout { return null; } | |
1347 | inout(StaticDtorDeclaration) isStaticDtorDeclaration() inout { return null; } | |
1348 | inout(SharedStaticCtorDeclaration) isSharedStaticCtorDeclaration() inout { return null; } | |
1349 | inout(SharedStaticDtorDeclaration) isSharedStaticDtorDeclaration() inout { return null; } | |
1350 | inout(InvariantDeclaration) isInvariantDeclaration() inout { return null; } | |
1351 | inout(UnitTestDeclaration) isUnitTestDeclaration() inout { return null; } | |
1352 | inout(NewDeclaration) isNewDeclaration() inout { return null; } | |
1353 | inout(VarDeclaration) isVarDeclaration() inout { return null; } | |
1354 | inout(VersionSymbol) isVersionSymbol() inout { return null; } | |
1355 | inout(DebugSymbol) isDebugSymbol() inout { return null; } | |
1356 | inout(ClassDeclaration) isClassDeclaration() inout { return null; } | |
1357 | inout(StructDeclaration) isStructDeclaration() inout { return null; } | |
1358 | inout(UnionDeclaration) isUnionDeclaration() inout { return null; } | |
1359 | inout(InterfaceDeclaration) isInterfaceDeclaration() inout { return null; } | |
1360 | inout(ScopeDsymbol) isScopeDsymbol() inout { return null; } | |
1361 | inout(ForwardingScopeDsymbol) isForwardingScopeDsymbol() inout { return null; } | |
1362 | inout(WithScopeSymbol) isWithScopeSymbol() inout { return null; } | |
1363 | inout(ArrayScopeSymbol) isArrayScopeSymbol() inout { return null; } | |
1364 | inout(Import) isImport() inout { return null; } | |
1365 | inout(EnumDeclaration) isEnumDeclaration() inout { return null; } | |
1366 | inout(SymbolDeclaration) isSymbolDeclaration() inout { return null; } | |
1367 | inout(AttribDeclaration) isAttribDeclaration() inout { return null; } | |
1368 | inout(AnonDeclaration) isAnonDeclaration() inout { return null; } | |
1369 | inout(CPPNamespaceDeclaration) isCPPNamespaceDeclaration() inout { return null; } | |
6384eff5 | 1370 | inout(VisibilityDeclaration) isVisibilityDeclaration() inout { return null; } |
5fee5ec3 IB |
1371 | inout(OverloadSet) isOverloadSet() inout { return null; } |
1372 | inout(CompileDeclaration) isCompileDeclaration() inout { return null; } | |
6384eff5 | 1373 | inout(StaticAssert) isStaticAssert() inout { return null; } |
5fee5ec3 IB |
1374 | } |
1375 | ||
1376 | /*********************************************************** | |
1377 | * Dsymbol that generates a scope | |
1378 | */ | |
1379 | extern (C++) class ScopeDsymbol : Dsymbol | |
1380 | { | |
1381 | Dsymbols* members; // all Dsymbol's in this scope | |
1382 | DsymbolTable symtab; // members[] sorted into table | |
1383 | uint endlinnum; // the linnumber of the statement after the scope (0 if unknown) | |
1384 | ||
1385 | private: | |
1386 | /// symbols whose members have been imported, i.e. imported modules and template mixins | |
1387 | Dsymbols* importedScopes; | |
1388 | Visibility.Kind* visibilities; // array of Visibility.Kind, one for each import | |
1389 | ||
1390 | import dmd.root.bitarray; | |
1391 | BitArray accessiblePackages, privateAccessiblePackages;// whitelists of accessible (imported) packages | |
1392 | ||
1393 | public: | |
31350635 | 1394 | final extern (D) this() nothrow |
5fee5ec3 IB |
1395 | { |
1396 | } | |
1397 | ||
31350635 | 1398 | final extern (D) this(Identifier ident) nothrow |
5fee5ec3 IB |
1399 | { |
1400 | super(ident); | |
1401 | } | |
1402 | ||
31350635 | 1403 | final extern (D) this(const ref Loc loc, Identifier ident) nothrow |
5fee5ec3 IB |
1404 | { |
1405 | super(loc, ident); | |
1406 | } | |
1407 | ||
1408 | override ScopeDsymbol syntaxCopy(Dsymbol s) | |
1409 | { | |
1410 | //printf("ScopeDsymbol::syntaxCopy('%s')\n", toChars()); | |
1411 | ScopeDsymbol sds = s ? cast(ScopeDsymbol)s : new ScopeDsymbol(ident); | |
1412 | sds.comment = comment; | |
1413 | sds.members = arraySyntaxCopy(members); | |
1414 | sds.endlinnum = endlinnum; | |
1415 | return sds; | |
1416 | } | |
1417 | ||
1418 | /***************************************** | |
1419 | * This function is #1 on the list of functions that eat cpu time. | |
1420 | * Be very, very careful about slowing it down. | |
1421 | */ | |
1422 | override Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly) | |
1423 | { | |
1424 | //printf("%s.ScopeDsymbol::search(ident='%s', flags=x%x)\n", toChars(), ident.toChars(), flags); | |
1425 | //if (strcmp(ident.toChars(),"c") == 0) *(char*)0=0; | |
1426 | ||
1427 | // Look in symbols declared in this module | |
1428 | if (symtab && !(flags & SearchImportsOnly)) | |
1429 | { | |
1430 | //printf(" look in locals\n"); | |
1431 | auto s1 = symtab.lookup(ident); | |
1432 | if (s1) | |
1433 | { | |
1434 | //printf("\tfound in locals = '%s.%s'\n",toChars(),s1.toChars()); | |
1435 | return s1; | |
1436 | } | |
1437 | } | |
1438 | //printf(" not found in locals\n"); | |
1439 | ||
1440 | // Look in imported scopes | |
1441 | if (!importedScopes) | |
1442 | return null; | |
1443 | ||
1444 | //printf(" look in imports\n"); | |
1445 | Dsymbol s = null; | |
1446 | OverloadSet a = null; | |
1447 | // Look in imported modules | |
1448 | for (size_t i = 0; i < importedScopes.dim; i++) | |
1449 | { | |
1450 | // If private import, don't search it | |
1451 | if ((flags & IgnorePrivateImports) && visibilities[i] == Visibility.Kind.private_) | |
1452 | continue; | |
1453 | int sflags = flags & (IgnoreErrors | IgnoreAmbiguous); // remember these in recursive searches | |
1454 | Dsymbol ss = (*importedScopes)[i]; | |
1455 | //printf("\tscanning import '%s', visibilities = %d, isModule = %p, isImport = %p\n", ss.toChars(), visibilities[i], ss.isModule(), ss.isImport()); | |
1456 | ||
1457 | if (ss.isModule()) | |
1458 | { | |
1459 | if (flags & SearchLocalsOnly) | |
1460 | continue; | |
1461 | } | |
1462 | else // mixin template | |
1463 | { | |
1464 | if (flags & SearchImportsOnly) | |
1465 | continue; | |
1466 | ||
1467 | sflags |= SearchLocalsOnly; | |
1468 | } | |
1469 | ||
1470 | /* Don't find private members if ss is a module | |
1471 | */ | |
1472 | Dsymbol s2 = ss.search(loc, ident, sflags | (ss.isModule() ? IgnorePrivateImports : IgnoreNone)); | |
1473 | import dmd.access : symbolIsVisible; | |
1474 | if (!s2 || !(flags & IgnoreSymbolVisibility) && !symbolIsVisible(this, s2)) | |
1475 | continue; | |
1476 | if (!s) | |
1477 | { | |
1478 | s = s2; | |
1479 | if (s && s.isOverloadSet()) | |
1480 | a = mergeOverloadSet(ident, a, s); | |
1481 | } | |
1482 | else if (s2 && s != s2) | |
1483 | { | |
1484 | if (s.toAlias() == s2.toAlias() || s.getType() == s2.getType() && s.getType()) | |
1485 | { | |
1486 | /* After following aliases, we found the same | |
1487 | * symbol, so it's not an ambiguity. But if one | |
1488 | * alias is deprecated or less accessible, prefer | |
1489 | * the other. | |
1490 | */ | |
1491 | if (s.isDeprecated() || s.visible() < s2.visible() && s2.visible().kind != Visibility.Kind.none) | |
1492 | s = s2; | |
1493 | } | |
1494 | else | |
1495 | { | |
1496 | /* Two imports of the same module should be regarded as | |
1497 | * the same. | |
1498 | */ | |
1499 | Import i1 = s.isImport(); | |
1500 | Import i2 = s2.isImport(); | |
1501 | if (!(i1 && i2 && (i1.mod == i2.mod || (!i1.parent.isImport() && !i2.parent.isImport() && i1.ident.equals(i2.ident))))) | |
1502 | { | |
1503 | /* https://issues.dlang.org/show_bug.cgi?id=8668 | |
1504 | * Public selective import adds AliasDeclaration in module. | |
1505 | * To make an overload set, resolve aliases in here and | |
1506 | * get actual overload roots which accessible via s and s2. | |
1507 | */ | |
1508 | s = s.toAlias(); | |
1509 | s2 = s2.toAlias(); | |
1510 | /* If both s2 and s are overloadable (though we only | |
1511 | * need to check s once) | |
1512 | */ | |
1513 | ||
1514 | auto so2 = s2.isOverloadSet(); | |
1515 | if ((so2 || s2.isOverloadable()) && (a || s.isOverloadable())) | |
1516 | { | |
1517 | if (symbolIsVisible(this, s2)) | |
1518 | { | |
1519 | a = mergeOverloadSet(ident, a, s2); | |
1520 | } | |
1521 | if (!symbolIsVisible(this, s)) | |
1522 | s = s2; | |
1523 | continue; | |
1524 | } | |
1525 | ||
1526 | /* Two different overflow sets can have the same members | |
1527 | * https://issues.dlang.org/show_bug.cgi?id=16709 | |
1528 | */ | |
1529 | auto so = s.isOverloadSet(); | |
1530 | if (so && so2) | |
1531 | { | |
1532 | if (so.a.length == so2.a.length) | |
1533 | { | |
1534 | foreach (j; 0 .. so.a.length) | |
1535 | { | |
1536 | if (so.a[j] !is so2.a[j]) | |
1537 | goto L1; | |
1538 | } | |
1539 | continue; // the same | |
1540 | L1: | |
1541 | { } // different | |
1542 | } | |
1543 | } | |
1544 | ||
1545 | if (flags & IgnoreAmbiguous) // if return NULL on ambiguity | |
1546 | return null; | |
c8dfa79c IB |
1547 | |
1548 | /* If two imports from C import files, pick first one, as C has global name space | |
1549 | */ | |
1550 | if (s.isCsymbol() && s2.isCsymbol()) | |
1551 | continue; | |
1552 | ||
5fee5ec3 IB |
1553 | if (!(flags & IgnoreErrors)) |
1554 | ScopeDsymbol.multiplyDefined(loc, s, s2); | |
1555 | break; | |
1556 | } | |
1557 | } | |
1558 | } | |
1559 | } | |
1560 | if (s) | |
1561 | { | |
1562 | /* Build special symbol if we had multiple finds | |
1563 | */ | |
1564 | if (a) | |
1565 | { | |
1566 | if (!s.isOverloadSet()) | |
1567 | a = mergeOverloadSet(ident, a, s); | |
1568 | s = a; | |
1569 | } | |
1570 | //printf("\tfound in imports %s.%s\n", toChars(), s.toChars()); | |
1571 | return s; | |
1572 | } | |
1573 | //printf(" not found in imports\n"); | |
1574 | return null; | |
1575 | } | |
1576 | ||
1577 | extern (D) private OverloadSet mergeOverloadSet(Identifier ident, OverloadSet os, Dsymbol s) | |
1578 | { | |
1579 | if (!os) | |
1580 | { | |
1581 | os = new OverloadSet(ident); | |
1582 | os.parent = this; | |
1583 | } | |
1584 | if (OverloadSet os2 = s.isOverloadSet()) | |
1585 | { | |
1586 | // Merge the cross-module overload set 'os2' into 'os' | |
1587 | if (os.a.dim == 0) | |
1588 | { | |
1589 | os.a.setDim(os2.a.dim); | |
1590 | memcpy(os.a.tdata(), os2.a.tdata(), (os.a[0]).sizeof * os2.a.dim); | |
1591 | } | |
1592 | else | |
1593 | { | |
1594 | for (size_t i = 0; i < os2.a.dim; i++) | |
1595 | { | |
1596 | os = mergeOverloadSet(ident, os, os2.a[i]); | |
1597 | } | |
1598 | } | |
1599 | } | |
1600 | else | |
1601 | { | |
1602 | assert(s.isOverloadable()); | |
1603 | /* Don't add to os[] if s is alias of previous sym | |
1604 | */ | |
1605 | for (size_t j = 0; j < os.a.dim; j++) | |
1606 | { | |
1607 | Dsymbol s2 = os.a[j]; | |
1608 | if (s.toAlias() == s2.toAlias()) | |
1609 | { | |
1610 | if (s2.isDeprecated() || (s2.visible() < s.visible() && s.visible().kind != Visibility.Kind.none)) | |
1611 | { | |
1612 | os.a[j] = s; | |
1613 | } | |
1614 | goto Lcontinue; | |
1615 | } | |
1616 | } | |
1617 | os.push(s); | |
1618 | Lcontinue: | |
1619 | } | |
1620 | return os; | |
1621 | } | |
1622 | ||
31350635 | 1623 | void importScope(Dsymbol s, Visibility visibility) nothrow |
5fee5ec3 IB |
1624 | { |
1625 | //printf("%s.ScopeDsymbol::importScope(%s, %d)\n", toChars(), s.toChars(), visibility); | |
1626 | // No circular or redundant import's | |
1627 | if (s != this) | |
1628 | { | |
1629 | if (!importedScopes) | |
1630 | importedScopes = new Dsymbols(); | |
1631 | else | |
1632 | { | |
1633 | for (size_t i = 0; i < importedScopes.dim; i++) | |
1634 | { | |
1635 | Dsymbol ss = (*importedScopes)[i]; | |
1636 | if (ss == s) // if already imported | |
1637 | { | |
1638 | if (visibility.kind > visibilities[i]) | |
1639 | visibilities[i] = visibility.kind; // upgrade access | |
1640 | return; | |
1641 | } | |
1642 | } | |
1643 | } | |
1644 | importedScopes.push(s); | |
1645 | visibilities = cast(Visibility.Kind*)mem.xrealloc(visibilities, importedScopes.dim * (visibilities[0]).sizeof); | |
1646 | visibilities[importedScopes.dim - 1] = visibility.kind; | |
1647 | } | |
1648 | } | |
1649 | ||
5eb9927a IB |
1650 | |
1651 | /***************************************** | |
1652 | * Returns: the symbols whose members have been imported, i.e. imported modules | |
1653 | * and template mixins. | |
1654 | * | |
1655 | * See_Also: importScope | |
1656 | */ | |
1657 | extern (D) final Dsymbols* getImportedScopes() nothrow @nogc @safe pure | |
1658 | { | |
1659 | return importedScopes; | |
1660 | } | |
1661 | ||
1662 | /***************************************** | |
1663 | * Returns: the array of visibilities associated with each imported scope. The | |
1664 | * length of the array matches the imported scopes array. | |
1665 | * | |
1666 | * See_Also: getImportedScopes | |
1667 | */ | |
1668 | extern (D) final Visibility.Kind[] getImportVisibilities() nothrow @nogc @safe pure | |
1669 | { | |
1670 | if (!importedScopes) | |
1671 | return null; | |
1672 | ||
1673 | return (() @trusted => visibilities[0 .. importedScopes.dim])(); | |
1674 | } | |
1675 | ||
31350635 | 1676 | extern (D) final void addAccessiblePackage(Package p, Visibility visibility) nothrow |
5fee5ec3 IB |
1677 | { |
1678 | auto pary = visibility.kind == Visibility.Kind.private_ ? &privateAccessiblePackages : &accessiblePackages; | |
1679 | if (pary.length <= p.tag) | |
1680 | pary.length = p.tag + 1; | |
1681 | (*pary)[p.tag] = true; | |
1682 | } | |
1683 | ||
31350635 | 1684 | bool isPackageAccessible(Package p, Visibility visibility, int flags = 0) nothrow |
5fee5ec3 IB |
1685 | { |
1686 | if (p.tag < accessiblePackages.length && accessiblePackages[p.tag] || | |
1687 | visibility.kind == Visibility.Kind.private_ && p.tag < privateAccessiblePackages.length && privateAccessiblePackages[p.tag]) | |
1688 | return true; | |
1689 | foreach (i, ss; importedScopes ? (*importedScopes)[] : null) | |
1690 | { | |
1691 | // only search visible scopes && imported modules should ignore private imports | |
1692 | if (visibility.kind <= visibilities[i] && | |
1693 | ss.isScopeDsymbol.isPackageAccessible(p, visibility, IgnorePrivateImports)) | |
1694 | return true; | |
1695 | } | |
1696 | return false; | |
1697 | } | |
1698 | ||
31350635 | 1699 | override final bool isforwardRef() nothrow |
5fee5ec3 IB |
1700 | { |
1701 | return (members is null); | |
1702 | } | |
1703 | ||
1704 | static void multiplyDefined(const ref Loc loc, Dsymbol s1, Dsymbol s2) | |
1705 | { | |
1706 | version (none) | |
1707 | { | |
1708 | printf("ScopeDsymbol::multiplyDefined()\n"); | |
1709 | printf("s1 = %p, '%s' kind = '%s', parent = %s\n", s1, s1.toChars(), s1.kind(), s1.parent ? s1.parent.toChars() : ""); | |
1710 | printf("s2 = %p, '%s' kind = '%s', parent = %s\n", s2, s2.toChars(), s2.kind(), s2.parent ? s2.parent.toChars() : ""); | |
1711 | } | |
1712 | if (loc.isValid()) | |
1713 | { | |
1714 | .error(loc, "%s `%s` at %s conflicts with %s `%s` at %s", | |
1715 | s1.kind(), s1.toPrettyChars(), s1.locToChars(), | |
1716 | s2.kind(), s2.toPrettyChars(), s2.locToChars()); | |
1717 | ||
1718 | static if (0) | |
1719 | { | |
1720 | if (auto so = s1.isOverloadSet()) | |
1721 | { | |
1722 | printf("first %p:\n", so); | |
1723 | foreach (s; so.a[]) | |
1724 | { | |
1725 | printf(" %p %s `%s` at %s\n", s, s.kind(), s.toPrettyChars(), s.locToChars()); | |
1726 | } | |
1727 | } | |
1728 | if (auto so = s2.isOverloadSet()) | |
1729 | { | |
1730 | printf("second %p:\n", so); | |
1731 | foreach (s; so.a[]) | |
1732 | { | |
1733 | printf(" %p %s `%s` at %s\n", s, s.kind(), s.toPrettyChars(), s.locToChars()); | |
1734 | } | |
1735 | } | |
1736 | } | |
1737 | } | |
1738 | else | |
1739 | { | |
1740 | s1.error(s1.loc, "conflicts with %s `%s` at %s", s2.kind(), s2.toPrettyChars(), s2.locToChars()); | |
1741 | } | |
1742 | } | |
1743 | ||
1744 | override const(char)* kind() const | |
1745 | { | |
1746 | return "ScopeDsymbol"; | |
1747 | } | |
1748 | ||
1749 | /******************************************* | |
1750 | * Look for member of the form: | |
1751 | * const(MemberInfo)[] getMembers(string); | |
1752 | * Returns NULL if not found | |
1753 | */ | |
1754 | final FuncDeclaration findGetMembers() | |
1755 | { | |
1756 | Dsymbol s = search_function(this, Id.getmembers); | |
1757 | FuncDeclaration fdx = s ? s.isFuncDeclaration() : null; | |
1758 | version (none) | |
1759 | { | |
1760 | // Finish | |
1761 | __gshared TypeFunction tfgetmembers; | |
1762 | if (!tfgetmembers) | |
1763 | { | |
1764 | Scope sc; | |
1765 | auto parameters = new Parameters(); | |
1766 | Parameters* p = new Parameter(STC.in_, Type.tchar.constOf().arrayOf(), null, null); | |
1767 | parameters.push(p); | |
1768 | Type tret = null; | |
1769 | tfgetmembers = new TypeFunction(parameters, tret, VarArg.none, LINK.d); | |
1770 | tfgetmembers = cast(TypeFunction)tfgetmembers.dsymbolSemantic(Loc.initial, &sc); | |
1771 | } | |
1772 | if (fdx) | |
1773 | fdx = fdx.overloadExactMatch(tfgetmembers); | |
1774 | } | |
1775 | if (fdx && fdx.isVirtual()) | |
1776 | fdx = null; | |
1777 | return fdx; | |
1778 | } | |
1779 | ||
1780 | /******************************** | |
1781 | * Insert Dsymbol in table. | |
1782 | * Params: | |
1783 | * s = symbol to add | |
1784 | * Returns: | |
1785 | * null if already in table, `s` if inserted | |
1786 | */ | |
31350635 | 1787 | Dsymbol symtabInsert(Dsymbol s) nothrow |
5fee5ec3 IB |
1788 | { |
1789 | return symtab.insert(s); | |
1790 | } | |
1791 | ||
1792 | /**************************************** | |
1793 | * Look up identifier in symbol table. | |
1794 | * Params: | |
1795 | * s = symbol | |
1796 | * id = identifier to look up | |
1797 | * Returns: | |
1798 | * Dsymbol if found, null if not | |
1799 | */ | |
31350635 | 1800 | Dsymbol symtabLookup(Dsymbol s, Identifier id) nothrow |
5fee5ec3 IB |
1801 | { |
1802 | return symtab.lookup(id); | |
1803 | } | |
1804 | ||
1805 | /**************************************** | |
1806 | * Return true if any of the members are static ctors or static dtors, or if | |
1807 | * any members have members that are. | |
1808 | */ | |
1809 | override bool hasStaticCtorOrDtor() | |
1810 | { | |
1811 | if (members) | |
1812 | { | |
1813 | for (size_t i = 0; i < members.dim; i++) | |
1814 | { | |
1815 | Dsymbol member = (*members)[i]; | |
1816 | if (member.hasStaticCtorOrDtor()) | |
1817 | return true; | |
1818 | } | |
1819 | } | |
1820 | return false; | |
1821 | } | |
1822 | ||
1823 | extern (D) alias ForeachDg = int delegate(size_t idx, Dsymbol s); | |
1824 | ||
1825 | /*************************************** | |
1826 | * Expands attribute declarations in members in depth first | |
1827 | * order. Calls dg(size_t symidx, Dsymbol *sym) for each | |
1828 | * member. | |
1829 | * If dg returns !=0, stops and returns that value else returns 0. | |
1830 | * Use this function to avoid the O(N + N^2/2) complexity of | |
1831 | * calculating dim and calling N times getNth. | |
1832 | * Returns: | |
1833 | * last value returned by dg() | |
1834 | */ | |
1835 | extern (D) static int _foreach(Scope* sc, Dsymbols* members, scope ForeachDg dg, size_t* pn = null) | |
1836 | { | |
1837 | assert(dg); | |
1838 | if (!members) | |
1839 | return 0; | |
1840 | size_t n = pn ? *pn : 0; // take over index | |
1841 | int result = 0; | |
1842 | foreach (size_t i; 0 .. members.dim) | |
1843 | { | |
1844 | Dsymbol s = (*members)[i]; | |
1845 | if (AttribDeclaration a = s.isAttribDeclaration()) | |
1846 | result = _foreach(sc, a.include(sc), dg, &n); | |
1847 | else if (TemplateMixin tm = s.isTemplateMixin()) | |
1848 | result = _foreach(sc, tm.members, dg, &n); | |
1849 | else if (s.isTemplateInstance()) | |
1850 | { | |
1851 | } | |
1852 | else if (s.isUnitTestDeclaration()) | |
1853 | { | |
1854 | } | |
1855 | else | |
1856 | result = dg(n++, s); | |
1857 | if (result) | |
1858 | break; | |
1859 | } | |
1860 | if (pn) | |
1861 | *pn = n; // update index | |
1862 | return result; | |
1863 | } | |
1864 | ||
1865 | override final inout(ScopeDsymbol) isScopeDsymbol() inout | |
1866 | { | |
1867 | return this; | |
1868 | } | |
1869 | ||
1870 | override void accept(Visitor v) | |
1871 | { | |
1872 | v.visit(this); | |
1873 | } | |
1874 | } | |
1875 | ||
1876 | /*********************************************************** | |
1877 | * With statement scope | |
1878 | */ | |
1879 | extern (C++) final class WithScopeSymbol : ScopeDsymbol | |
1880 | { | |
1881 | WithStatement withstate; | |
1882 | ||
31350635 | 1883 | extern (D) this(WithStatement withstate) nothrow |
5fee5ec3 IB |
1884 | { |
1885 | this.withstate = withstate; | |
1886 | } | |
1887 | ||
1888 | override Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly) | |
1889 | { | |
1890 | //printf("WithScopeSymbol.search(%s)\n", ident.toChars()); | |
1891 | if (flags & SearchImportsOnly) | |
1892 | return null; | |
1893 | // Acts as proxy to the with class declaration | |
1894 | Dsymbol s = null; | |
1895 | Expression eold = null; | |
fd43568c | 1896 | for (Expression e = withstate.exp; e && e != eold; e = resolveAliasThis(_scope, e, true)) |
5fee5ec3 | 1897 | { |
9c7d5e88 | 1898 | if (e.op == EXP.scope_) |
5fee5ec3 IB |
1899 | { |
1900 | s = (cast(ScopeExp)e).sds; | |
1901 | } | |
9c7d5e88 | 1902 | else if (e.op == EXP.type) |
5fee5ec3 IB |
1903 | { |
1904 | s = e.type.toDsymbol(null); | |
1905 | } | |
1906 | else | |
1907 | { | |
1908 | Type t = e.type.toBasetype(); | |
1909 | s = t.toDsymbol(null); | |
1910 | } | |
1911 | if (s) | |
1912 | { | |
1913 | s = s.search(loc, ident, flags); | |
1914 | if (s) | |
1915 | return s; | |
1916 | } | |
1917 | eold = e; | |
1918 | } | |
1919 | return null; | |
1920 | } | |
1921 | ||
1922 | override inout(WithScopeSymbol) isWithScopeSymbol() inout | |
1923 | { | |
1924 | return this; | |
1925 | } | |
1926 | ||
1927 | override void accept(Visitor v) | |
1928 | { | |
1929 | v.visit(this); | |
1930 | } | |
1931 | } | |
1932 | ||
1933 | /*********************************************************** | |
1934 | * Array Index/Slice scope | |
1935 | */ | |
1936 | extern (C++) final class ArrayScopeSymbol : ScopeDsymbol | |
1937 | { | |
1938 | // either a SliceExp, an IndexExp, an ArrayExp, a TypeTuple or a TupleDeclaration. | |
9c7d5e88 | 1939 | // Discriminated using DYNCAST and, for expressions, also EXP |
5fee5ec3 IB |
1940 | private RootObject arrayContent; |
1941 | Scope* sc; | |
1942 | ||
31350635 | 1943 | extern (D) this(Scope* sc, Expression exp) nothrow |
5fee5ec3 IB |
1944 | { |
1945 | super(exp.loc, null); | |
9c7d5e88 | 1946 | assert(exp.op == EXP.index || exp.op == EXP.slice || exp.op == EXP.array); |
5fee5ec3 IB |
1947 | this.sc = sc; |
1948 | this.arrayContent = exp; | |
1949 | } | |
1950 | ||
31350635 | 1951 | extern (D) this(Scope* sc, TypeTuple type) nothrow |
5fee5ec3 IB |
1952 | { |
1953 | this.sc = sc; | |
1954 | this.arrayContent = type; | |
1955 | } | |
1956 | ||
31350635 | 1957 | extern (D) this(Scope* sc, TupleDeclaration td) nothrow |
5fee5ec3 IB |
1958 | { |
1959 | this.sc = sc; | |
1960 | this.arrayContent = td; | |
1961 | } | |
1962 | ||
1963 | /// This override is used to solve `$` | |
1964 | override Dsymbol search(const ref Loc loc, Identifier ident, int flags = IgnoreNone) | |
1965 | { | |
1966 | //printf("ArrayScopeSymbol::search('%s', flags = %d)\n", ident.toChars(), flags); | |
1967 | if (ident != Id.dollar) | |
1968 | return null; | |
1969 | ||
1970 | VarDeclaration* pvar; | |
1971 | Expression ce; | |
1972 | ||
1973 | static Dsymbol dollarFromTypeTuple(const ref Loc loc, TypeTuple tt, Scope* sc) | |
1974 | { | |
1975 | ||
1976 | /* $ gives the number of type entries in the type tuple | |
1977 | */ | |
1978 | auto v = new VarDeclaration(loc, Type.tsize_t, Id.dollar, null); | |
1979 | Expression e = new IntegerExp(Loc.initial, tt.arguments.dim, Type.tsize_t); | |
1980 | v._init = new ExpInitializer(Loc.initial, e); | |
1981 | v.storage_class |= STC.temp | STC.static_ | STC.const_; | |
1982 | v.dsymbolSemantic(sc); | |
1983 | return v; | |
1984 | } | |
1985 | ||
1986 | const DYNCAST kind = arrayContent.dyncast(); | |
610d7898 | 1987 | switch (kind) with (DYNCAST) |
5fee5ec3 | 1988 | { |
610d7898 | 1989 | case dsymbol: |
5fee5ec3 IB |
1990 | TupleDeclaration td = cast(TupleDeclaration) arrayContent; |
1991 | /* $ gives the number of elements in the tuple | |
1992 | */ | |
1993 | auto v = new VarDeclaration(loc, Type.tsize_t, Id.dollar, null); | |
1994 | Expression e = new IntegerExp(Loc.initial, td.objects.dim, Type.tsize_t); | |
1995 | v._init = new ExpInitializer(Loc.initial, e); | |
1996 | v.storage_class |= STC.temp | STC.static_ | STC.const_; | |
1997 | v.dsymbolSemantic(sc); | |
1998 | return v; | |
610d7898 | 1999 | case type: |
5fee5ec3 | 2000 | return dollarFromTypeTuple(loc, cast(TypeTuple) arrayContent, sc); |
610d7898 IB |
2001 | default: |
2002 | break; | |
5fee5ec3 IB |
2003 | } |
2004 | Expression exp = cast(Expression) arrayContent; | |
2005 | if (auto ie = exp.isIndexExp()) | |
2006 | { | |
2007 | /* array[index] where index is some function of $ | |
2008 | */ | |
2009 | pvar = &ie.lengthVar; | |
2010 | ce = ie.e1; | |
2011 | } | |
2012 | else if (auto se = exp.isSliceExp()) | |
2013 | { | |
2014 | /* array[lwr .. upr] where lwr or upr is some function of $ | |
2015 | */ | |
2016 | pvar = &se.lengthVar; | |
2017 | ce = se.e1; | |
2018 | } | |
2019 | else if (auto ae = exp.isArrayExp()) | |
2020 | { | |
2021 | /* array[e0, e1, e2, e3] where e0, e1, e2 are some function of $ | |
2022 | * $ is a opDollar!(dim)() where dim is the dimension(0,1,2,...) | |
2023 | */ | |
2024 | pvar = &ae.lengthVar; | |
2025 | ce = ae.e1; | |
2026 | } | |
2027 | else | |
2028 | { | |
2029 | /* Didn't find $, look in enclosing scope(s). | |
2030 | */ | |
2031 | return null; | |
2032 | } | |
2033 | ce = ce.lastComma(); | |
2034 | /* If we are indexing into an array that is really a type | |
2035 | * tuple, rewrite this as an index into a type tuple and | |
2036 | * try again. | |
2037 | */ | |
2038 | if (auto te = ce.isTypeExp()) | |
2039 | { | |
2040 | if (auto ttp = te.type.isTypeTuple()) | |
2041 | return dollarFromTypeTuple(loc, ttp, sc); | |
2042 | } | |
2043 | /* *pvar is lazily initialized, so if we refer to $ | |
2044 | * multiple times, it gets set only once. | |
2045 | */ | |
2046 | if (!*pvar) // if not already initialized | |
2047 | { | |
2048 | /* Create variable v and set it to the value of $ | |
2049 | */ | |
2050 | VarDeclaration v; | |
2051 | Type t; | |
2052 | if (auto tupexp = ce.isTupleExp()) | |
2053 | { | |
2054 | /* It is for an expression tuple, so the | |
2055 | * length will be a const. | |
2056 | */ | |
2057 | Expression e = new IntegerExp(Loc.initial, tupexp.exps.dim, Type.tsize_t); | |
2058 | v = new VarDeclaration(loc, Type.tsize_t, Id.dollar, new ExpInitializer(Loc.initial, e)); | |
2059 | v.storage_class |= STC.temp | STC.static_ | STC.const_; | |
2060 | } | |
2061 | else if (ce.type && (t = ce.type.toBasetype()) !is null && (t.ty == Tstruct || t.ty == Tclass)) | |
2062 | { | |
2063 | // Look for opDollar | |
9c7d5e88 | 2064 | assert(exp.op == EXP.array || exp.op == EXP.slice); |
5fee5ec3 IB |
2065 | AggregateDeclaration ad = isAggregate(t); |
2066 | assert(ad); | |
2067 | Dsymbol s = ad.search(loc, Id.opDollar); | |
2068 | if (!s) // no dollar exists -- search in higher scope | |
2069 | return null; | |
2070 | s = s.toAlias(); | |
2071 | Expression e = null; | |
2072 | // Check for multi-dimensional opDollar(dim) template. | |
2073 | if (TemplateDeclaration td = s.isTemplateDeclaration()) | |
2074 | { | |
2075 | dinteger_t dim = 0; | |
9c7d5e88 | 2076 | if (exp.op == EXP.array) |
5fee5ec3 IB |
2077 | { |
2078 | dim = (cast(ArrayExp)exp).currentDimension; | |
2079 | } | |
9c7d5e88 | 2080 | else if (exp.op == EXP.slice) |
5fee5ec3 IB |
2081 | { |
2082 | dim = 0; // slices are currently always one-dimensional | |
2083 | } | |
2084 | else | |
2085 | { | |
2086 | assert(0); | |
2087 | } | |
2088 | auto tiargs = new Objects(); | |
2089 | Expression edim = new IntegerExp(Loc.initial, dim, Type.tsize_t); | |
2090 | edim = edim.expressionSemantic(sc); | |
2091 | tiargs.push(edim); | |
2092 | e = new DotTemplateInstanceExp(loc, ce, td.ident, tiargs); | |
2093 | } | |
2094 | else | |
2095 | { | |
2096 | /* opDollar exists, but it's not a template. | |
2097 | * This is acceptable ONLY for single-dimension indexing. | |
2098 | * Note that it's impossible to have both template & function opDollar, | |
2099 | * because both take no arguments. | |
2100 | */ | |
9c7d5e88 | 2101 | if (exp.op == EXP.array && (cast(ArrayExp)exp).arguments.dim != 1) |
5fee5ec3 IB |
2102 | { |
2103 | exp.error("`%s` only defines opDollar for one dimension", ad.toChars()); | |
2104 | return null; | |
2105 | } | |
2106 | Declaration d = s.isDeclaration(); | |
2107 | assert(d); | |
2108 | e = new DotVarExp(loc, ce, d); | |
2109 | } | |
2110 | e = e.expressionSemantic(sc); | |
2111 | if (!e.type) | |
2112 | exp.error("`%s` has no value", e.toChars()); | |
2113 | t = e.type.toBasetype(); | |
2114 | if (t && t.ty == Tfunction) | |
2115 | e = new CallExp(e.loc, e); | |
2116 | v = new VarDeclaration(loc, null, Id.dollar, new ExpInitializer(Loc.initial, e)); | |
2117 | v.storage_class |= STC.temp | STC.ctfe | STC.rvalue; | |
2118 | } | |
2119 | else | |
2120 | { | |
2121 | /* For arrays, $ will either be a compile-time constant | |
2122 | * (in which case its value in set during constant-folding), | |
2123 | * or a variable (in which case an expression is created in | |
2124 | * toir.c). | |
2125 | */ | |
2126 | auto e = new VoidInitializer(Loc.initial); | |
2127 | e.type = Type.tsize_t; | |
2128 | v = new VarDeclaration(loc, Type.tsize_t, Id.dollar, e); | |
2129 | v.storage_class |= STC.temp | STC.ctfe; // it's never a true static variable | |
2130 | } | |
2131 | *pvar = v; | |
2132 | } | |
2133 | (*pvar).dsymbolSemantic(sc); | |
2134 | return (*pvar); | |
2135 | } | |
2136 | ||
2137 | override inout(ArrayScopeSymbol) isArrayScopeSymbol() inout | |
2138 | { | |
2139 | return this; | |
2140 | } | |
2141 | ||
2142 | override void accept(Visitor v) | |
2143 | { | |
2144 | v.visit(this); | |
2145 | } | |
2146 | } | |
2147 | ||
2148 | /*********************************************************** | |
2149 | * Overload Sets | |
2150 | */ | |
2151 | extern (C++) final class OverloadSet : Dsymbol | |
2152 | { | |
2153 | Dsymbols a; // array of Dsymbols | |
2154 | ||
31350635 | 2155 | extern (D) this(Identifier ident, OverloadSet os = null) nothrow |
5fee5ec3 IB |
2156 | { |
2157 | super(ident); | |
2158 | if (os) | |
2159 | { | |
2160 | a.pushSlice(os.a[]); | |
2161 | } | |
2162 | } | |
2163 | ||
31350635 | 2164 | void push(Dsymbol s) nothrow |
5fee5ec3 IB |
2165 | { |
2166 | a.push(s); | |
2167 | } | |
2168 | ||
2169 | override inout(OverloadSet) isOverloadSet() inout | |
2170 | { | |
2171 | return this; | |
2172 | } | |
2173 | ||
2174 | override const(char)* kind() const | |
2175 | { | |
2176 | return "overloadset"; | |
2177 | } | |
2178 | ||
2179 | override void accept(Visitor v) | |
2180 | { | |
2181 | v.visit(this); | |
2182 | } | |
2183 | } | |
2184 | ||
2185 | /*********************************************************** | |
2186 | * Forwarding ScopeDsymbol. Used by ForwardingAttribDeclaration and | |
2187 | * ForwardingScopeDeclaration to forward symbol insertions to another | |
2188 | * scope. See `dmd.attrib.ForwardingAttribDeclaration` for more | |
2189 | * details. | |
2190 | */ | |
2191 | extern (C++) final class ForwardingScopeDsymbol : ScopeDsymbol | |
2192 | { | |
ec486b73 | 2193 | extern (D) this() nothrow |
5fee5ec3 | 2194 | { |
ec486b73 | 2195 | super(); |
5fee5ec3 | 2196 | } |
31350635 IB |
2197 | |
2198 | override Dsymbol symtabInsert(Dsymbol s) nothrow | |
5fee5ec3 | 2199 | { |
5fee5ec3 IB |
2200 | if (auto d = s.isDeclaration()) |
2201 | { | |
2202 | if (d.storage_class & STC.local) | |
2203 | { | |
2204 | // Symbols with storage class STC.local are not | |
2205 | // forwarded, but stored in the local symbol | |
2206 | // table. (Those are the `static foreach` variables.) | |
2207 | if (!symtab) | |
2208 | { | |
2209 | symtab = new DsymbolTable(); | |
2210 | } | |
2211 | return super.symtabInsert(s); // insert locally | |
2212 | } | |
2213 | } | |
ec486b73 IB |
2214 | auto forward = parent.isScopeDsymbol(); |
2215 | assert(forward); | |
5fee5ec3 IB |
2216 | if (!forward.symtab) |
2217 | { | |
2218 | forward.symtab = new DsymbolTable(); | |
2219 | } | |
2220 | // Non-STC.local symbols are forwarded to `forward`. | |
2221 | return forward.symtabInsert(s); | |
2222 | } | |
2223 | ||
2224 | /************************ | |
2225 | * This override handles the following two cases: | |
2226 | * static foreach (i, i; [0]) { ... } | |
2227 | * and | |
2228 | * static foreach (i; [0]) { enum i = 2; } | |
2229 | */ | |
31350635 | 2230 | override Dsymbol symtabLookup(Dsymbol s, Identifier id) nothrow |
5fee5ec3 | 2231 | { |
5fee5ec3 IB |
2232 | // correctly diagnose clashing foreach loop variables. |
2233 | if (auto d = s.isDeclaration()) | |
2234 | { | |
2235 | if (d.storage_class & STC.local) | |
2236 | { | |
2237 | if (!symtab) | |
2238 | { | |
2239 | symtab = new DsymbolTable(); | |
2240 | } | |
2241 | return super.symtabLookup(s,id); | |
2242 | } | |
2243 | } | |
2244 | // Declarations within `static foreach` do not clash with | |
2245 | // `static foreach` loop variables. | |
ec486b73 IB |
2246 | auto forward = parent.isScopeDsymbol(); |
2247 | assert(forward); | |
5fee5ec3 IB |
2248 | if (!forward.symtab) |
2249 | { | |
2250 | forward.symtab = new DsymbolTable(); | |
2251 | } | |
2252 | return forward.symtabLookup(s,id); | |
2253 | } | |
2254 | ||
2255 | override void importScope(Dsymbol s, Visibility visibility) | |
2256 | { | |
ec486b73 IB |
2257 | auto forward = parent.isScopeDsymbol(); |
2258 | assert(forward); | |
5fee5ec3 IB |
2259 | forward.importScope(s, visibility); |
2260 | } | |
2261 | ||
2262 | override const(char)* kind()const{ return "local scope"; } | |
2263 | ||
31350635 | 2264 | override inout(ForwardingScopeDsymbol) isForwardingScopeDsymbol() inout nothrow |
5fee5ec3 IB |
2265 | { |
2266 | return this; | |
2267 | } | |
2268 | ||
2269 | } | |
2270 | ||
2271 | /** | |
2272 | * Class that holds an expression in a Dsymbol wrapper. | |
2273 | * This is not an AST node, but a class used to pass | |
2274 | * an expression as a function parameter of type Dsymbol. | |
2275 | */ | |
2276 | extern (C++) final class ExpressionDsymbol : Dsymbol | |
2277 | { | |
2278 | Expression exp; | |
31350635 | 2279 | this(Expression exp) nothrow |
5fee5ec3 IB |
2280 | { |
2281 | super(); | |
2282 | this.exp = exp; | |
2283 | } | |
2284 | ||
31350635 | 2285 | override inout(ExpressionDsymbol) isExpressionDsymbol() inout nothrow |
5fee5ec3 IB |
2286 | { |
2287 | return this; | |
2288 | } | |
2289 | } | |
2290 | ||
2291 | /********************************************** | |
2292 | * Encapsulate assigning to an alias: | |
2293 | * `identifier = type;` | |
2294 | * `identifier = symbol;` | |
2295 | * where `identifier` is an AliasDeclaration in scope. | |
2296 | */ | |
2297 | extern (C++) final class AliasAssign : Dsymbol | |
2298 | { | |
2299 | Identifier ident; /// Dsymbol's ident will be null, as this class is anonymous | |
2300 | Type type; /// replace previous RHS of AliasDeclaration with `type` | |
2301 | Dsymbol aliassym; /// replace previous RHS of AliasDeclaration with `aliassym` | |
2302 | /// only one of type and aliassym can be != null | |
2303 | ||
31350635 | 2304 | extern (D) this(const ref Loc loc, Identifier ident, Type type, Dsymbol aliassym) nothrow |
5fee5ec3 IB |
2305 | { |
2306 | super(loc, null); | |
2307 | this.ident = ident; | |
2308 | this.type = type; | |
2309 | this.aliassym = aliassym; | |
2310 | } | |
2311 | ||
2312 | override AliasAssign syntaxCopy(Dsymbol s) | |
2313 | { | |
2314 | assert(!s); | |
2315 | AliasAssign aa = new AliasAssign(loc, ident, | |
2316 | type ? type.syntaxCopy() : null, | |
2317 | aliassym ? aliassym.syntaxCopy(null) : null); | |
2318 | return aa; | |
2319 | } | |
2320 | ||
2321 | override inout(AliasAssign) isAliasAssign() inout | |
2322 | { | |
2323 | return this; | |
2324 | } | |
2325 | ||
2326 | override const(char)* kind() const | |
2327 | { | |
2328 | return "alias assignment"; | |
2329 | } | |
2330 | ||
2331 | override void accept(Visitor v) | |
2332 | { | |
2333 | v.visit(this); | |
2334 | } | |
2335 | } | |
2336 | ||
2337 | /*********************************************************** | |
2338 | * Table of Dsymbol's | |
2339 | */ | |
2340 | extern (C++) final class DsymbolTable : RootObject | |
2341 | { | |
2342 | AssocArray!(Identifier, Dsymbol) tab; | |
2343 | ||
31350635 IB |
2344 | nothrow: |
2345 | ||
5fee5ec3 IB |
2346 | /*************************** |
2347 | * Look up Identifier in symbol table | |
2348 | * Params: | |
2349 | * ident = identifer to look up | |
2350 | * Returns: | |
2351 | * Dsymbol if found, null if not | |
2352 | */ | |
2353 | Dsymbol lookup(const Identifier ident) | |
2354 | { | |
2355 | //printf("DsymbolTable::lookup(%s)\n", ident.toChars()); | |
2356 | return tab[ident]; | |
2357 | } | |
2358 | ||
2359 | /********** | |
2360 | * Replace existing symbol in symbol table with `s`. | |
2361 | * If it's not there, add it. | |
2362 | * Params: | |
2363 | * s = replacement symbol with same identifier | |
2364 | */ | |
2365 | void update(Dsymbol s) | |
2366 | { | |
2367 | *tab.getLvalue(s.ident) = s; | |
2368 | } | |
2369 | ||
2370 | /************************** | |
2371 | * Insert Dsymbol in table. | |
2372 | * Params: | |
2373 | * s = symbol to add | |
2374 | * Returns: | |
2375 | * null if already in table, `s` if inserted | |
2376 | */ | |
2377 | Dsymbol insert(Dsymbol s) | |
2378 | { | |
2379 | return insert(s.ident, s); | |
2380 | } | |
2381 | ||
2382 | /************************** | |
2383 | * Insert Dsymbol in table. | |
2384 | * Params: | |
2385 | * ident = identifier to serve as index | |
2386 | * s = symbol to add | |
2387 | * Returns: | |
2388 | * null if already in table, `s` if inserted | |
2389 | */ | |
2390 | Dsymbol insert(const Identifier ident, Dsymbol s) | |
2391 | { | |
2392 | //printf("DsymbolTable.insert(this = %p, '%s')\n", this, s.ident.toChars()); | |
2393 | Dsymbol* ps = tab.getLvalue(ident); | |
2394 | if (*ps) | |
2395 | return null; // already in table | |
2396 | *ps = s; | |
2397 | return s; | |
2398 | } | |
2399 | ||
2400 | /***************** | |
2401 | * Returns: | |
2402 | * number of symbols in symbol table | |
2403 | */ | |
2404 | size_t length() const pure | |
2405 | { | |
2406 | return tab.length; | |
2407 | } | |
2408 | } | |
2409 | ||
2410 | /********************************************** | |
2411 | * ImportC tag symbols sit in a parallel symbol table, | |
2412 | * so that this C code works: | |
2413 | * --- | |
2414 | * struct S { a; }; | |
2415 | * int S; | |
2416 | * struct S s; | |
2417 | * --- | |
2418 | * But there are relatively few such tag symbols, so that would be | |
2419 | * a waste of memory and complexity. An additional problem is we'd like the D side | |
2420 | * to find the tag symbols with ordinary lookup, not lookup in both | |
2421 | * tables, if the tag symbol is not conflicting with an ordinary symbol. | |
2422 | * The solution is to put the tag symbols that conflict into an associative | |
2423 | * array, indexed by the address of the ordinary symbol that conflicts with it. | |
2424 | * C has no modules, so this associative array is tagSymTab[] in ModuleDeclaration. | |
2425 | * A side effect of our approach is that D code cannot access a tag symbol that is | |
2426 | * hidden by an ordinary symbol. This is more of a theoretical problem, as nobody | |
2427 | * has mentioned it when importing C headers. If someone wants to do it, | |
2428 | * too bad so sad. Change the C code. | |
2429 | * This function fixes up the symbol table when faced with adding a new symbol | |
2430 | * `s` when there is an existing symbol `s2` with the same name. | |
2431 | * C also allows forward and prototype declarations of tag symbols, | |
2432 | * this function merges those. | |
2433 | * Params: | |
2434 | * sc = context | |
2435 | * s = symbol to add to symbol table | |
2436 | * s2 = existing declaration | |
2437 | * sds = symbol table | |
2438 | * Returns: | |
2439 | * if s and s2 are successfully put in symbol table then return the merged symbol, | |
2440 | * null if they conflict | |
2441 | */ | |
2442 | Dsymbol handleTagSymbols(ref Scope sc, Dsymbol s, Dsymbol s2, ScopeDsymbol sds) | |
2443 | { | |
2444 | enum log = false; | |
d7569187 | 2445 | if (log) printf("handleTagSymbols('%s') add %p existing %p\n", s.toChars(), s, s2); |
235d5a96 | 2446 | if (log) printf(" add %s %s, existing %s %s\n", s.kind(), s.toChars(), s2.kind(), s2.toChars()); |
5fee5ec3 IB |
2447 | auto sd = s.isScopeDsymbol(); // new declaration |
2448 | auto sd2 = s2.isScopeDsymbol(); // existing declaration | |
2449 | ||
b6df1132 IB |
2450 | static if (log) void print(EnumDeclaration sd) |
2451 | { | |
2452 | printf("members: %p\n", sd.members); | |
2453 | printf("symtab: %p\n", sd.symtab); | |
2454 | printf("endlinnum: %d\n", sd.endlinnum); | |
2455 | printf("type: %s\n", sd.type.toChars()); | |
2456 | printf("memtype: %s\n", sd.memtype.toChars()); | |
2457 | } | |
2458 | ||
5fee5ec3 IB |
2459 | if (!sd2) |
2460 | { | |
2461 | /* Look in tag table | |
2462 | */ | |
2463 | if (log) printf(" look in tag table\n"); | |
2464 | if (auto p = cast(void*)s2 in sc._module.tagSymTab) | |
2465 | { | |
2466 | Dsymbol s2tag = *p; | |
2467 | sd2 = s2tag.isScopeDsymbol(); | |
2468 | assert(sd2); // only tags allowed in tag symbol table | |
2469 | } | |
2470 | } | |
2471 | ||
2472 | if (sd && sd2) // `s` is a tag, `sd2` is the same tag | |
2473 | { | |
2474 | if (log) printf(" tag is already defined\n"); | |
2475 | ||
2476 | if (sd.kind() != sd2.kind()) // being enum/struct/union must match | |
2477 | return null; // conflict | |
2478 | ||
2479 | /* Not a redeclaration if one is a forward declaration. | |
2480 | * Move members to the first declared type, which is sd2. | |
2481 | */ | |
2482 | if (sd2.members) | |
2483 | { | |
2484 | if (!sd.members) | |
2485 | return sd2; // ignore the sd redeclaration | |
2486 | } | |
2487 | else if (sd.members) | |
2488 | { | |
2489 | sd2.members = sd.members; // transfer definition to sd2 | |
2490 | sd.members = null; | |
b6df1132 IB |
2491 | if (auto ed2 = sd2.isEnumDeclaration()) |
2492 | { | |
2493 | auto ed = sd.isEnumDeclaration(); | |
2494 | if (ed.memtype != ed2.memtype) | |
2495 | return null; // conflict | |
2496 | ||
2497 | // transfer ed's members to sd2 | |
2498 | ed2.members.foreachDsymbol( (s) | |
2499 | { | |
2500 | if (auto em = s.isEnumMember()) | |
2501 | em.ed = ed2; | |
2502 | }); | |
2503 | ||
2504 | ed2.type = ed.type; | |
2505 | ed2.memtype = ed.memtype; | |
2506 | ed2.added = false; | |
2507 | } | |
5fee5ec3 IB |
2508 | return sd2; |
2509 | } | |
2510 | else | |
2511 | return sd2; // ignore redeclaration | |
2512 | } | |
2513 | else if (sd) // `s` is a tag, `s2` is not | |
2514 | { | |
2515 | if (log) printf(" s is tag, s2 is not\n"); | |
2516 | /* add `s` as tag indexed by s2 | |
2517 | */ | |
2518 | sc._module.tagSymTab[cast(void*)s2] = s; | |
2519 | return s; | |
2520 | } | |
2521 | else if (s2 is sd2) // `s2` is a tag, `s` is not | |
2522 | { | |
2523 | if (log) printf(" s2 is tag, s is not\n"); | |
2524 | /* replace `s2` in symbol table with `s`, | |
2525 | * then add `s2` as tag indexed by `s` | |
2526 | */ | |
2527 | sds.symtab.update(s); | |
2528 | sc._module.tagSymTab[cast(void*)s] = s2; | |
2529 | return s; | |
2530 | } | |
235d5a96 | 2531 | // neither s2 nor s is a tag |
5fee5ec3 IB |
2532 | if (log) printf(" collision\n"); |
2533 | return null; | |
2534 | } | |
2535 | ||
2536 | ||
0fb57034 IB |
2537 | /********************************************** |
2538 | * ImportC allows redeclarations of C variables, functions and typedefs. | |
2539 | * extern int x; | |
2540 | * int x = 3; | |
2541 | * and: | |
2542 | * extern void f(); | |
2543 | * void f() { } | |
2544 | * Attempt to merge them. | |
2545 | * Params: | |
2546 | * sc = context | |
2547 | * s = symbol to add to symbol table | |
2548 | * s2 = existing declaration | |
2549 | * sds = symbol table | |
2550 | * Returns: | |
2551 | * if s and s2 are successfully put in symbol table then return the merged symbol, | |
2552 | * null if they conflict | |
2553 | */ | |
2554 | Dsymbol handleSymbolRedeclarations(ref Scope sc, Dsymbol s, Dsymbol s2, ScopeDsymbol sds) | |
2555 | { | |
2556 | enum log = false; | |
2557 | if (log) printf("handleSymbolRedeclarations('%s')\n", s.toChars()); | |
235d5a96 | 2558 | if (log) printf(" add %s %s, existing %s %s\n", s.kind(), s.toChars(), s2.kind(), s2.toChars()); |
0fb57034 IB |
2559 | |
2560 | static Dsymbol collision() | |
2561 | { | |
2562 | if (log) printf(" collision\n"); | |
2563 | return null; | |
2564 | } | |
610d7898 IB |
2565 | /* |
2566 | Handle merging declarations with asm("foo") and their definitions | |
2567 | */ | |
2568 | static void mangleWrangle(Declaration oldDecl, Declaration newDecl) | |
2569 | { | |
2570 | if (oldDecl && newDecl) | |
2571 | { | |
2572 | newDecl.mangleOverride = oldDecl.mangleOverride ? oldDecl.mangleOverride : null; | |
2573 | } | |
2574 | } | |
0fb57034 IB |
2575 | |
2576 | auto vd = s.isVarDeclaration(); // new declaration | |
2577 | auto vd2 = s2.isVarDeclaration(); // existing declaration | |
2578 | if (vd && vd2) | |
2579 | { | |
fd43568c IB |
2580 | /* if one is `static` and the other isn't, the result is undefined |
2581 | * behavior, C11 6.2.2.7 | |
2582 | */ | |
0fb57034 IB |
2583 | if ((vd.storage_class ^ vd2.storage_class) & STC.static_) |
2584 | return collision(); | |
2585 | ||
2586 | const i1 = vd._init && ! vd._init.isVoidInitializer(); | |
2587 | const i2 = vd2._init && !vd2._init.isVoidInitializer(); | |
2588 | ||
2589 | if (i1 && i2) | |
2590 | return collision(); // can't both have initializers | |
2591 | ||
610d7898 IB |
2592 | mangleWrangle(vd2, vd); |
2593 | ||
b3f58f87 | 2594 | if (i1) // vd is the definition |
fd43568c | 2595 | { |
ae56e2da | 2596 | vd2.storage_class |= STC.extern_; // so toObjFile() won't emit it |
b3f58f87 IB |
2597 | sds.symtab.update(vd); // replace vd2 with the definition |
2598 | return vd; | |
fd43568c | 2599 | } |
0fb57034 IB |
2600 | |
2601 | /* BUG: the types should match, which needs semantic() to be run on it | |
2602 | * extern int x; | |
2603 | * int x; // match | |
2604 | * typedef int INT; | |
2605 | * INT x; // match | |
2606 | * long x; // collision | |
2607 | * We incorrectly ignore these collisions | |
2608 | */ | |
2609 | return vd2; | |
2610 | } | |
2611 | ||
2612 | auto fd = s.isFuncDeclaration(); // new declaration | |
2613 | auto fd2 = s2.isFuncDeclaration(); // existing declaration | |
2614 | if (fd && fd2) | |
2615 | { | |
fd43568c IB |
2616 | /* if one is `static` and the other isn't, the result is undefined |
2617 | * behavior, C11 6.2.2.7 | |
2618 | * However, match what gcc allows: | |
2619 | * static int sun1(); int sun1() { return 0; } | |
2620 | * and: | |
2621 | * static int sun2() { return 0; } int sun2(); | |
2622 | * Both produce a static function. | |
2623 | * | |
2624 | * Both of these should fail: | |
2625 | * int sun3(); static int sun3() { return 0; } | |
2626 | * and: | |
2627 | * int sun4() { return 0; } static int sun4(); | |
2628 | */ | |
2629 | // if adding `static` | |
2630 | if ( fd.storage_class & STC.static_ && | |
2631 | !(fd2.storage_class & STC.static_)) | |
2632 | { | |
0fb57034 | 2633 | return collision(); |
fd43568c | 2634 | } |
0fb57034 IB |
2635 | |
2636 | if (fd.fbody && fd2.fbody) | |
2637 | return collision(); // can't both have bodies | |
2638 | ||
610d7898 IB |
2639 | mangleWrangle(fd2, fd); |
2640 | ||
b3f58f87 | 2641 | if (fd.fbody) // fd is the definition |
fd43568c | 2642 | { |
235d5a96 | 2643 | if (log) printf(" replace existing with new\n"); |
b3f58f87 | 2644 | sds.symtab.update(fd); // replace fd2 in symbol table with fd |
6384eff5 | 2645 | fd.overnext = fd2; |
235d5a96 IB |
2646 | |
2647 | /* If fd2 is covering a tag symbol, then fd has to cover the same one | |
2648 | */ | |
2649 | auto ps = cast(void*)fd2 in sc._module.tagSymTab; | |
2650 | if (ps) | |
2651 | sc._module.tagSymTab[cast(void*)fd] = *ps; | |
2652 | ||
b3f58f87 | 2653 | return fd; |
fd43568c | 2654 | } |
0fb57034 | 2655 | |
6384eff5 IB |
2656 | /* Just like with VarDeclaration, the types should match, which needs semantic() to be run on it. |
2657 | * FuncDeclaration::semantic() detects this, but it relies on .overnext being set. | |
0fb57034 | 2658 | */ |
6384eff5 IB |
2659 | fd2.overloadInsert(fd); |
2660 | ||
0fb57034 IB |
2661 | return fd2; |
2662 | } | |
2663 | ||
2664 | auto td = s.isAliasDeclaration(); // new declaration | |
2665 | auto td2 = s2.isAliasDeclaration(); // existing declaration | |
2666 | if (td && td2) | |
2667 | { | |
2668 | /* BUG: just like with variables and functions, the types should match, which needs semantic() to be run on it. | |
2669 | * FuncDeclaration::semantic2() can detect this, but it relies overnext being set. | |
2670 | */ | |
2671 | return td2; | |
2672 | } | |
2673 | ||
2674 | return collision(); | |
2675 | } |