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