1
/**
2
 * Defines a package and module.
3
 *
4
 * Specification: $(LINK2 https://dlang.org/spec/module.html, Modules)
5
 *
6
 * Copyright:   Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
7
 * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
8
 * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9
 * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/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.file;
38
import dmd.root.filename;
39
import dmd.root.outbuffer;
40
import dmd.root.port;
41
import dmd.root.rmem;
42
import dmd.root.rootobject;
43
import dmd.root.string;
44
import dmd.semantic2;
45
import dmd.semantic3;
46
import dmd.visitor;
47

48
version(Windows) {
49
    extern (C) char* getcwd(char* buffer, size_t maxlen);
50
} else {
51
    import core.sys.posix.unistd : getcwd;
52
}
53

54
/* ===========================  ===================== */
55
/********************************************
56
 * Look for the source file if it's different from filename.
57
 * Look for .di, .d, directory, and along global.path.
58
 * Does not open the file.
59
 * Input:
60
 *      filename        as supplied by the user
61
 *      global.path
62
 * Returns:
63
 *      NULL if it's not different from filename.
64
 */
65
private const(char)[] lookForSourceFile(const(char)[] filename)
66
{
67
    /* Search along global.path for .di file, then .d file.
68
     */
69 1
    const sdi = FileName.forceExt(filename, global.hdr_ext);
70 1
    if (FileName.exists(sdi) == 1)
71 0
        return sdi;
72 1
    scope(exit) FileName.free(sdi.ptr);
73 1
    const sd = FileName.forceExt(filename, global.mars_ext);
74 1
    if (FileName.exists(sd) == 1)
75 1
        return sd;
76 1
    scope(exit) FileName.free(sd.ptr);
77 1
    if (FileName.exists(filename) == 2)
78
    {
79
        /* The filename exists and it's a directory.
80
         * Therefore, the result should be: filename/package.d
81
         * iff filename/package.d is a file
82
         */
83 1
        const ni = FileName.combine(filename, "package.di");
84 1
        if (FileName.exists(ni) == 1)
85 0
            return ni;
86 1
        FileName.free(ni.ptr);
87 1
        const n = FileName.combine(filename, "package.d");
88 1
        if (FileName.exists(n) == 1)
89 0
            return n;
90 1
        FileName.free(n.ptr);
91
    }
92 1
    if (FileName.absolute(filename))
93 0
        return null;
94 1
    if (!global.path)
95 0
        return null;
96 1
    for (size_t i = 0; i < global.path.dim; i++)
97
    {
98 1
        const p = (*global.path)[i].toDString();
99 1
        const(char)[] n = FileName.combine(p, sdi);
100 1
        if (FileName.exists(n) == 1) {
101 1
            return n;
102
        }
103 1
        FileName.free(n.ptr);
104 1
        n = FileName.combine(p, sd);
105 1
        if (FileName.exists(n) == 1) {
106 1
            return n;
107
        }
108 1
        FileName.free(n.ptr);
109 1
        const b = FileName.removeExt(filename);
110 1
        n = FileName.combine(p, b);
111 1
        FileName.free(b.ptr);
112 1
        if (FileName.exists(n) == 2)
113
        {
114 1
            const n2i = FileName.combine(n, "package.di");
115 1
            if (FileName.exists(n2i) == 1)
116 1
                return n2i;
117 1
            FileName.free(n2i.ptr);
118 1
            const n2 = FileName.combine(n, "package.d");
119 1
            if (FileName.exists(n2) == 1) {
120 1
                return n2;
121
            }
122 1
            FileName.free(n2.ptr);
123
        }
124 1
        FileName.free(n.ptr);
125
    }
126 1
    return null;
127
}
128

129
// function used to call semantic3 on a module's dependencies
130
void semantic3OnDependencies(Module m)
131
{
132 1
    if (!m)
133 0
        return;
134

135 1
    if (m.semanticRun > PASS.semantic3)
136 1
        return;
137

138 1
    m.semantic3(null);
139

140 1
    foreach (i; 1 .. m.aimports.dim)
141 1
        semantic3OnDependencies(m.aimports[i]);
142
}
143

144
/**
145
 * Remove generated .di files on error and exit
146
 */
147
void removeHdrFilesAndFail(ref Param params, ref Modules modules)
148
{
149 1
    if (params.doHdrGeneration)
150
    {
151 1
        foreach (m; modules)
152
        {
153 1
            if (m.isHdrFile)
154 0
                continue;
155 1
            File.remove(m.hdrfile.toChars());
156
        }
157
    }
158

159 1
    fatal();
160
}
161

162
/**
163
 * Converts a chain of identifiers to the filename of the module
164
 *
165
 * Params:
166
 *  packages = the names of the "parent" packages
167
 *  ident = the name of the child package or module
168
 *
169
 * Returns:
170
 *  the filename of the child package or module
171
 */
172
private const(char)[] getFilename(Identifiers* packages, Identifier ident)
173
{
174 1
    const(char)[] filename = ident.toString();
175

176 1
    if (packages == null || packages.dim == 0)
177 1
        return filename;
178

179 1
    OutBuffer buf;
180 1
    OutBuffer dotmods;
181 1
    auto modAliases = &global.params.modFileAliasStrings;
182

183
    void checkModFileAlias(const(char)[] p)
184
    {
185
        /* Check and replace the contents of buf[] with
186
        * an alias string from global.params.modFileAliasStrings[]
187
        */
188 1
        dotmods.writestring(p);
189 1
        foreach_reverse (const m; *modAliases)
190
        {
191 1
            const q = strchr(m, '=');
192 1
            assert(q);
193 1
            if (dotmods.length == q - m && memcmp(dotmods.peekChars(), m, q - m) == 0)
194
            {
195 1
                buf.setsize(0);
196 1
                auto rhs = q[1 .. strlen(q)];
197 1
                if (rhs.length > 0 && (rhs[$ - 1] == '/' || rhs[$ - 1] == '\\'))
198 1
                    rhs = rhs[0 .. $ - 1]; // remove trailing separator
199 1
                buf.writestring(rhs);
200 1
                break; // last matching entry in ms[] wins
201
            }
202
        }
203 1
        dotmods.writeByte('.');
204
    }
205

206 1
    foreach (pid; *packages)
207
    {
208 1
        const p = pid.toString();
209 1
        buf.writestring(p);
210 1
        if (modAliases.dim)
211 1
            checkModFileAlias(p);
212
        version (Windows)
213
            enum FileSeparator = '\\';
214
        else
215
            enum FileSeparator = '/';
216 1
        buf.writeByte(FileSeparator);
217
    }
218 1
    buf.writestring(filename);
219 1
    if (modAliases.dim)
220 1
        checkModFileAlias(filename);
221 1
    buf.writeByte(0);
222 1
    filename = buf.extractSlice()[0 .. $ - 1];
223

224 1
    return filename;
225
}
226

227
enum PKG : int
228
{
229
    unknown,     // not yet determined whether it's a package.d or not
230
    module_,      // already determined that's an actual package.d
231
    package_,     // already determined that's an actual package
232
}
233

234
/***********************************************************
235
 */
236
extern (C++) class Package : ScopeDsymbol
237
{
238
    PKG isPkgMod = PKG.unknown;
239
    uint tag;        // auto incremented tag, used to mask package tree in scopes
240
    Module mod;     // !=null if isPkgMod == PKG.module_
241

242 1
    final extern (D) this(const ref Loc loc, Identifier ident)
243
    {
244 1
        super(loc, ident);
245 1
        __gshared uint packageTag;
246 1
        this.tag = packageTag++;
247
    }
248

249
    override const(char)* kind() const
250
    {
251 1
        return "package";
252
    }
253

254
    override bool equals(const RootObject o) const
255
    {
256
        // custom 'equals' for bug 17441. "package a" and "module a" are not equal
257 1
        if (this == o)
258 1
            return true;
259 1
        auto p = cast(Package)o;
260 1
        return p && isModule() == p.isModule() && ident.equals(p.ident);
261
    }
262

263
    /****************************************************
264
     * Input:
265
     *      packages[]      the pkg1.pkg2 of pkg1.pkg2.mod
266
     * Returns:
267
     *      the symbol table that mod should be inserted into
268
     * Output:
269
     *      *pparent        the rightmost package, i.e. pkg2, or NULL if no packages
270
     *      *ppkg           the leftmost package, i.e. pkg1, or NULL if no packages
271
     */
272
    extern (D) static DsymbolTable resolve(Identifiers* packages, Dsymbol* pparent, Package* ppkg)
273
    {
274 1
        DsymbolTable dst = Module.modules;
275 1
        Dsymbol parent = null;
276
        //printf("Package::resolve()\n");
277 1
        if (ppkg)
278 1
            *ppkg = null;
279 1
        if (packages)
280
        {
281 1
            for (size_t i = 0; i < packages.dim; i++)
282
            {
283 1
                Identifier pid = (*packages)[i];
284 1
                Package pkg;
285 1
                Dsymbol p = dst.lookup(pid);
286 1
                if (!p)
287
                {
288 1
                    pkg = new Package(Loc.initial, pid);
289 1
                    dst.insert(pkg);
290 1
                    pkg.parent = parent;
291 1
                    pkg.symtab = new DsymbolTable();
292
                }
293
                else
294
                {
295 1
                    pkg = p.isPackage();
296 1
                    assert(pkg);
297
                    // It might already be a module, not a package, but that needs
298
                    // to be checked at a higher level, where a nice error message
299
                    // can be generated.
300
                    // dot net needs modules and packages with same name
301
                    // But we still need a symbol table for it
302 1
                    if (!pkg.symtab)
303 1
                        pkg.symtab = new DsymbolTable();
304
                }
305 1
                parent = pkg;
306 1
                dst = pkg.symtab;
307 1
                if (ppkg && !*ppkg)
308 1
                    *ppkg = pkg;
309 1
                if (pkg.isModule())
310
                {
311
                    // Return the module so that a nice error message can be generated
312 1
                    if (ppkg)
313 1
                        *ppkg = cast(Package)p;
314 1
                    break;
315
                }
316
            }
317
        }
318 1
        if (pparent)
319 1
            *pparent = parent;
320 1
        return dst;
321
    }
322

323
    override final inout(Package) isPackage() inout
324
    {
325 1
        return this;
326
    }
327

328
    /**
329
     * Checks if pkg is a sub-package of this
330
     *
331
     * For example, if this qualifies to 'a1.a2' and pkg - to 'a1.a2.a3',
332
     * this function returns 'true'. If it is other way around or qualified
333
     * package paths conflict function returns 'false'.
334
     *
335
     * Params:
336
     *  pkg = possible subpackage
337
     *
338
     * Returns:
339
     *  see description
340
     */
341
    final bool isAncestorPackageOf(const Package pkg) const
342
    {
343 1
        if (this == pkg)
344 1
            return true;
345 1
        if (!pkg || !pkg.parent)
346 1
            return false;
347 1
        return isAncestorPackageOf(pkg.parent.isPackage());
348
    }
349

350
    override Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly)
351
    {
352
        //printf("%s Package.search('%s', flags = x%x)\n", toChars(), ident.toChars(), flags);
353 1
        flags &= ~SearchLocalsOnly;  // searching an import is always transitive
354 1
        if (!isModule() && mod)
355
        {
356
            // Prefer full package name.
357 1
            Dsymbol s = symtab ? symtab.lookup(ident) : null;
358 1
            if (s)
359 1
                return s;
360
            //printf("[%s] through pkdmod: %s\n", loc.toChars(), toChars());
361 1
            return mod.search(loc, ident, flags);
362
        }
363 1
        return ScopeDsymbol.search(loc, ident, flags);
364
    }
365

366
    override void accept(Visitor v)
367
    {
368 1
        v.visit(this);
369
    }
370

371
    final Module isPackageMod()
372
    {
373 1
        if (isPkgMod == PKG.module_)
374
        {
375 1
            return mod;
376
        }
377 1
        return null;
378
    }
379

380
    /**
381
     * Checks for the existence of a package.d to set isPkgMod appropriately
382
     * if isPkgMod == PKG.unknown
383
     */
384
    final void resolvePKGunknown()
385
    {
386 1
        if (isModule())
387 1
            return;
388 1
        if (isPkgMod != PKG.unknown)
389 1
            return;
390

391 1
        Identifiers packages;
392 1
        for (Dsymbol s = this.parent; s; s = s.parent)
393 1
            packages.insert(0, s.ident);
394

395 1
        if (lookForSourceFile(getFilename(&packages, ident)))
396 1
            Module.load(Loc(), &packages, this.ident);
397
        else
398 1
            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 1
        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 0
        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 0
        if (selfimports == 0)
457
        {
458 0
            for (size_t i = 0; i < amodules.dim; i++)
459 0
                amodules[i].insearch = 0;
460 0
            selfimports = imports(this) + 1;
461 0
            for (size_t i = 0; i < amodules.dim; i++)
462 0
                amodules[i].insearch = 0;
463
        }
464 0
        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 1
        if (rootimports == 0)
476
        {
477 1
            for (size_t i = 0; i < amodules.dim; i++)
478 1
                amodules[i].insearch = 0;
479 1
            rootimports = 1;
480 1
            for (size_t i = 0; i < amodules.dim; ++i)
481
            {
482 1
                Module m = amodules[i];
483 1
                if (m.isRoot() && imports(m))
484
                {
485 1
                    rootimports = 2;
486 1
                    break;
487
                }
488
            }
489 1
            for (size_t i = 0; i < amodules.dim; i++)
490 1
                amodules[i].insearch = 0;
491
        }
492 1
        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 1
    extern (D) this(const ref Loc loc, const(char)[] filename, Identifier ident, int doDocComment, int doHdrGen)
528
    {
529 1
        super(loc, ident);
530 1
        const(char)[] srcfilename;
531
        //printf("Module::Module(filename = '%s', ident = '%s')\n", filename, ident.toChars());
532 1
        this.arg = filename;
533 1
        srcfilename = FileName.defaultExt(filename, global.mars_ext);
534 1
        if (global.run_noext && global.params.run &&
535 1
            !FileName.ext(filename) &&
536 0
            FileName.exists(srcfilename) == 0 &&
537 0
            FileName.exists(filename) == 1)
538
        {
539 0
            FileName.free(srcfilename.ptr);
540 0
            srcfilename = FileName.removeExt(filename); // just does a mem.strdup(filename)
541
        }
542 1
        else if (!FileName.equalsExt(srcfilename, global.mars_ext) &&
543 1
                 !FileName.equalsExt(srcfilename, global.hdr_ext) &&
544 1
                 !FileName.equalsExt(srcfilename, "dd"))
545
        {
546

547 0
            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 0
            fatal();
551
        }
552

553 1
        srcfile = FileName(srcfilename);
554 1
        objfile = setOutfilename(global.params.objname, global.params.objdir, filename, global.obj_ext);
555 1
        if (doDocComment)
556 1
            setDocfile();
557 1
        if (doHdrGen)
558 1
            hdrfile = setOutfilename(global.params.hdrname, global.params.hdrdir, arg, global.hdr_ext);
559 1
        escapetable = new Escape();
560
    }
561

562 1
    extern (D) this(const(char)[] filename, Identifier ident, int doDocComment, int doHdrGen)
563
    {
564 1
        this(Loc.initial, filename, ident, doDocComment, doHdrGen);
565
    }
566

567
    static Module create(const(char)* filename, Identifier ident, int doDocComment, int doHdrGen)
568
    {
569 0
        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 0
        return new Module(Loc.initial, filename, ident, doDocComment, doHdrGen);
575
    }
576

577
    static Module load(Loc loc, Identifiers* packages, Identifier ident)
578
    {
579
        //printf("Module::load(ident = '%s')\n", ident.toChars());
580
        // Build module filename by turning:
581
        //  foo.bar.baz
582
        // into:
583
        //  foo\bar\baz
584 1
        const(char)[] filename = getFilename(packages, ident);
585
        // Look for the source file
586 1
        if (const result = lookForSourceFile(filename))
587 1
            filename = result; // leaks
588

589 1
        auto m = new Module(loc, filename, ident, 0, 0);
590

591 1
        if (!m.read(loc))
592 1
            return null;
593 1
        if (global.params.verbose)
594
        {
595 1
            OutBuffer buf;
596 1
            if (packages)
597
            {
598 1
                foreach (pid; *packages)
599
                {
600 1
                    buf.writestring(pid.toString());
601 1
                    buf.writeByte('.');
602
                }
603
            }
604 1
            buf.printf("%s\t(%s)", ident.toChars(), m.srcfile.toChars());
605 1
            message("import    %s", buf.peekChars());
606
        }
607 1
        m = m.parse();
608

609
        // Call onImport here because if the module is going to be compiled then we
610
        // need to determine it early because it affects semantic analysis. This is
611
        // being done after parsing the module so the full module name can be taken
612
        // from whatever was declared in the file.
613 1
        if (!m.isRoot() && Compiler.onImport(m))
614
        {
615 1
            m.importedFrom = m;
616 1
            assert(m.isRoot());
617
        }
618 1
        return m;
619
    }
620

621
    override const(char)* kind() const
622
    {
623 1
        return "module";
624
    }
625

626
    /*********************************************
627
     * Combines things into output file name for .html and .di files.
628
     * Input:
629
     *      name    Command line name given for the file, NULL if none
630
     *      dir     Command line directory given for the file, NULL if none
631
     *      arg     Name of the source file
632
     *      ext     File name extension to use if 'name' is NULL
633
     *      global.params.preservePaths     get output path from arg
634
     *      srcfile Input file - output file name must not match input file
635
     */
636
    extern(D) FileName setOutfilename(const(char)[] name, const(char)[] dir, const(char)[] arg, const(char)[] ext)
637
    {
638 1
        const(char)[] docfilename;
639 1
        if (name)
640
        {
641 1
            docfilename = name;
642
        }
643
        else
644
        {
645 1
            const(char)[] argdoc;
646 1
            OutBuffer buf;
647 1
            if (arg == "__stdin.d")
648
            {
649
                version (Posix)
650
                    import core.sys.posix.unistd : getpid;
651
                else version (Windows)
652
                    import core.sys.windows.winbase : getpid = GetCurrentProcessId;
653 1
                buf.printf("__stdin_%d.d", getpid());
654 1
                arg = buf[];
655
            }
656 1
            if (global.params.preservePaths)
657 0
                argdoc = arg;
658
            else
659 1
                argdoc = FileName.name(arg);
660
            // If argdoc doesn't have an absolute path, make it relative to dir
661 1
            if (!FileName.absolute(argdoc))
662
            {
663
                //FileName::ensurePathExists(dir);
664 1
                argdoc = FileName.combine(dir, argdoc);
665
            }
666 1
            docfilename = FileName.forceExt(argdoc, ext);
667
        }
668 1
        if (FileName.equals(docfilename, srcfile.toString()))
669
        {
670 0
            error("source file and output file have same name '%s'", srcfile.toChars());
671 0
            fatal();
672
        }
673 1
        return FileName(docfilename);
674
    }
675

676
    extern (D) void setDocfile()
677
    {
678 1
        docfile = setOutfilename(global.params.docname, global.params.docdir, arg, global.doc_ext);
679
    }
680

681
    /**
682
     * Loads the source buffer from the given read result into `this.srcBuffer`.
683
     *
684
     * Will take ownership of the buffer located inside `readResult`.
685
     *
686
     * Params:
687
     *  loc = the location
688
     *  readResult = the result of reading a file containing the source code
689
     *
690
     * Returns: `true` if successful
691
     */
692
    bool loadSourceBuffer(const ref Loc loc, ref File.ReadResult readResult)
693
    {
694
        //printf("Module::loadSourceBuffer('%s') file '%s'\n", toChars(), srcfile.toChars());
695
        // take ownership of buffer
696 1
        srcBuffer = new FileBuffer(readResult.extractSlice());
697 1
        if (readResult.success)
698 1
            return true;
699

700 1
        if (FileName.equals(srcfile.toString(), "object.d"))
701
        {
702 1
            .error(loc, "cannot find source code for runtime library file 'object.d'");
703 1
            errorSupplemental(loc, "dmd might not be correctly installed. Run 'dmd -man' for installation instructions.");
704 1
            const dmdConfFile = global.inifilename.length ? FileName.canonicalName(global.inifilename) : "not found";
705 1
            errorSupplemental(loc, "config file: %.*s", cast(int)dmdConfFile.length, dmdConfFile.ptr);
706
        }
707
        else
708
        {
709
            // if module is not named 'package' but we're trying to read 'package.d', we're looking for a package module
710 1
            bool isPackageMod = (strcmp(toChars(), "package") != 0) && (strcmp(srcfile.name(), "package.d") == 0 || (strcmp(srcfile.name(), "package.di") == 0));
711 1
            if (isPackageMod)
712 0
                .error(loc, "importing package '%s' requires a 'package.d' file which cannot be found in '%s'", toChars(), srcfile.toChars());
713
            else
714 1
                error(loc, "is in file '%s' which cannot be read", srcfile.toChars());
715
        }
716 1
        if (!global.gag)
717
        {
718
            /* Print path
719
             */
720 1
            if (global.path)
721
            {
722 1
                foreach (i, p; *global.path)
723 1
                    fprintf(stderr, "import path[%llu] = %s\n", cast(ulong)i, p);
724
            }
725
            else
726
            {
727 1
                fprintf(stderr, "Specify path to file '%s' with -I switch\n", srcfile.toChars());
728
            }
729

730 1
            removeHdrFilesAndFail(global.params, Module.amodules);
731
        }
732 1
        return false;
733
    }
734

735
    /**
736
     * Reads the file from `srcfile` and loads the source buffer.
737
     *
738
     * Params:
739
     *  loc = the location
740
     *
741
     * Returns: `true` if successful
742
     * See_Also: loadSourceBuffer
743
     */
744
    bool read(const ref Loc loc)
745
    {
746 1
        if (srcBuffer)
747 1
            return true; // already read
748

749
        //printf("Module::read('%s') file '%s'\n", toChars(), srcfile.toChars());
750 1
        auto readResult = File.read(srcfile.toChars());
751

752 1
        return loadSourceBuffer(loc, readResult);
753
    }
754

755
    /// syntactic parse
756
    Module parse()
757
    {
758 1
        return parseModule!ASTCodegen();
759
    }
760

761
    /// ditto
762
    extern (D) Module parseModule(AST)()
763
    {
764

765

766
        enum Endian { little, big}
767
        enum SourceEncoding { utf16, utf32}
768

769
        /*
770
         * Convert a buffer from UTF32 to UTF8
771
         * Params:
772
         *    Endian = is the buffer big/little endian
773
         *    buf = buffer of UTF32 data
774
         * Returns:
775
         *    input buffer reencoded as UTF8
776
         */
777

778
        char[] UTF32ToUTF8(Endian endian)(const(char)[] buf)
779
        {
780
            static if (endian == Endian.little)
781
                alias readNext = Port.readlongLE;
782
            else
783
                alias readNext = Port.readlongBE;
784

785 1
            if (buf.length & 3)
786
            {
787 0
                error("odd length of UTF-32 char source %llu", cast(ulong) buf.length);
788 0
                fatal();
789
            }
790

791 1
            const (uint)[] eBuf = cast(const(uint)[])buf;
792

793 1
            OutBuffer dbuf;
794 1
            dbuf.reserve(eBuf.length);
795

796 1
            foreach (i; 0 .. eBuf.length)
797
            {
798 1
                const u = readNext(&eBuf[i]);
799 1
                if (u & ~0x7F)
800
                {
801 1
                    if (u > 0x10FFFF)
802
                    {
803 0
                        error("UTF-32 value %08x greater than 0x10FFFF", u);
804 0
                        fatal();
805
                    }
806 1
                    dbuf.writeUTF8(u);
807
                }
808
                else
809 1
                    dbuf.writeByte(u);
810
            }
811 1
            dbuf.writeByte(0); //add null terminator
812 1
            return dbuf.extractSlice();
813
        }
814

815
        /*
816
         * Convert a buffer from UTF16 to UTF8
817
         * Params:
818
         *    Endian = is the buffer big/little endian
819
         *    buf = buffer of UTF16 data
820
         * Returns:
821
         *    input buffer reencoded as UTF8
822
         */
823

824
        char[] UTF16ToUTF8(Endian endian)(const(char)[] buf)
825
        {
826
            static if (endian == Endian.little)
827
                alias readNext = Port.readwordLE;
828
            else
829
                alias readNext = Port.readwordBE;
830

831 1
            if (buf.length & 1)
832
            {
833 0
                error("odd length of UTF-16 char source %llu", cast(ulong) buf.length);
834 0
                fatal();
835
            }
836

837 1
            const (ushort)[] eBuf = cast(const(ushort)[])buf;
838

839 1
            OutBuffer dbuf;
840 1
            dbuf.reserve(eBuf.length);
841

842
            //i will be incremented in the loop for high codepoints
843 1
            foreach (ref i; 0 .. eBuf.length)
844
            {
845 1
                uint u = readNext(&eBuf[i]);
846 1
                if (u & ~0x7F)
847
                {
848 0
                    if (0xD800 <= u && u < 0xDC00)
849
                    {
850 0
                        i++;
851 0
                        if (i >= eBuf.length)
852
                        {
853 0
                            error("surrogate UTF-16 high value %04x at end of file", u);
854 0
                            fatal();
855
                        }
856 0
                        const u2 = readNext(&eBuf[i]);
857 0
                        if (u2 < 0xDC00 || 0xE000 <= u2)
858
                        {
859 0
                            error("surrogate UTF-16 low value %04x out of range", u2);
860 0
                            fatal();
861
                        }
862 0
                        u = (u - 0xD7C0) << 10;
863 0
                        u |= (u2 - 0xDC00);
864
                    }
865 0
                    else if (u >= 0xDC00 && u <= 0xDFFF)
866
                    {
867 0
                        error("unpaired surrogate UTF-16 value %04x", u);
868 0
                        fatal();
869
                    }
870 0
                    else if (u == 0xFFFE || u == 0xFFFF)
871
                    {
872 0
                        error("illegal UTF-16 value %04x", u);
873 0
                        fatal();
874
                    }
875 0
                    dbuf.writeUTF8(u);
876
                }
877
                else
878 1
                    dbuf.writeByte(u);
879
            }
880 1
            dbuf.writeByte(0); //add a terminating null byte
881 1
            return dbuf.extractSlice();
882
        }
883

884 1
        const(char)* srcname = srcfile.toChars();
885
        //printf("Module::parse(srcname = '%s')\n", srcname);
886 1
        isPackageFile = (strcmp(srcfile.name(), "package.d") == 0 ||
887 1
                         strcmp(srcfile.name(), "package.di") == 0);
888 1
        const(char)[] buf = cast(const(char)[]) srcBuffer.data;
889

890 1
        bool needsReencoding = true;
891 1
        bool hasBOM = true; //assume there's a BOM
892 1
        Endian endian;
893 1
        SourceEncoding sourceEncoding;
894

895 1
        if (buf.length >= 2)
896
        {
897
            /* Convert all non-UTF-8 formats to UTF-8.
898
             * BOM : http://www.unicode.org/faq/utf_bom.html
899
             * 00 00 FE FF  UTF-32BE, big-endian
900
             * FF FE 00 00  UTF-32LE, little-endian
901
             * FE FF        UTF-16BE, big-endian
902
             * FF FE        UTF-16LE, little-endian
903
             * EF BB BF     UTF-8
904
             */
905 1
            if (buf[0] == 0xFF && buf[1] == 0xFE)
906
            {
907 1
                endian = Endian.little;
908

909 1
                sourceEncoding = buf.length >= 4 && buf[2] == 0 && buf[3] == 0
910 1
                                 ? SourceEncoding.utf32
911 1
                                 : SourceEncoding.utf16;
912
            }
913 1
            else if (buf[0] == 0xFE && buf[1] == 0xFF)
914
            {
915 1
                endian = Endian.big;
916 1
                sourceEncoding = SourceEncoding.utf16;
917
            }
918 1
            else if (buf.length >= 4 && buf[0] == 0 && buf[1] == 0 && buf[2] == 0xFE && buf[3] == 0xFF)
919
            {
920 0
                endian = Endian.big;
921 0
                sourceEncoding = SourceEncoding.utf32;
922
            }
923 1
            else if (buf.length >= 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF)
924
            {
925 1
                needsReencoding = false;//utf8 with BOM
926
            }
927
            else
928
            {
929
                /* There is no BOM. Make use of Arcane Jill's insight that
930
                 * the first char of D source must be ASCII to
931
                 * figure out the encoding.
932
                 */
933 1
                hasBOM = false;
934 1
                if (buf.length >= 4 && buf[1] == 0 && buf[2] == 0 && buf[3] == 0)
935
                {
936 0
                    endian = Endian.little;
937 0
                    sourceEncoding = SourceEncoding.utf32;
938
                }
939 1
                else if (buf.length >= 4 && buf[0] == 0 && buf[1] == 0 && buf[2] == 0)
940
                {
941 0
                    endian = Endian.big;
942 0
                    sourceEncoding = SourceEncoding.utf32;
943
                }
944 1
                else if (buf.length >= 2 && buf[1] == 0) //try to check for UTF-16
945
                {
946 0
                    endian = Endian.little;
947 0
                    sourceEncoding = SourceEncoding.utf16;
948
                }
949 1
                else if (buf[0] == 0)
950
                {
951 0
                    endian = Endian.big;
952 0
                    sourceEncoding = SourceEncoding.utf16;
953
                }
954
                else {
955
                    // It's UTF-8
956 1
                    needsReencoding = false;
957 1
                    if (buf[0] >= 0x80)
958
                    {
959 1
                        error("source file must start with BOM or ASCII character, not \\x%02X", buf[0]);
960 1
                        fatal();
961
                    }
962
                }
963
            }
964
            //throw away BOM
965 1
            if (hasBOM)
966
            {
967 1
                if (!needsReencoding) buf = buf[3..$];// utf-8 already
968 1
                else if (sourceEncoding == SourceEncoding.utf32) buf = buf[4..$];
969 1
                else buf = buf[2..$]; //utf 16
970
            }
971
        }
972
        // Assume the buffer is from memory and has not be read from disk. Assume UTF-8.
973 1
        else if (buf.length >= 1 && (buf[0] == '\0' || buf[0] == 0x1A))
974 0
            needsReencoding = false;
975
         //printf("%s, %d, %d, %d\n", srcfile.name.toChars(), needsReencoding, endian == Endian.little, sourceEncoding == SourceEncoding.utf16);
976 1
        if (needsReencoding)
977
        {
978 1
            if (sourceEncoding == SourceEncoding.utf16)
979
            {
980 1
                buf = endian == Endian.little
981 1
                      ? UTF16ToUTF8!(Endian.little)(buf)
982 1
                      : UTF16ToUTF8!(Endian.big)(buf);
983
            }
984
            else
985
            {
986 1
                buf = endian == Endian.little
987 1
                      ? UTF32ToUTF8!(Endian.little)(buf)
988 0
                      : UTF32ToUTF8!(Endian.big)(buf);
989
            }
990
        }
991

992
        /* If it starts with the string "Ddoc", then it's a documentation
993
         * source file.
994
         */
995 1
        if (buf.length>= 4 && buf[0..4] == "Ddoc")
996
        {
997 1
            comment = buf.ptr + 4;
998 1
            isDocFile = true;
999 1
            if (!docfile)
1000 0
                setDocfile();
1001 1
            return this;
1002
        }
1003
        /* If it has the extension ".dd", it is also a documentation
1004
         * source file. Documentation source files may begin with "Ddoc"
1005
         * but do not have to if they have the .dd extension.
1006
         * https://issues.dlang.org/show_bug.cgi?id=15465
1007
         */
1008 1
        if (FileName.equalsExt(arg, "dd"))
1009
        {
1010 0
            comment = buf.ptr; // the optional Ddoc, if present, is handled above.
1011 0
            isDocFile = true;
1012 0
            if (!docfile)
1013 0
                setDocfile();
1014 0
            return this;
1015
        }
1016
        /* If it has the extension ".di", it is a "header" file.
1017
         */
1018 1
        if (FileName.equalsExt(arg, "di"))
1019
        {
1020 1
            isHdrFile = true;
1021
        }
1022
        {
1023 1
            scope p = new Parser!AST(this, buf, cast(bool) docfile);
1024 1
            p.nextToken();
1025 1
            members = p.parseModule();
1026 1
            md = p.md;
1027 1
            numlines = p.scanloc.linnum;
1028
        }
1029 1
        srcBuffer.destroy();
1030 1
        srcBuffer = null;
1031
        /* The symbol table into which the module is to be inserted.
1032
         */
1033 1
        DsymbolTable dst;
1034 1
        if (md)
1035
        {
1036
            /* A ModuleDeclaration, md, was provided.
1037
             * The ModuleDeclaration sets the packages this module appears in, and
1038
             * the name of this module.
1039
             */
1040 1
            this.ident = md.id;
1041 1
            Package ppack = null;
1042 1
            dst = Package.resolve(md.packages, &this.parent, &ppack);
1043 1
            assert(dst);
1044 1
            Module m = ppack ? ppack.isModule() : null;
1045 1
            if (m && (strcmp(m.srcfile.name(), "package.d") != 0 &&
1046 1
                      strcmp(m.srcfile.name(), "package.di") != 0))
1047
            {
1048 1
                .error(md.loc, "package name '%s' conflicts with usage as a module name in file %s", ppack.toPrettyChars(), m.srcfile.toChars());
1049
            }
1050
        }
1051
        else
1052
        {
1053
            /* The name of the module is set to the source file name.
1054
             * There are no packages.
1055
             */
1056 1
            dst = modules; // and so this module goes into global module symbol table
1057
            /* Check to see if module name is a valid identifier
1058
             */
1059 1
            if (!Identifier.isValidIdentifier(this.ident.toChars()))
1060 0
                error("has non-identifier characters in filename, use module declaration instead");
1061
        }
1062
        // Insert module into the symbol table
1063 1
        Dsymbol s = this;
1064 1
        if (isPackageFile)
1065
        {
1066
            /* If the source tree is as follows:
1067
             *     pkg/
1068
             *     +- package.d
1069
             *     +- common.d
1070
             * the 'pkg' will be incorporated to the internal package tree in two ways:
1071
             *     import pkg;
1072
             * and:
1073
             *     import pkg.common;
1074
             *
1075
             * If both are used in one compilation, 'pkg' as a module (== pkg/package.d)
1076
             * and a package name 'pkg' will conflict each other.
1077
             *
1078
             * To avoid the conflict:
1079
             * 1. If preceding package name insertion had occurred by Package::resolve,
1080
             *    reuse the previous wrapping 'Package' if it exists
1081
             * 2. Otherwise, 'package.d' wrapped by 'Package' is inserted to the internal tree in here.
1082
             *
1083
             * Then change Package::isPkgMod to PKG.module_ and set Package::mod.
1084
             *
1085
             * Note that the 'wrapping Package' is the Package that contains package.d and other submodules,
1086
             * the one inserted to the symbol table.
1087
             */
1088 1
            auto ps = dst.lookup(ident);
1089 1
            Package p = ps ? ps.isPackage() : null;
1090 1
            if (p is null)
1091
            {
1092 1
                p = new Package(Loc.initial, ident);
1093 1
                p.tag = this.tag; // reuse the same package tag
1094 1
                p.symtab = new DsymbolTable();
1095
            }
1096 1
            this.tag = p.tag; // reuse the 'older' package tag
1097 1
            this.pkg = p;
1098 1
            p.parent = this.parent;
1099 1
            p.isPkgMod = PKG.module_;
1100 1
            p.mod = this;
1101 1
            s = p;
1102
        }
1103 1
        if (!dst.insert(s))
1104
        {
1105
            /* It conflicts with a name that is already in the symbol table.
1106
             * Figure out what went wrong, and issue error message.
1107
             */
1108 1
            Dsymbol prev = dst.lookup(ident);
1109 1
            assert(prev);
1110 1
            if (Module mprev = prev.isModule())
1111
            {
1112 1
                if (!FileName.equals(srcname, mprev.srcfile.toChars()))
1113 0
                    error(loc, "from file %s conflicts with another module %s from file %s", srcname, mprev.toChars(), mprev.srcfile.toChars());
1114 1
                else if (isRoot() && mprev.isRoot())
1115 0
                    error(loc, "from file %s is specified twice on the command line", srcname);
1116
                else
1117 1
                    error(loc, "from file %s must be imported with 'import %s;'", srcname, toPrettyChars());
1118
                // https://issues.dlang.org/show_bug.cgi?id=14446
1119
                // Return previously parsed module to avoid AST duplication ICE.
1120 1
                return mprev;
1121
            }
1122 1
            else if (Package pkg = prev.isPackage())
1123
            {
1124
                // 'package.d' loaded after a previous 'Package' insertion
1125 1
                if (isPackageFile)
1126 1
                    amodules.push(this); // Add to global array of all modules
1127
                else
1128 1
                    error(md ? md.loc : loc, "from file %s conflicts with package name %s", srcname, pkg.toChars());
1129
            }
1130
            else
1131 1
                assert(global.errors);
1132
        }
1133
        else
1134
        {
1135
            // Add to global array of all modules
1136 1
            amodules.push(this);
1137
        }
1138 1
        Compiler.onParseModule(this);
1139 1
        return this;
1140
    }
1141

1142
    override void importAll(Scope* prevsc)
1143
    {
1144
        //printf("+Module::importAll(this = %p, '%s'): parent = %p\n", this, toChars(), parent);
1145 1
        if (_scope)
1146 1
            return; // already done
1147 1
        if (isDocFile)
1148
        {
1149 0
            error("is a Ddoc file, cannot import it");
1150 0
            return;
1151
        }
1152

1153
        /* Note that modules get their own scope, from scratch.
1154
         * This is so regardless of where in the syntax a module
1155
         * gets imported, it is unaffected by context.
1156
         * Ignore prevsc.
1157
         */
1158 1
        Scope* sc = Scope.createGlobal(this); // create root scope
1159

1160 1
        if (md && md.msg)
1161 1
            md.msg = semanticString(sc, md.msg, "deprecation message");
1162

1163
        // Add import of "object", even for the "object" module.
1164
        // If it isn't there, some compiler rewrites, like
1165
        //    classinst == classinst -> .object.opEquals(classinst, classinst)
1166
        // would fail inside object.d.
1167 1
        if (members.dim == 0 || (*members)[0].ident != Id.object ||
1168 1
            (*members)[0].isImport() is null)
1169
        {
1170 1
            auto im = new Import(Loc.initial, null, Id.object, null, 0);
1171 1
            members.shift(im);
1172
        }
1173 1
        if (!symtab)
1174
        {
1175
            // Add all symbols into module's symbol table
1176 1
            symtab = new DsymbolTable();
1177 1
            for (size_t i = 0; i < members.dim; i++)
1178
            {
1179 1
                Dsymbol s = (*members)[i];
1180 1
                s.addMember(sc, sc.scopesym);
1181
            }
1182
        }
1183
        // anything else should be run after addMember, so version/debug symbols are defined
1184
        /* Set scope for the symbols so that if we forward reference
1185
         * a symbol, it can possibly be resolved on the spot.
1186
         * If this works out well, it can be extended to all modules
1187
         * before any semantic() on any of them.
1188
         */
1189 1
        setScope(sc); // remember module scope for semantic
1190 1
        for (size_t i = 0; i < members.dim; i++)
1191
        {
1192 1
            Dsymbol s = (*members)[i];
1193 1
            s.setScope(sc);
1194
        }
1195 1
        for (size_t i = 0; i < members.dim; i++)
1196
        {
1197 1
            Dsymbol s = (*members)[i];
1198 1
            s.importAll(sc);
1199
        }
1200 1
        sc = sc.pop();
1201 1
        sc.pop(); // 2 pops because Scope::createGlobal() created 2
1202
    }
1203

1204
    /**********************************
1205
     * Determine if we need to generate an instance of ModuleInfo
1206
     * for this Module.
1207
     */
1208
    int needModuleInfo()
1209
    {
1210
        //printf("needModuleInfo() %s, %d, %d\n", toChars(), needmoduleinfo, global.params.cov);
1211 0
        return needmoduleinfo || global.params.cov;
1212
    }
1213

1214
    /*******************************************
1215
     * Print deprecation warning if we're deprecated, when
1216
     * this module is imported from scope sc.
1217
     *
1218
     * Params:
1219
     *  sc = the scope into which we are imported
1220
     *  loc = the location of the import statement
1221
     */
1222
    void checkImportDeprecation(const ref Loc loc, Scope* sc)
1223
    {
1224 1
        if (md && md.isdeprecated && !sc.isDeprecated)
1225
        {
1226 1
            Expression msg = md.msg;
1227 1
            if (StringExp se = msg ? msg.toStringExp() : null)
1228
            {
1229 1
                const slice = se.peekString();
1230 1
                deprecation(loc, "is deprecated - %.*s", cast(int)slice.length, slice.ptr);
1231
            }
1232
            else
1233 1
                deprecation(loc, "is deprecated");
1234
        }
1235
    }
1236

1237
    override Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly)
1238
    {
1239
        /* Since modules can be circularly referenced,
1240
         * need to stop infinite recursive searches.
1241
         * This is done with the cache.
1242
         */
1243
        //printf("%s Module.search('%s', flags = x%x) insearch = %d\n", toChars(), ident.toChars(), flags, insearch);
1244 1
        if (insearch)
1245 1
            return null;
1246

1247
        /* Qualified module searches always search their imports,
1248
         * even if SearchLocalsOnly
1249
         */
1250 1
        if (!(flags & SearchUnqualifiedModule))
1251 1
            flags &= ~(SearchUnqualifiedModule | SearchLocalsOnly);
1252

1253 1
        if (searchCacheIdent == ident && searchCacheFlags == flags)
1254
        {
1255
            //printf("%s Module::search('%s', flags = %d) insearch = %d searchCacheSymbol = %s\n",
1256
            //        toChars(), ident.toChars(), flags, insearch, searchCacheSymbol ? searchCacheSymbol.toChars() : "null");
1257 1
            return searchCacheSymbol;
1258
        }
1259

1260 1
        uint errors = global.errors;
1261

1262 1
        insearch = 1;
1263 1
        Dsymbol s = ScopeDsymbol.search(loc, ident, flags);
1264 1
        insearch = 0;
1265

1266 1
        if (errors == global.errors)
1267
        {
1268
            // https://issues.dlang.org/show_bug.cgi?id=10752
1269
            // Can cache the result only when it does not cause
1270
            // access error so the side-effect should be reproduced in later search.
1271 1
            searchCacheIdent = ident;
1272 1
            searchCacheSymbol = s;
1273 1
            searchCacheFlags = flags;
1274
        }
1275 1
        return s;
1276
    }
1277

1278
    override bool isPackageAccessible(Package p, Prot protection, int flags = 0)
1279
    {
1280 1
        if (insearch) // don't follow import cycles
1281 0
            return false;
1282 1
        insearch = true;
1283
        scope (exit)
1284 1
            insearch = false;
1285 1
        if (flags & IgnorePrivateImports)
1286 1
            protection = Prot(Prot.Kind.public_); // only consider public imports
1287 1
        return super.isPackageAccessible(p, protection);
1288
    }
1289

1290
    override Dsymbol symtabInsert(Dsymbol s)
1291
    {
1292 1
        searchCacheIdent = null; // symbol is inserted, so invalidate cache
1293 1
        return Package.symtabInsert(s);
1294
    }
1295

1296
    void deleteObjFile()
1297
    {
1298 1
        if (global.params.obj)
1299 1
            File.remove(objfile.toChars());
1300 1
        if (docfile)
1301 1
            File.remove(docfile.toChars());
1302
    }
1303

1304
    /*******************************************
1305
     * Can't run semantic on s now, try again later.
1306
     */
1307
    extern (D) static void addDeferredSemantic(Dsymbol s)
1308
    {
1309
        //printf("Module::addDeferredSemantic('%s')\n", s.toChars());
1310 1
        deferred.push(s);
1311
    }
1312

1313
    extern (D) static void addDeferredSemantic2(Dsymbol s)
1314
    {
1315
        //printf("Module::addDeferredSemantic2('%s')\n", s.toChars());
1316 1
        deferred2.push(s);
1317
    }
1318

1319
    extern (D) static void addDeferredSemantic3(Dsymbol s)
1320
    {
1321
        //printf("Module::addDeferredSemantic3('%s')\n", s.toChars());
1322 1
        deferred3.push(s);
1323
    }
1324

1325
    /******************************************
1326
     * Run semantic() on deferred symbols.
1327
     */
1328
    static void runDeferredSemantic()
1329
    {
1330 1
        if (dprogress == 0)
1331 1
            return;
1332

1333 1
        __gshared int nested;
1334 1
        if (nested)
1335 1
            return;
1336
        //if (deferred.dim) printf("+Module::runDeferredSemantic(), len = %d\n", deferred.dim);
1337 1
        nested++;
1338

1339 1
        size_t len;
1340
        do
1341
        {
1342 1
            dprogress = 0;
1343 1
            len = deferred.dim;
1344 1
            if (!len)
1345 1
                break;
1346

1347 1
            Dsymbol* todo;
1348 1
            Dsymbol* todoalloc = null;
1349 1
            Dsymbol tmp;
1350 1
            if (len == 1)
1351
            {
1352 1
                todo = &tmp;
1353
            }
1354
            else
1355
            {
1356 1
                todo = cast(Dsymbol*)Mem.check(malloc(len * Dsymbol.sizeof));
1357 1
                todoalloc = todo;
1358
            }
1359 1
            memcpy(todo, deferred.tdata(), len * Dsymbol.sizeof);
1360 1
            deferred.setDim(0);
1361

1362 1
            for (size_t i = 0; i < len; i++)
1363
            {
1364 1
                Dsymbol s = todo[i];
1365 1
                s.dsymbolSemantic(null);
1366
                //printf("deferred: %s, parent = %s\n", s.toChars(), s.parent.toChars());
1367
            }
1368
            //printf("\tdeferred.dim = %d, len = %d, dprogress = %d\n", deferred.dim, len, dprogress);
1369 1
            if (todoalloc)
1370 1
                free(todoalloc);
1371
        }
1372 1
        while (deferred.dim < len || dprogress); // while making progress
1373 1
        nested--;
1374
        //printf("-Module::runDeferredSemantic(), len = %d\n", deferred.dim);
1375
    }
1376

1377
    static void runDeferredSemantic2()
1378
    {
1379 1
        Module.runDeferredSemantic();
1380

1381 1
        Dsymbols* a = &Module.deferred2;
1382 1
        for (size_t i = 0; i < a.dim; i++)
1383
        {
1384 1
            Dsymbol s = (*a)[i];
1385
            //printf("[%d] %s semantic2a\n", i, s.toPrettyChars());
1386 1
            s.semantic2(null);
1387

1388 1
            if (global.errors)
1389 1
                break;
1390
        }
1391 1
        a.setDim(0);
1392
    }
1393

1394
    static void runDeferredSemantic3()
1395
    {
1396 1
        Module.runDeferredSemantic2();
1397

1398 1
        Dsymbols* a = &Module.deferred3;
1399 1
        for (size_t i = 0; i < a.dim; i++)
1400
        {
1401 1
            Dsymbol s = (*a)[i];
1402
            //printf("[%d] %s semantic3a\n", i, s.toPrettyChars());
1403 1
            s.semantic3(null);
1404

1405 1
            if (global.errors)
1406 1
                break;
1407
        }
1408 1
        a.setDim(0);
1409
    }
1410

1411
    extern (D) static void clearCache()
1412
    {
1413 1
        for (size_t i = 0; i < amodules.dim; i++)
1414
        {
1415 1
            Module m = amodules[i];
1416 1
            m.searchCacheIdent = null;
1417
        }
1418
    }
1419

1420
    /************************************
1421
     * Recursively look at every module this module imports,
1422
     * return true if it imports m.
1423
     * Can be used to detect circular imports.
1424
     */
1425
    int imports(Module m)
1426
    {
1427
        //printf("%s Module::imports(%s)\n", toChars(), m.toChars());
1428
        version (none)
1429
        {
1430
            for (size_t i = 0; i < aimports.dim; i++)
1431
            {
1432
                Module mi = cast(Module)aimports.data[i];
1433
                printf("\t[%d] %s\n", i, mi.toChars());
1434
            }
1435
        }
1436 1
        for (size_t i = 0; i < aimports.dim; i++)
1437
        {
1438 1
            Module mi = aimports[i];
1439 1
            if (mi == m)
1440 1
                return true;
1441 1
            if (!mi.insearch)
1442
            {
1443 1
                mi.insearch = 1;
1444 1
                int r = mi.imports(m);
1445 1
                if (r)
1446 1
                    return r;
1447
            }
1448
        }
1449 1
        return false;
1450
    }
1451

1452
    bool isRoot()
1453
    {
1454 1
        return this.importedFrom == this;
1455
    }
1456

1457
    // true if the module source file is directly
1458
    // listed in command line.
1459
    bool isCoreModule(Identifier ident)
1460
    {
1461 1
        return this.ident == ident && parent && parent.ident == Id.core && !parent.parent;
1462
    }
1463

1464
    // Back end
1465
    int doppelganger; // sub-module
1466
    Symbol* cov; // private uint[] __coverage;
1467
    uint* covb; // bit array of valid code line numbers
1468
    Symbol* sictor; // module order independent constructor
1469
    Symbol* sctor; // module constructor
1470
    Symbol* sdtor; // module destructor
1471
    Symbol* ssharedctor; // module shared constructor
1472
    Symbol* sshareddtor; // module shared destructor
1473
    Symbol* stest; // module unit test
1474
    Symbol* sfilename; // symbol for filename
1475

1476
    uint[uint] ctfe_cov; /// coverage information from ctfe execution_count[line]
1477

1478
    override inout(Module) isModule() inout
1479
    {
1480 1
        return this;
1481
    }
1482

1483
    override void accept(Visitor v)
1484
    {
1485 1
        v.visit(this);
1486
    }
1487

1488
    /***********************************************
1489
     * Writes this module's fully-qualified name to buf
1490
     * Params:
1491
     *    buf = The buffer to write to
1492
     */
1493
    void fullyQualifiedName(ref OutBuffer buf)
1494
    {
1495 0
        buf.writestring(ident.toString());
1496

1497 0
        for (auto package_ = parent; package_ !is null; package_ = package_.parent)
1498
        {
1499 0
            buf.prependstring(".");
1500 0
            buf.prependstring(package_.ident.toChars());
1501
        }
1502
    }
1503
}
1504

1505
/***********************************************************
1506
 */
1507
extern (C++) struct ModuleDeclaration
1508
{
1509
    Loc loc;
1510
    Identifier id;
1511
    Identifiers* packages;  // array of Identifier's representing packages
1512
    bool isdeprecated;      // if it is a deprecated module
1513
    Expression msg;
1514

1515 1
    extern (D) this(const ref Loc loc, Identifiers* packages, Identifier id, Expression msg, bool isdeprecated)
1516
    {
1517 1
        this.loc = loc;
1518 1
        this.packages = packages;
1519 1
        this.id = id;
1520 1
        this.msg = msg;
1521 1
        this.isdeprecated = isdeprecated;
1522
    }
1523

1524
    extern (C++) const(char)* toChars() const
1525
    {
1526 1
        OutBuffer buf;
1527 1
        if (packages && packages.dim)
1528
        {
1529 1
            foreach (pid; *packages)
1530
            {
1531 1
                buf.writestring(pid.toString());
1532 1
                buf.writeByte('.');
1533
            }
1534
        }
1535 1
        buf.writestring(id.toString());
1536 1
        return buf.extractChars();
1537
    }
1538

1539
    /// Provide a human readable representation
1540
    extern (D) const(char)[] toString() const
1541
    {
1542 1
        return this.toChars().toDString;
1543
    }
1544
}

Read our documentation on viewing source code .

Loading