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