1 /**
2 * Defines a package and module.
3 *
4 * Specification: $(LINK2 https://dlang.org/spec/module.html, Modules)
5 *
6 * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
7 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
8 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dmodule.d, _dmodule.d)
10 * Documentation: https://dlang.org/phobos/dmd_dmodule.html
11 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dmodule.d
12 */
13
14 module dmd.dmodule;
15
16 import core.stdc.stdio;
17 import core.stdc.stdlib;
18 import core.stdc.string;
19 import dmd.aggregate;
20 import dmd.arraytypes;
21 import dmd.astcodegen;
22 import dmd.astenums;
23 import dmd.compiler;
24 import dmd.gluelayer;
25 import dmd.dimport;
26 import dmd.dmacro;
27 import dmd.doc;
28 import dmd.dscope;
29 import dmd.dsymbol;
30 import dmd.dsymbolsem;
31 import dmd.errors;
32 import dmd.expression;
33 import dmd.expressionsem;
34 import dmd.globals;
35 import dmd.id;
36 import dmd.identifier;
37 import dmd.parse;
38 import dmd.cparse;
39 import dmd.root.array;
40 import dmd.root.file;
41 import dmd.root.filename;
42 import dmd.root.outbuffer;
43 import dmd.root.port;
44 import dmd.root.rmem;
45 import dmd.root.rootobject;
46 import dmd.root.string;
47 import dmd.semantic2;
48 import dmd.semantic3;
49 import dmd.target;
50 import dmd.utils;
51 import dmd.visitor;
52
53 enum package_d = "package." ~ mars_ext;
54 enum package_di = "package." ~ hdr_ext;
55
56 /********************************************
57 * Look for the source file if it's different from filename.
58 * Look for .di, .d, directory, and along global.path.
59 * Does not open the file.
60 * Params:
61 * filename = as supplied by the user
62 * path = path to look for filename
63 * Returns:
64 * the found file name or
65 * `null` if it is not different from filename.
66 */
lookForSourceFile(const char[]filename,const char * []path)67 private const(char)[] lookForSourceFile(const char[] filename, const char*[] path)
68 {
69 //printf("lookForSourceFile(`%.*s`)\n", cast(int)filename.length, filename.ptr);
70 /* Search along path[] for .di file, then .d file, then .i file, then .c file.
71 */
72 const sdi = FileName.forceExt(filename, hdr_ext);
73 if (FileName.exists(sdi) == 1)
74 return sdi;
75 scope(exit) FileName.free(sdi.ptr);
76
77 const sd = FileName.forceExt(filename, mars_ext);
78 if (FileName.exists(sd) == 1)
79 return sd;
80 scope(exit) FileName.free(sd.ptr);
81
82 const si = FileName.forceExt(filename, i_ext);
83 if (FileName.exists(si) == 1)
84 return si;
85 scope(exit) FileName.free(si.ptr);
86
87 const sc = FileName.forceExt(filename, c_ext);
88 if (FileName.exists(sc) == 1)
89 return sc;
90 scope(exit) FileName.free(sc.ptr);
91
92 if (FileName.exists(filename) == 2)
93 {
94 /* The filename exists and it's a directory.
95 * Therefore, the result should be: filename/package.d
96 * iff filename/package.d is a file
97 */
98 const ni = FileName.combine(filename, package_di);
99 if (FileName.exists(ni) == 1)
100 return ni;
101 FileName.free(ni.ptr);
102
103 const n = FileName.combine(filename, package_d);
104 if (FileName.exists(n) == 1)
105 return n;
106 FileName.free(n.ptr);
107 }
108 if (FileName.absolute(filename))
109 return null;
110 if (!path.length)
111 return null;
112 foreach (entry; path)
113 {
114 const p = entry.toDString();
115
116 const(char)[] n = FileName.combine(p, sdi);
117 if (FileName.exists(n) == 1) {
118 return n;
119 }
120 FileName.free(n.ptr);
121
122 n = FileName.combine(p, sd);
123 if (FileName.exists(n) == 1) {
124 return n;
125 }
126 FileName.free(n.ptr);
127
128 n = FileName.combine(p, si);
129 if (FileName.exists(n) == 1) {
130 return n;
131 }
132 FileName.free(n.ptr);
133
134 n = FileName.combine(p, sc);
135 if (FileName.exists(n) == 1) {
136 return n;
137 }
138 FileName.free(n.ptr);
139
140 const b = FileName.removeExt(filename);
141 n = FileName.combine(p, b);
142 FileName.free(b.ptr);
143 if (FileName.exists(n) == 2)
144 {
145 const n2i = FileName.combine(n, package_di);
146 if (FileName.exists(n2i) == 1)
147 return n2i;
148 FileName.free(n2i.ptr);
149 const n2 = FileName.combine(n, package_d);
150 if (FileName.exists(n2) == 1) {
151 return n2;
152 }
153 FileName.free(n2.ptr);
154 }
155 FileName.free(n.ptr);
156 }
157 return null;
158 }
159
160 // function used to call semantic3 on a module's dependencies
semantic3OnDependencies(Module m)161 void semantic3OnDependencies(Module m)
162 {
163 if (!m)
164 return;
165
166 if (m.semanticRun > PASS.semantic3)
167 return;
168
169 m.semantic3(null);
170
171 foreach (i; 1 .. m.aimports.dim)
172 semantic3OnDependencies(m.aimports[i]);
173 }
174
175 /**
176 * Remove generated .di files on error and exit
177 */
removeHdrFilesAndFail(ref Param params,ref Modules modules)178 void removeHdrFilesAndFail(ref Param params, ref Modules modules)
179 {
180 if (params.doHdrGeneration)
181 {
182 foreach (m; modules)
183 {
184 if (m.isHdrFile)
185 continue;
186 File.remove(m.hdrfile.toChars());
187 }
188 }
189
190 fatal();
191 }
192
193 /**
194 * Converts a chain of identifiers to the filename of the module
195 *
196 * Params:
197 * packages = the names of the "parent" packages
198 * ident = the name of the child package or module
199 *
200 * Returns:
201 * the filename of the child package or module
202 */
getFilename(Identifier[]packages,Identifier ident)203 private const(char)[] getFilename(Identifier[] packages, Identifier ident)
204 {
205 const(char)[] filename = ident.toString();
206
207 if (packages.length == 0)
208 return filename;
209
210 OutBuffer buf;
211 OutBuffer dotmods;
212 auto modAliases = &global.params.modFileAliasStrings;
213
214 void checkModFileAlias(const(char)[] p)
215 {
216 /* Check and replace the contents of buf[] with
217 * an alias string from global.params.modFileAliasStrings[]
218 */
219 dotmods.writestring(p);
220 foreach_reverse (const m; *modAliases)
221 {
222 const q = strchr(m, '=');
223 assert(q);
224 if (dotmods.length == q - m && memcmp(dotmods.peekChars(), m, q - m) == 0)
225 {
226 buf.setsize(0);
227 auto rhs = q[1 .. strlen(q)];
228 if (rhs.length > 0 && (rhs[$ - 1] == '/' || rhs[$ - 1] == '\\'))
229 rhs = rhs[0 .. $ - 1]; // remove trailing separator
230 buf.writestring(rhs);
231 break; // last matching entry in ms[] wins
232 }
233 }
234 dotmods.writeByte('.');
235 }
236
237 foreach (pid; packages)
238 {
239 const p = pid.toString();
240 buf.writestring(p);
241 if (modAliases.dim)
242 checkModFileAlias(p);
243 version (Windows)
244 enum FileSeparator = '\\';
245 else
246 enum FileSeparator = '/';
247 buf.writeByte(FileSeparator);
248 }
249 buf.writestring(filename);
250 if (modAliases.dim)
251 checkModFileAlias(filename);
252 buf.writeByte(0);
253 filename = buf.extractSlice()[0 .. $ - 1];
254
255 return filename;
256 }
257
258 /***********************************************************
259 */
260 extern (C++) class Package : ScopeDsymbol
261 {
262 PKG isPkgMod = PKG.unknown;
263 uint tag; // auto incremented tag, used to mask package tree in scopes
264 Module mod; // !=null if isPkgMod == PKG.module_
265
this(const ref Loc loc,Identifier ident)266 final extern (D) this(const ref Loc loc, Identifier ident)
267 {
268 super(loc, ident);
269 __gshared uint packageTag;
270 this.tag = packageTag++;
271 }
272
kind()273 override const(char)* kind() const
274 {
275 return "package";
276 }
277
equals(const RootObject o)278 override bool equals(const RootObject o) const
279 {
280 // custom 'equals' for bug 17441. "package a" and "module a" are not equal
281 if (this == o)
282 return true;
283 auto p = cast(Package)o;
284 return p && isModule() == p.isModule() && ident.equals(p.ident);
285 }
286
287 /****************************************************
288 * Input:
289 * packages[] the pkg1.pkg2 of pkg1.pkg2.mod
290 * Returns:
291 * the symbol table that mod should be inserted into
292 * Output:
293 * *pparent the rightmost package, i.e. pkg2, or NULL if no packages
294 * *ppkg the leftmost package, i.e. pkg1, or NULL if no packages
295 */
resolve(Identifier[]packages,Dsymbol * pparent,Package * ppkg)296 extern (D) static DsymbolTable resolve(Identifier[] packages, Dsymbol* pparent, Package* ppkg)
297 {
298 DsymbolTable dst = Module.modules;
299 Dsymbol parent = null;
300 //printf("Package::resolve()\n");
301 if (ppkg)
302 *ppkg = null;
303 foreach (pid; packages)
304 {
305 Package pkg;
306 Dsymbol p = dst.lookup(pid);
307 if (!p)
308 {
309 pkg = new Package(Loc.initial, pid);
310 dst.insert(pkg);
311 pkg.parent = parent;
312 pkg.symtab = new DsymbolTable();
313 }
314 else
315 {
316 pkg = p.isPackage();
317 assert(pkg);
318 // It might already be a module, not a package, but that needs
319 // to be checked at a higher level, where a nice error message
320 // can be generated.
321 // dot net needs modules and packages with same name
322 // But we still need a symbol table for it
323 if (!pkg.symtab)
324 pkg.symtab = new DsymbolTable();
325 }
326 parent = pkg;
327 dst = pkg.symtab;
328 if (ppkg && !*ppkg)
329 *ppkg = pkg;
330 if (pkg.isModule())
331 {
332 // Return the module so that a nice error message can be generated
333 if (ppkg)
334 *ppkg = cast(Package)p;
335 break;
336 }
337 }
338
339 if (pparent)
340 *pparent = parent;
341 return dst;
342 }
343
inout(Package)344 override final inout(Package) isPackage() inout
345 {
346 return this;
347 }
348
349 /**
350 * Checks if pkg is a sub-package of this
351 *
352 * For example, if this qualifies to 'a1.a2' and pkg - to 'a1.a2.a3',
353 * this function returns 'true'. If it is other way around or qualified
354 * package paths conflict function returns 'false'.
355 *
356 * Params:
357 * pkg = possible subpackage
358 *
359 * Returns:
360 * see description
361 */
isAncestorPackageOf(const Package pkg)362 final bool isAncestorPackageOf(const Package pkg) const
363 {
364 if (this == pkg)
365 return true;
366 if (!pkg || !pkg.parent)
367 return false;
368 return isAncestorPackageOf(pkg.parent.isPackage());
369 }
370
371 override Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly)
372 {
373 //printf("%s Package.search('%s', flags = x%x)\n", toChars(), ident.toChars(), flags);
374 flags &= ~SearchLocalsOnly; // searching an import is always transitive
375 if (!isModule() && mod)
376 {
377 // Prefer full package name.
378 Dsymbol s = symtab ? symtab.lookup(ident) : null;
379 if (s)
380 return s;
381 //printf("[%s] through pkdmod: %s\n", loc.toChars(), toChars());
382 return mod.search(loc, ident, flags);
383 }
384 return ScopeDsymbol.search(loc, ident, flags);
385 }
386
accept(Visitor v)387 override void accept(Visitor v)
388 {
389 v.visit(this);
390 }
391
isPackageMod()392 final Module isPackageMod()
393 {
394 if (isPkgMod == PKG.module_)
395 {
396 return mod;
397 }
398 return null;
399 }
400
401 /**
402 * Checks for the existence of a package.d to set isPkgMod appropriately
403 * if isPkgMod == PKG.unknown
404 */
resolvePKGunknown()405 final void resolvePKGunknown()
406 {
407 if (isModule())
408 return;
409 if (isPkgMod != PKG.unknown)
410 return;
411
412 Identifier[] packages;
413 for (Dsymbol s = this.parent; s; s = s.parent)
414 packages ~= s.ident;
415 reverse(packages);
416
417 if (lookForSourceFile(getFilename(packages, ident), global.path ? (*global.path)[] : null))
418 Module.load(Loc(), packages, this.ident);
419 else
420 isPkgMod = PKG.package_;
421 }
422 }
423
424 /***********************************************************
425 */
426 extern (C++) final class Module : Package
427 {
428 extern (C++) __gshared Module rootModule;
429 extern (C++) __gshared DsymbolTable modules; // symbol table of all modules
430 extern (C++) __gshared Modules amodules; // array of all modules
431 extern (C++) __gshared Dsymbols deferred; // deferred Dsymbol's needing semantic() run on them
432 extern (C++) __gshared Dsymbols deferred2; // deferred Dsymbol's needing semantic2() run on them
433 extern (C++) __gshared Dsymbols deferred3; // deferred Dsymbol's needing semantic3() run on them
434 extern (C++) __gshared uint dprogress; // progress resolving the deferred list
435
_init()436 static void _init()
437 {
438 modules = new DsymbolTable();
439 }
440
441 /**
442 * Deinitializes the global state of the compiler.
443 *
444 * This can be used to restore the state set by `_init` to its original
445 * state.
446 */
deinitialize()447 static void deinitialize()
448 {
449 modules = modules.init;
450 }
451
452 extern (C++) __gshared AggregateDeclaration moduleinfo;
453
454 const(char)[] arg; // original argument name
455 ModuleDeclaration* md; // if !=null, the contents of the ModuleDeclaration declaration
456 const FileName srcfile; // input source file
457 const FileName objfile; // output .obj file
458 const FileName hdrfile; // 'header' file
459 FileName docfile; // output documentation file
460 FileBuffer* srcBuffer; // set during load(), free'd in parse()
461 uint errors; // if any errors in file
462 uint numlines; // number of lines in source file
463 bool isHdrFile; // if it is a header (.di) file
464 bool isCFile; // if it is a C (.c) file
465 bool isDocFile; // if it is a documentation input file, not D source
466 bool hasAlwaysInlines; // contains references to functions that must be inlined
467 bool isPackageFile; // if it is a package.d
468 Package pkg; // if isPackageFile is true, the Package that contains this package.d
469 Strings contentImportedFiles; // array of files whose content was imported
470 int needmoduleinfo;
471 int selfimports; // 0: don't know, 1: does not, 2: does
472 Dsymbol[void*] tagSymTab; /// ImportC: tag symbols that conflict with other symbols used as the index
473
474 /*************************************
475 * Return true if module imports itself.
476 */
selfImports()477 bool selfImports()
478 {
479 //printf("Module::selfImports() %s\n", toChars());
480 if (selfimports == 0)
481 {
482 foreach (Module m; amodules)
483 m.insearch = 0;
484 selfimports = imports(this) + 1;
485 foreach (Module m; amodules)
486 m.insearch = 0;
487 }
488 return selfimports == 2;
489 }
490
491 int rootimports; // 0: don't know, 1: does not, 2: does
492
493 /*************************************
494 * Return true if module imports root module.
495 */
rootImports()496 bool rootImports()
497 {
498 //printf("Module::rootImports() %s\n", toChars());
499 if (rootimports == 0)
500 {
501 foreach (Module m; amodules)
502 m.insearch = 0;
503 rootimports = 1;
504 foreach (Module m; amodules)
505 {
506 if (m.isRoot() && imports(m))
507 {
508 rootimports = 2;
509 break;
510 }
511 }
512 foreach (Module m; amodules)
513 m.insearch = 0;
514 }
515 return rootimports == 2;
516 }
517
518 int insearch;
519 Identifier searchCacheIdent;
520 Dsymbol searchCacheSymbol; // cached value of search
521 int searchCacheFlags; // cached flags
522
523 /**
524 * A root module is one that will be compiled all the way to
525 * object code. This field holds the root module that caused
526 * this module to be loaded. If this module is a root module,
527 * then it will be set to `this`. This is used to determine
528 * ownership of template instantiation.
529 */
530 Module importedFrom;
531
532 Dsymbols* decldefs; // top level declarations for this Module
533
534 Modules aimports; // all imported modules
535
536 uint debuglevel; // debug level
537 Identifiers* debugids; // debug identifiers
538 Identifiers* debugidsNot; // forward referenced debug identifiers
539
540 uint versionlevel; // version level
541 Identifiers* versionids; // version identifiers
542 Identifiers* versionidsNot; // forward referenced version identifiers
543
544 MacroTable macrotable; // document comment macros
545 Escape* _escapetable; // document comment escapes
546
547 size_t nameoffset; // offset of module name from start of ModuleInfo
548 size_t namelen; // length of module name in characters
549
this(const ref Loc loc,const (char)[]filename,Identifier ident,int doDocComment,int doHdrGen)550 extern (D) this(const ref Loc loc, const(char)[] filename, Identifier ident, int doDocComment, int doHdrGen)
551 {
552 super(loc, ident);
553 const(char)[] srcfilename;
554 //printf("Module::Module(filename = '%.*s', ident = '%s')\n", cast(int)filename.length, filename.ptr, ident.toChars());
555 this.arg = filename;
556 srcfilename = FileName.defaultExt(filename, mars_ext);
557 if (target.run_noext && global.params.run &&
558 !FileName.ext(filename) &&
559 FileName.exists(srcfilename) == 0 &&
560 FileName.exists(filename) == 1)
561 {
562 FileName.free(srcfilename.ptr);
563 srcfilename = FileName.removeExt(filename); // just does a mem.strdup(filename)
564 }
565 else if (!FileName.equalsExt(srcfilename, mars_ext) &&
566 !FileName.equalsExt(srcfilename, hdr_ext) &&
567 !FileName.equalsExt(srcfilename, c_ext) &&
568 !FileName.equalsExt(srcfilename, i_ext) &&
569 !FileName.equalsExt(srcfilename, dd_ext))
570 {
571
572 error("source file name '%.*s' must have .%.*s extension",
573 cast(int)srcfilename.length, srcfilename.ptr,
574 cast(int)mars_ext.length, mars_ext.ptr);
575 fatal();
576 }
577
578 srcfile = FileName(srcfilename);
579 objfile = setOutfilename(global.params.objname, global.params.objdir, filename, target.obj_ext);
580 if (doDocComment)
581 setDocfile();
582 if (doHdrGen)
583 hdrfile = setOutfilename(global.params.hdrname, global.params.hdrdir, arg, hdr_ext);
584 }
585
this(const (char)[]filename,Identifier ident,int doDocComment,int doHdrGen)586 extern (D) this(const(char)[] filename, Identifier ident, int doDocComment, int doHdrGen)
587 {
588 this(Loc.initial, filename, ident, doDocComment, doHdrGen);
589 }
590
create(const (char)* filename,Identifier ident,int doDocComment,int doHdrGen)591 static Module create(const(char)* filename, Identifier ident, int doDocComment, int doHdrGen)
592 {
593 return create(filename.toDString, ident, doDocComment, doHdrGen);
594 }
595
create(const (char)[]filename,Identifier ident,int doDocComment,int doHdrGen)596 extern (D) static Module create(const(char)[] filename, Identifier ident, int doDocComment, int doHdrGen)
597 {
598 return new Module(Loc.initial, filename, ident, doDocComment, doHdrGen);
599 }
600
load(Loc loc,Identifiers * packages,Identifier ident)601 extern (C++) static Module load(Loc loc, Identifiers* packages, Identifier ident)
602 {
603 return load(loc, packages ? (*packages)[] : null, ident);
604 }
605
load(Loc loc,Identifier[]packages,Identifier ident)606 extern (D) static Module load(Loc loc, Identifier[] packages, Identifier ident)
607 {
608 //printf("Module::load(ident = '%s')\n", ident.toChars());
609 // Build module filename by turning:
610 // foo.bar.baz
611 // into:
612 // foo\bar\baz
613 const(char)[] filename = getFilename(packages, ident);
614 // Look for the source file
615 if (const result = lookForSourceFile(filename, global.path ? (*global.path)[] : null))
616 filename = result; // leaks
617
618 auto m = new Module(loc, filename, ident, 0, 0);
619
620 if (!m.read(loc))
621 return null;
622 if (global.params.verbose)
623 {
624 OutBuffer buf;
625 foreach (pid; packages)
626 {
627 buf.writestring(pid.toString());
628 buf.writeByte('.');
629 }
630 buf.printf("%s\t(%s)", ident.toChars(), m.srcfile.toChars());
631 message("import %s", buf.peekChars());
632 }
633 m = m.parse();
634
635 // Call onImport here because if the module is going to be compiled then we
636 // need to determine it early because it affects semantic analysis. This is
637 // being done after parsing the module so the full module name can be taken
638 // from whatever was declared in the file.
639 if (!m.isRoot() && Compiler.onImport(m))
640 {
641 m.importedFrom = m;
642 assert(m.isRoot());
643 }
644 return m;
645 }
646
kind()647 override const(char)* kind() const
648 {
649 return "module";
650 }
651
652 /*********************************************
653 * Combines things into output file name for .html and .di files.
654 * Input:
655 * name Command line name given for the file, NULL if none
656 * dir Command line directory given for the file, NULL if none
657 * arg Name of the source file
658 * ext File name extension to use if 'name' is NULL
659 * global.params.preservePaths get output path from arg
660 * srcfile Input file - output file name must not match input file
661 */
setOutfilename(const (char)[]name,const (char)[]dir,const (char)[]arg,const (char)[]ext)662 extern(D) FileName setOutfilename(const(char)[] name, const(char)[] dir, const(char)[] arg, const(char)[] ext)
663 {
664 const(char)[] docfilename;
665 if (name)
666 {
667 docfilename = name;
668 }
669 else
670 {
671 const(char)[] argdoc;
672 OutBuffer buf;
673 if (arg == "__stdin.d")
674 {
675 version (Posix)
676 import core.sys.posix.unistd : getpid;
677 else version (Windows)
678 import core.sys.windows.winbase : getpid = GetCurrentProcessId;
679 buf.printf("__stdin_%d.d", getpid());
680 arg = buf[];
681 }
682 if (global.params.preservePaths)
683 argdoc = arg;
684 else
685 argdoc = FileName.name(arg);
686 // If argdoc doesn't have an absolute path, make it relative to dir
687 if (!FileName.absolute(argdoc))
688 {
689 //FileName::ensurePathExists(dir);
690 argdoc = FileName.combine(dir, argdoc);
691 }
692 docfilename = FileName.forceExt(argdoc, ext);
693 }
694 if (FileName.equals(docfilename, srcfile.toString()))
695 {
696 error("source file and output file have same name '%s'", srcfile.toChars());
697 fatal();
698 }
699 return FileName(docfilename);
700 }
701
setDocfile()702 extern (D) void setDocfile()
703 {
704 docfile = setOutfilename(global.params.docname, global.params.docdir, arg, doc_ext);
705 }
706
707 /**
708 * Loads the source buffer from the given read result into `this.srcBuffer`.
709 *
710 * Will take ownership of the buffer located inside `readResult`.
711 *
712 * Params:
713 * loc = the location
714 * readResult = the result of reading a file containing the source code
715 *
716 * Returns: `true` if successful
717 */
718 bool loadSourceBuffer(const ref Loc loc, ref File.ReadResult readResult)
719 {
720 //printf("Module::loadSourceBuffer('%s') file '%s'\n", toChars(), srcfile.toChars());
721 // take ownership of buffer
722 srcBuffer = new FileBuffer(readResult.extractSlice());
723 if (readResult.success)
724 return true;
725
726 if (FileName.equals(srcfile.toString(), "object.d"))
727 {
728 .error(loc, "cannot find source code for runtime library file 'object.d'");
729 errorSupplemental(loc, "dmd might not be correctly installed. Run 'dmd -man' for installation instructions.");
730 const dmdConfFile = global.inifilename.length ? FileName.canonicalName(global.inifilename) : "not found";
731 errorSupplemental(loc, "config file: %.*s", cast(int)dmdConfFile.length, dmdConfFile.ptr);
732 }
733 else
734 {
735 // if module is not named 'package' but we're trying to read 'package.d', we're looking for a package module
736 bool isPackageMod = (strcmp(toChars(), "package") != 0) && (strcmp(srcfile.name(), package_d) == 0 || (strcmp(srcfile.name(), package_di) == 0));
737 if (isPackageMod)
738 .error(loc, "importing package '%s' requires a 'package.d' file which cannot be found in '%s'", toChars(), srcfile.toChars());
739 else
740 error(loc, "is in file '%s' which cannot be read", srcfile.toChars());
741 }
742 if (!global.gag)
743 {
744 /* Print path
745 */
746 if (global.path)
747 {
748 foreach (i, p; *global.path)
749 fprintf(stderr, "import path[%llu] = %s\n", cast(ulong)i, p);
750 }
751 else
752 {
753 fprintf(stderr, "Specify path to file '%s' with -I switch\n", srcfile.toChars());
754 }
755
756 removeHdrFilesAndFail(global.params, Module.amodules);
757 }
758 return false;
759 }
760
761 /**
762 * Reads the file from `srcfile` and loads the source buffer.
763 *
764 * If makefile module dependency is requested, we add this module
765 * to the list of dependencies from here.
766 *
767 * Params:
768 * loc = the location
769 *
770 * Returns: `true` if successful
771 * See_Also: loadSourceBuffer
772 */
read(const ref Loc loc)773 bool read(const ref Loc loc)
774 {
775 if (srcBuffer)
776 return true; // already read
777
778 //printf("Module::read('%s') file '%s'\n", toChars(), srcfile.toChars());
779 auto readResult = File.read(srcfile.toChars());
780
781 if (global.params.emitMakeDeps)
782 {
783 global.params.makeDeps.push(srcfile.toChars());
784 }
785
786 return loadSourceBuffer(loc, readResult);
787 }
788
789 /// syntactic parse
parse()790 Module parse()
791 {
792 return parseModule!ASTCodegen();
793 }
794
795 /// ditto
parseModule(AST)796 extern (D) Module parseModule(AST)()
797 {
798 enum Endian { little, big}
799 enum SourceEncoding { utf16, utf32}
800
801 /*
802 * Convert a buffer from UTF32 to UTF8
803 * Params:
804 * Endian = is the buffer big/little endian
805 * buf = buffer of UTF32 data
806 * Returns:
807 * input buffer reencoded as UTF8
808 */
809
810 char[] UTF32ToUTF8(Endian endian)(const(char)[] buf)
811 {
812 static if (endian == Endian.little)
813 alias readNext = Port.readlongLE;
814 else
815 alias readNext = Port.readlongBE;
816
817 if (buf.length & 3)
818 {
819 error("odd length of UTF-32 char source %llu", cast(ulong) buf.length);
820 fatal();
821 }
822
823 const (uint)[] eBuf = cast(const(uint)[])buf;
824
825 OutBuffer dbuf;
826 dbuf.reserve(eBuf.length);
827
828 foreach (i; 0 .. eBuf.length)
829 {
830 const u = readNext(&eBuf[i]);
831 if (u & ~0x7F)
832 {
833 if (u > 0x10FFFF)
834 {
835 error("UTF-32 value %08x greater than 0x10FFFF", u);
836 fatal();
837 }
838 dbuf.writeUTF8(u);
839 }
840 else
841 dbuf.writeByte(u);
842 }
843 dbuf.writeByte(0); //add null terminator
844 return dbuf.extractSlice();
845 }
846
847 /*
848 * Convert a buffer from UTF16 to UTF8
849 * Params:
850 * Endian = is the buffer big/little endian
851 * buf = buffer of UTF16 data
852 * Returns:
853 * input buffer reencoded as UTF8
854 */
855
856 char[] UTF16ToUTF8(Endian endian)(const(char)[] buf)
857 {
858 static if (endian == Endian.little)
859 alias readNext = Port.readwordLE;
860 else
861 alias readNext = Port.readwordBE;
862
863 if (buf.length & 1)
864 {
865 error("odd length of UTF-16 char source %llu", cast(ulong) buf.length);
866 fatal();
867 }
868
869 const (ushort)[] eBuf = cast(const(ushort)[])buf;
870
871 OutBuffer dbuf;
872 dbuf.reserve(eBuf.length);
873
874 //i will be incremented in the loop for high codepoints
875 foreach (ref i; 0 .. eBuf.length)
876 {
877 uint u = readNext(&eBuf[i]);
878 if (u & ~0x7F)
879 {
880 if (0xD800 <= u && u < 0xDC00)
881 {
882 i++;
883 if (i >= eBuf.length)
884 {
885 error("surrogate UTF-16 high value %04x at end of file", u);
886 fatal();
887 }
888 const u2 = readNext(&eBuf[i]);
889 if (u2 < 0xDC00 || 0xE000 <= u2)
890 {
891 error("surrogate UTF-16 low value %04x out of range", u2);
892 fatal();
893 }
894 u = (u - 0xD7C0) << 10;
895 u |= (u2 - 0xDC00);
896 }
897 else if (u >= 0xDC00 && u <= 0xDFFF)
898 {
899 error("unpaired surrogate UTF-16 value %04x", u);
900 fatal();
901 }
902 else if (u == 0xFFFE || u == 0xFFFF)
903 {
904 error("illegal UTF-16 value %04x", u);
905 fatal();
906 }
907 dbuf.writeUTF8(u);
908 }
909 else
910 dbuf.writeByte(u);
911 }
912 dbuf.writeByte(0); //add a terminating null byte
913 return dbuf.extractSlice();
914 }
915
916 const(char)* srcname = srcfile.toChars();
917 //printf("Module::parse(srcname = '%s')\n", srcname);
918 isPackageFile = (strcmp(srcfile.name(), package_d) == 0 ||
919 strcmp(srcfile.name(), package_di) == 0);
920 const(char)[] buf = cast(const(char)[]) srcBuffer.data;
921
922 bool needsReencoding = true;
923 bool hasBOM = true; //assume there's a BOM
924 Endian endian;
925 SourceEncoding sourceEncoding;
926
927 if (buf.length >= 2)
928 {
929 /* Convert all non-UTF-8 formats to UTF-8.
930 * BOM : http://www.unicode.org/faq/utf_bom.html
931 * 00 00 FE FF UTF-32BE, big-endian
932 * FF FE 00 00 UTF-32LE, little-endian
933 * FE FF UTF-16BE, big-endian
934 * FF FE UTF-16LE, little-endian
935 * EF BB BF UTF-8
936 */
937 if (buf[0] == 0xFF && buf[1] == 0xFE)
938 {
939 endian = Endian.little;
940
941 sourceEncoding = buf.length >= 4 && buf[2] == 0 && buf[3] == 0
942 ? SourceEncoding.utf32
943 : SourceEncoding.utf16;
944 }
945 else if (buf[0] == 0xFE && buf[1] == 0xFF)
946 {
947 endian = Endian.big;
948 sourceEncoding = SourceEncoding.utf16;
949 }
950 else if (buf.length >= 4 && buf[0] == 0 && buf[1] == 0 && buf[2] == 0xFE && buf[3] == 0xFF)
951 {
952 endian = Endian.big;
953 sourceEncoding = SourceEncoding.utf32;
954 }
955 else if (buf.length >= 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF)
956 {
957 needsReencoding = false;//utf8 with BOM
958 }
959 else
960 {
961 /* There is no BOM. Make use of Arcane Jill's insight that
962 * the first char of D source must be ASCII to
963 * figure out the encoding.
964 */
965 hasBOM = false;
966 if (buf.length >= 4 && buf[1] == 0 && buf[2] == 0 && buf[3] == 0)
967 {
968 endian = Endian.little;
969 sourceEncoding = SourceEncoding.utf32;
970 }
971 else if (buf.length >= 4 && buf[0] == 0 && buf[1] == 0 && buf[2] == 0)
972 {
973 endian = Endian.big;
974 sourceEncoding = SourceEncoding.utf32;
975 }
976 else if (buf.length >= 2 && buf[1] == 0) //try to check for UTF-16
977 {
978 endian = Endian.little;
979 sourceEncoding = SourceEncoding.utf16;
980 }
981 else if (buf[0] == 0)
982 {
983 endian = Endian.big;
984 sourceEncoding = SourceEncoding.utf16;
985 }
986 else {
987 // It's UTF-8
988 needsReencoding = false;
989 if (buf[0] >= 0x80)
990 {
991 error("source file must start with BOM or ASCII character, not \\x%02X", buf[0]);
992 fatal();
993 }
994 }
995 }
996 //throw away BOM
997 if (hasBOM)
998 {
999 if (!needsReencoding) buf = buf[3..$];// utf-8 already
1000 else if (sourceEncoding == SourceEncoding.utf32) buf = buf[4..$];
1001 else buf = buf[2..$]; //utf 16
1002 }
1003 }
1004 // Assume the buffer is from memory and has not be read from disk. Assume UTF-8.
1005 else if (buf.length >= 1 && (buf[0] == '\0' || buf[0] == 0x1A))
1006 needsReencoding = false;
1007 //printf("%s, %d, %d, %d\n", srcfile.name.toChars(), needsReencoding, endian == Endian.little, sourceEncoding == SourceEncoding.utf16);
1008 if (needsReencoding)
1009 {
1010 if (sourceEncoding == SourceEncoding.utf16)
1011 {
1012 buf = endian == Endian.little
1013 ? UTF16ToUTF8!(Endian.little)(buf)
1014 : UTF16ToUTF8!(Endian.big)(buf);
1015 }
1016 else
1017 {
1018 buf = endian == Endian.little
1019 ? UTF32ToUTF8!(Endian.little)(buf)
1020 : UTF32ToUTF8!(Endian.big)(buf);
1021 }
1022 }
1023
1024 /* If it starts with the string "Ddoc", then it's a documentation
1025 * source file.
1026 */
1027 if (buf.length>= 4 && buf[0..4] == "Ddoc")
1028 {
1029 comment = buf.ptr + 4;
1030 isDocFile = true;
1031 if (!docfile)
1032 setDocfile();
1033 return this;
1034 }
1035 /* If it has the extension ".dd", it is also a documentation
1036 * source file. Documentation source files may begin with "Ddoc"
1037 * but do not have to if they have the .dd extension.
1038 * https://issues.dlang.org/show_bug.cgi?id=15465
1039 */
1040 if (FileName.equalsExt(arg, dd_ext))
1041 {
1042 comment = buf.ptr; // the optional Ddoc, if present, is handled above.
1043 isDocFile = true;
1044 if (!docfile)
1045 setDocfile();
1046 return this;
1047 }
1048 /* If it has the extension ".di", it is a "header" file.
1049 */
1050 if (FileName.equalsExt(arg, hdr_ext))
1051 {
1052 isHdrFile = true;
1053 }
1054
1055 /* If it has the extension ".c", it is a "C" file.
1056 * If it has the extension ".i", it is a preprocessed "C" file.
1057 */
1058 if (FileName.equalsExt(arg, c_ext) || FileName.equalsExt(arg, i_ext))
1059 {
1060 isCFile = true;
1061
1062 scope p = new CParser!AST(this, buf, cast(bool) docfile, target.c);
1063 p.nextToken();
1064 members = p.parseModule();
1065 md = p.md;
1066 numlines = p.scanloc.linnum;
1067 }
1068 else
1069 {
1070 scope p = new Parser!AST(this, buf, cast(bool) docfile);
1071 p.nextToken();
1072 members = p.parseModule();
1073 md = p.md;
1074 numlines = p.scanloc.linnum;
1075 }
1076 srcBuffer.destroy();
1077 srcBuffer = null;
1078 /* The symbol table into which the module is to be inserted.
1079 */
1080 DsymbolTable dst;
1081 if (md)
1082 {
1083 /* A ModuleDeclaration, md, was provided.
1084 * The ModuleDeclaration sets the packages this module appears in, and
1085 * the name of this module.
1086 */
1087 this.ident = md.id;
1088 Package ppack = null;
1089 dst = Package.resolve(md.packages, &this.parent, &ppack);
1090
1091 // Mark the package path as accessible from the current module
1092 // https://issues.dlang.org/show_bug.cgi?id=21661
1093 // Code taken from Import.addPackageAccess()
1094 if (md.packages.length > 0)
1095 {
1096 // module a.b.c.d;
1097 auto p = ppack; // a
1098 addAccessiblePackage(p, Visibility(Visibility.Kind.private_));
1099 foreach (id; md.packages[1 .. $]) // [b, c]
1100 {
1101 p = cast(Package) p.symtab.lookup(id);
1102 if (p is null)
1103 break;
1104 addAccessiblePackage(p, Visibility(Visibility.Kind.private_));
1105 }
1106 }
1107 assert(dst);
1108 Module m = ppack ? ppack.isModule() : null;
1109 if (m && (strcmp(m.srcfile.name(), package_d) != 0 &&
1110 strcmp(m.srcfile.name(), package_di) != 0))
1111 {
1112 .error(md.loc, "package name '%s' conflicts with usage as a module name in file %s", ppack.toPrettyChars(), m.srcfile.toChars());
1113 }
1114 }
1115 else
1116 {
1117 /* The name of the module is set to the source file name.
1118 * There are no packages.
1119 */
1120 dst = modules; // and so this module goes into global module symbol table
1121 /* Check to see if module name is a valid identifier
1122 */
1123 if (!Identifier.isValidIdentifier(this.ident.toChars()))
1124 error("has non-identifier characters in filename, use module declaration instead");
1125 }
1126 // Insert module into the symbol table
1127 Dsymbol s = this;
1128 if (isPackageFile)
1129 {
1130 /* If the source tree is as follows:
1131 * pkg/
1132 * +- package.d
1133 * +- common.d
1134 * the 'pkg' will be incorporated to the internal package tree in two ways:
1135 * import pkg;
1136 * and:
1137 * import pkg.common;
1138 *
1139 * If both are used in one compilation, 'pkg' as a module (== pkg/package.d)
1140 * and a package name 'pkg' will conflict each other.
1141 *
1142 * To avoid the conflict:
1143 * 1. If preceding package name insertion had occurred by Package::resolve,
1144 * reuse the previous wrapping 'Package' if it exists
1145 * 2. Otherwise, 'package.d' wrapped by 'Package' is inserted to the internal tree in here.
1146 *
1147 * Then change Package::isPkgMod to PKG.module_ and set Package::mod.
1148 *
1149 * Note that the 'wrapping Package' is the Package that contains package.d and other submodules,
1150 * the one inserted to the symbol table.
1151 */
1152 auto ps = dst.lookup(ident);
1153 Package p = ps ? ps.isPackage() : null;
1154 if (p is null)
1155 {
1156 p = new Package(Loc.initial, ident);
1157 p.tag = this.tag; // reuse the same package tag
1158 p.symtab = new DsymbolTable();
1159 }
1160 this.tag = p.tag; // reuse the 'older' package tag
1161 this.pkg = p;
1162 p.parent = this.parent;
1163 p.isPkgMod = PKG.module_;
1164 p.mod = this;
1165 s = p;
1166 }
1167 if (!dst.insert(s))
1168 {
1169 /* It conflicts with a name that is already in the symbol table.
1170 * Figure out what went wrong, and issue error message.
1171 */
1172 Dsymbol prev = dst.lookup(ident);
1173 assert(prev);
1174 if (Module mprev = prev.isModule())
1175 {
1176 if (!FileName.equals(srcname, mprev.srcfile.toChars()))
1177 error(loc, "from file %s conflicts with another module %s from file %s", srcname, mprev.toChars(), mprev.srcfile.toChars());
1178 else if (isRoot() && mprev.isRoot())
1179 error(loc, "from file %s is specified twice on the command line", srcname);
1180 else
1181 error(loc, "from file %s must be imported with 'import %s;'", srcname, toPrettyChars());
1182 // https://issues.dlang.org/show_bug.cgi?id=14446
1183 // Return previously parsed module to avoid AST duplication ICE.
1184 return mprev;
1185 }
1186 else if (Package pkg = prev.isPackage())
1187 {
1188 // 'package.d' loaded after a previous 'Package' insertion
1189 if (isPackageFile)
1190 amodules.push(this); // Add to global array of all modules
1191 else
1192 error(md ? md.loc : loc, "from file %s conflicts with package name %s", srcname, pkg.toChars());
1193 }
1194 else
1195 assert(global.errors);
1196 }
1197 else
1198 {
1199 // Add to global array of all modules
1200 amodules.push(this);
1201 }
1202 Compiler.onParseModule(this);
1203 return this;
1204 }
1205
importAll(Scope * prevsc)1206 override void importAll(Scope* prevsc)
1207 {
1208 //printf("+Module::importAll(this = %p, '%s'): parent = %p\n", this, toChars(), parent);
1209 if (_scope)
1210 return; // already done
1211 if (isDocFile)
1212 {
1213 error("is a Ddoc file, cannot import it");
1214 return;
1215 }
1216
1217 /* Note that modules get their own scope, from scratch.
1218 * This is so regardless of where in the syntax a module
1219 * gets imported, it is unaffected by context.
1220 * Ignore prevsc.
1221 */
1222 Scope* sc = Scope.createGlobal(this); // create root scope
1223
1224 if (md && md.msg)
1225 md.msg = semanticString(sc, md.msg, "deprecation message");
1226
1227 // Add import of "object", even for the "object" module.
1228 // If it isn't there, some compiler rewrites, like
1229 // classinst == classinst -> .object.opEquals(classinst, classinst)
1230 // would fail inside object.d.
1231 if (members.dim == 0 || (*members)[0].ident != Id.object ||
1232 (*members)[0].isImport() is null)
1233 {
1234 auto im = new Import(Loc.initial, null, Id.object, null, 0);
1235 members.shift(im);
1236 }
1237 if (!symtab)
1238 {
1239 // Add all symbols into module's symbol table
1240 symtab = new DsymbolTable();
1241 for (size_t i = 0; i < members.dim; i++)
1242 {
1243 Dsymbol s = (*members)[i];
1244 s.addMember(sc, sc.scopesym);
1245 }
1246 }
1247 // anything else should be run after addMember, so version/debug symbols are defined
1248 /* Set scope for the symbols so that if we forward reference
1249 * a symbol, it can possibly be resolved on the spot.
1250 * If this works out well, it can be extended to all modules
1251 * before any semantic() on any of them.
1252 */
1253 setScope(sc); // remember module scope for semantic
1254 for (size_t i = 0; i < members.dim; i++)
1255 {
1256 Dsymbol s = (*members)[i];
1257 s.setScope(sc);
1258 }
1259 for (size_t i = 0; i < members.dim; i++)
1260 {
1261 Dsymbol s = (*members)[i];
1262 s.importAll(sc);
1263 }
1264 sc = sc.pop();
1265 sc.pop(); // 2 pops because Scope.createGlobal() created 2
1266 }
1267
1268 /**********************************
1269 * Determine if we need to generate an instance of ModuleInfo
1270 * for this Module.
1271 */
needModuleInfo()1272 int needModuleInfo()
1273 {
1274 //printf("needModuleInfo() %s, %d, %d\n", toChars(), needmoduleinfo, global.params.cov);
1275 return needmoduleinfo || global.params.cov;
1276 }
1277
1278 /*******************************************
1279 * Print deprecation warning if we're deprecated, when
1280 * this module is imported from scope sc.
1281 *
1282 * Params:
1283 * sc = the scope into which we are imported
1284 * loc = the location of the import statement
1285 */
checkImportDeprecation(const ref Loc loc,Scope * sc)1286 void checkImportDeprecation(const ref Loc loc, Scope* sc)
1287 {
1288 if (md && md.isdeprecated && !sc.isDeprecated)
1289 {
1290 Expression msg = md.msg;
1291 if (StringExp se = msg ? msg.toStringExp() : null)
1292 {
1293 const slice = se.peekString();
1294 deprecation(loc, "is deprecated - %.*s", cast(int)slice.length, slice.ptr);
1295 }
1296 else
1297 deprecation(loc, "is deprecated");
1298 }
1299 }
1300
1301 override Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly)
1302 {
1303 /* Since modules can be circularly referenced,
1304 * need to stop infinite recursive searches.
1305 * This is done with the cache.
1306 */
1307 //printf("%s Module.search('%s', flags = x%x) insearch = %d\n", toChars(), ident.toChars(), flags, insearch);
1308 if (insearch)
1309 return null;
1310
1311 /* Qualified module searches always search their imports,
1312 * even if SearchLocalsOnly
1313 */
1314 if (!(flags & SearchUnqualifiedModule))
1315 flags &= ~(SearchUnqualifiedModule | SearchLocalsOnly);
1316
1317 if (searchCacheIdent == ident && searchCacheFlags == flags)
1318 {
1319 //printf("%s Module::search('%s', flags = %d) insearch = %d searchCacheSymbol = %s\n",
1320 // toChars(), ident.toChars(), flags, insearch, searchCacheSymbol ? searchCacheSymbol.toChars() : "null");
1321 return searchCacheSymbol;
1322 }
1323
1324 uint errors = global.errors;
1325
1326 insearch = 1;
1327 Dsymbol s = ScopeDsymbol.search(loc, ident, flags);
1328 insearch = 0;
1329
1330 if (errors == global.errors)
1331 {
1332 // https://issues.dlang.org/show_bug.cgi?id=10752
1333 // Can cache the result only when it does not cause
1334 // access error so the side-effect should be reproduced in later search.
1335 searchCacheIdent = ident;
1336 searchCacheSymbol = s;
1337 searchCacheFlags = flags;
1338 }
1339 return s;
1340 }
1341
1342 override bool isPackageAccessible(Package p, Visibility visibility, int flags = 0)
1343 {
1344 if (insearch) // don't follow import cycles
1345 return false;
1346 insearch = true;
1347 scope (exit)
1348 insearch = false;
1349 if (flags & IgnorePrivateImports)
1350 visibility = Visibility(Visibility.Kind.public_); // only consider public imports
1351 return super.isPackageAccessible(p, visibility);
1352 }
1353
symtabInsert(Dsymbol s)1354 override Dsymbol symtabInsert(Dsymbol s)
1355 {
1356 searchCacheIdent = null; // symbol is inserted, so invalidate cache
1357 return Package.symtabInsert(s);
1358 }
1359
deleteObjFile()1360 void deleteObjFile()
1361 {
1362 if (global.params.obj)
1363 File.remove(objfile.toChars());
1364 if (docfile)
1365 File.remove(docfile.toChars());
1366 }
1367
1368 /*******************************************
1369 * Can't run semantic on s now, try again later.
1370 */
addDeferredSemantic(Dsymbol s)1371 extern (D) static void addDeferredSemantic(Dsymbol s)
1372 {
1373 //printf("Module::addDeferredSemantic('%s')\n", s.toChars());
1374 deferred.push(s);
1375 }
1376
addDeferredSemantic2(Dsymbol s)1377 extern (D) static void addDeferredSemantic2(Dsymbol s)
1378 {
1379 //printf("Module::addDeferredSemantic2('%s')\n", s.toChars());
1380 deferred2.push(s);
1381 }
1382
addDeferredSemantic3(Dsymbol s)1383 extern (D) static void addDeferredSemantic3(Dsymbol s)
1384 {
1385 //printf("Module::addDeferredSemantic3('%s')\n", s.toChars());
1386 deferred3.push(s);
1387 }
1388
1389 /******************************************
1390 * Run semantic() on deferred symbols.
1391 */
runDeferredSemantic()1392 static void runDeferredSemantic()
1393 {
1394 if (dprogress == 0)
1395 return;
1396
1397 __gshared int nested;
1398 if (nested)
1399 return;
1400 //if (deferred.dim) printf("+Module::runDeferredSemantic(), len = %d\n", deferred.dim);
1401 nested++;
1402
1403 size_t len;
1404 do
1405 {
1406 dprogress = 0;
1407 len = deferred.dim;
1408 if (!len)
1409 break;
1410
1411 Dsymbol* todo;
1412 Dsymbol* todoalloc = null;
1413 Dsymbol tmp;
1414 if (len == 1)
1415 {
1416 todo = &tmp;
1417 }
1418 else
1419 {
1420 todo = cast(Dsymbol*)Mem.check(malloc(len * Dsymbol.sizeof));
1421 todoalloc = todo;
1422 }
1423 memcpy(todo, deferred.tdata(), len * Dsymbol.sizeof);
1424 deferred.setDim(0);
1425
1426 foreach (i; 0..len)
1427 {
1428 Dsymbol s = todo[i];
1429 s.dsymbolSemantic(null);
1430 //printf("deferred: %s, parent = %s\n", s.toChars(), s.parent.toChars());
1431 }
1432 //printf("\tdeferred.dim = %d, len = %d, dprogress = %d\n", deferred.dim, len, dprogress);
1433 if (todoalloc)
1434 free(todoalloc);
1435 }
1436 while (deferred.dim < len || dprogress); // while making progress
1437 nested--;
1438 //printf("-Module::runDeferredSemantic(), len = %d\n", deferred.dim);
1439 }
1440
runDeferredSemantic2()1441 static void runDeferredSemantic2()
1442 {
1443 Module.runDeferredSemantic();
1444
1445 Dsymbols* a = &Module.deferred2;
1446 for (size_t i = 0; i < a.dim; i++)
1447 {
1448 Dsymbol s = (*a)[i];
1449 //printf("[%d] %s semantic2a\n", i, s.toPrettyChars());
1450 s.semantic2(null);
1451
1452 if (global.errors)
1453 break;
1454 }
1455 a.setDim(0);
1456 }
1457
runDeferredSemantic3()1458 static void runDeferredSemantic3()
1459 {
1460 Module.runDeferredSemantic2();
1461
1462 Dsymbols* a = &Module.deferred3;
1463 for (size_t i = 0; i < a.dim; i++)
1464 {
1465 Dsymbol s = (*a)[i];
1466 //printf("[%d] %s semantic3a\n", i, s.toPrettyChars());
1467 s.semantic3(null);
1468
1469 if (global.errors)
1470 break;
1471 }
1472 a.setDim(0);
1473 }
1474
clearCache()1475 extern (D) static void clearCache()
1476 {
1477 foreach (Module m; amodules)
1478 m.searchCacheIdent = null;
1479 }
1480
1481 /************************************
1482 * Recursively look at every module this module imports,
1483 * return true if it imports m.
1484 * Can be used to detect circular imports.
1485 */
imports(Module m)1486 int imports(Module m)
1487 {
1488 //printf("%s Module::imports(%s)\n", toChars(), m.toChars());
1489 version (none)
1490 {
1491 foreach (i, Module mi; aimports)
1492 printf("\t[%d] %s\n", cast(int) i, mi.toChars());
1493 }
1494 foreach (Module mi; aimports)
1495 {
1496 if (mi == m)
1497 return true;
1498 if (!mi.insearch)
1499 {
1500 mi.insearch = 1;
1501 int r = mi.imports(m);
1502 if (r)
1503 return r;
1504 }
1505 }
1506 return false;
1507 }
1508
isRoot()1509 bool isRoot()
1510 {
1511 return this.importedFrom == this;
1512 }
1513
1514 // true if the module source file is directly
1515 // listed in command line.
isCoreModule(Identifier ident)1516 bool isCoreModule(Identifier ident)
1517 {
1518 return this.ident == ident && parent && parent.ident == Id.core && !parent.parent;
1519 }
1520
1521 // Back end
1522 int doppelganger; // sub-module
1523 Symbol* cov; // private uint[] __coverage;
1524 uint* covb; // bit array of valid code line numbers
1525 Symbol* sictor; // module order independent constructor
1526 Symbol* sctor; // module constructor
1527 Symbol* sdtor; // module destructor
1528 Symbol* ssharedctor; // module shared constructor
1529 Symbol* sshareddtor; // module shared destructor
1530 Symbol* stest; // module unit test
1531 Symbol* sfilename; // symbol for filename
1532
1533 uint[uint] ctfe_cov; /// coverage information from ctfe execution_count[line]
1534
inout(Module)1535 override inout(Module) isModule() inout
1536 {
1537 return this;
1538 }
1539
accept(Visitor v)1540 override void accept(Visitor v)
1541 {
1542 v.visit(this);
1543 }
1544
1545 /***********************************************
1546 * Writes this module's fully-qualified name to buf
1547 * Params:
1548 * buf = The buffer to write to
1549 */
fullyQualifiedName(ref OutBuffer buf)1550 void fullyQualifiedName(ref OutBuffer buf)
1551 {
1552 buf.writestring(ident.toString());
1553
1554 for (auto package_ = parent; package_ !is null; package_ = package_.parent)
1555 {
1556 buf.prependstring(".");
1557 buf.prependstring(package_.ident.toChars());
1558 }
1559 }
1560
1561 /** Lazily initializes and returns the escape table.
1562 Turns out it eats a lot of memory.
1563 */
escapetable()1564 extern(D) Escape* escapetable()
1565 {
1566 if (!_escapetable)
1567 _escapetable = new Escape();
1568 return _escapetable;
1569 }
1570 }
1571
1572 /***********************************************************
1573 */
1574 extern (C++) struct ModuleDeclaration
1575 {
1576 Loc loc;
1577 Identifier id;
1578 Identifier[] packages; // array of Identifier's representing packages
1579 bool isdeprecated; // if it is a deprecated module
1580 Expression msg;
1581
thisModuleDeclaration1582 extern (D) this(const ref Loc loc, Identifier[] packages, Identifier id, Expression msg, bool isdeprecated)
1583 {
1584 this.loc = loc;
1585 this.packages = packages;
1586 this.id = id;
1587 this.msg = msg;
1588 this.isdeprecated = isdeprecated;
1589 }
1590
toCharsModuleDeclaration1591 extern (C++) const(char)* toChars() const
1592 {
1593 OutBuffer buf;
1594 foreach (pid; packages)
1595 {
1596 buf.writestring(pid.toString());
1597 buf.writeByte('.');
1598 }
1599 buf.writestring(id.toString());
1600 return buf.extractChars();
1601 }
1602
1603 /// Provide a human readable representation
toStringModuleDeclaration1604 extern (D) const(char)[] toString() const
1605 {
1606 return this.toChars().toDString;
1607 }
1608 }
1609