2 * Defines a package and module.
4 * Specification: $(LINK2 https://dlang.org/spec/module.html, Modules)
6 * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved
7 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
8 * License: $(LINK2 https://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
16 import core.stdc.stdio;
17 import core.stdc.stdlib;
18 import core.stdc.string;
21 import dmd.arraytypes;
22 import dmd.astcodegen;
24 import dmd.common.outbuffer;
32 import dmd.dsymbolsem;
35 import dmd.expression;
36 import dmd.expressionsem;
37 import dmd.file_manager;
42 import dmd.identifier;
45 import dmd.root.array;
47 import dmd.root.filename;
50 import dmd.root.string;
51 import dmd.rootobject;
60 import core.sys.windows.winbase : getpid = GetCurrentProcessId;
61 enum PathSeparator = '\\';
65 import core.sys.posix.unistd : getpid;
66 enum PathSeparator = '/';
72 else version (IN_LLVM) {}
75 // function used to call semantic3 on a module's dependencies
76 void semantic3OnDependencies(Module m)
81 if (m.semanticRun > PASS.semantic3)
86 foreach (i; 1 .. m.aimports.length)
87 semantic3OnDependencies(m.aimports[i]);
91 * Remove generated .di files on error and exit
93 void removeHdrFilesAndFail(ref Param params, ref Modules modules) nothrow
95 if (params.dihdr.doOutput)
99 if (m.filetype == FileType.dhdr)
101 File.remove(m.hdrfile.toChars());
109 * Converts a chain of identifiers to the filename of the module
112 * packages = the names of the "parent" packages
113 * ident = the name of the child package or module
116 * the filename of the child package or module
118 private const(char)[] getFilename(Identifier[] packages, Identifier ident) nothrow
120 const(char)[] filename = ident.toString();
124 auto modAliases = &global.params.modFileAliasStrings;
126 if (packages.length == 0 && modAliases.length == 0)
129 void checkModFileAlias(const(char)[] p)
131 /* Check and replace the contents of buf[] with
132 * an alias string from global.params.modFileAliasStrings[]
134 dotmods.writestring(p);
135 foreach_reverse (const m; *modAliases)
137 const q = strchr(m, '=');
139 if (dotmods.length == q - m && memcmp(dotmods.peekChars(), m, q - m) == 0)
142 auto rhs = q[1 .. strlen(q)];
143 if (rhs.length > 0 && (rhs[$ - 1] == '/' || rhs[$ - 1] == '\\'))
144 rhs = rhs[0 .. $ - 1]; // remove trailing separator
145 buf.writestring(rhs);
146 break; // last matching entry in ms[] wins
149 dotmods.writeByte('.');
152 foreach (pid; packages)
154 const p = pid.toString();
156 if (modAliases.length)
157 checkModFileAlias(p);
158 buf.writeByte(PathSeparator);
160 buf.writestring(filename);
161 if (modAliases.length)
162 checkModFileAlias(filename);
164 filename = buf.extractSlice()[0 .. $ - 1];
169 /***********************************************************
171 extern (C++) class Package : ScopeDsymbol
173 PKG isPkgMod = PKG.unknown;
174 uint tag; // auto incremented tag, used to mask package tree in scopes
175 Module mod; // !=null if isPkgMod == PKG.module_
177 final extern (D) this(const ref Loc loc, Identifier ident) nothrow
180 __gshared uint packageTag;
181 this.tag = packageTag++;
184 override const(char)* kind() const nothrow
189 override bool equals(const RootObject o) const
191 // custom 'equals' for bug 17441. "package a" and "module a" are not equal
194 auto p = cast(Package)o;
195 return p && isModule() == p.isModule() && ident.equals(p.ident);
198 /****************************************************
200 * packages[] the pkg1.pkg2 of pkg1.pkg2.mod
202 * the symbol table that mod should be inserted into
204 * *pparent the rightmost package, i.e. pkg2, or NULL if no packages
205 * *ppkg the leftmost package, i.e. pkg1, or NULL if no packages
207 extern (D) static DsymbolTable resolve(Identifier[] packages, Dsymbol* pparent, Package* ppkg)
209 DsymbolTable dst = Module.modules;
210 Dsymbol parent = null;
211 //printf("Package::resolve()\n");
214 foreach (pid; packages)
217 Dsymbol p = dst.lookup(pid);
220 pkg = new Package(Loc.initial, pid);
223 pkg.symtab = new DsymbolTable();
229 // It might already be a module, not a package, but that needs
230 // to be checked at a higher level, where a nice error message
232 // dot net needs modules and packages with same name
233 // But we still need a symbol table for it
235 pkg.symtab = new DsymbolTable();
243 // Return the module so that a nice error message can be generated
245 *ppkg = cast(Package)p;
255 override final inout(Package) isPackage() inout
261 * Checks if pkg is a sub-package of this
263 * For example, if this qualifies to 'a1.a2' and pkg - to 'a1.a2.a3',
264 * this function returns 'true'. If it is other way around or qualified
265 * package paths conflict function returns 'false'.
268 * pkg = possible subpackage
273 final bool isAncestorPackageOf(const Package pkg) const
277 if (!pkg || !pkg.parent)
279 return isAncestorPackageOf(pkg.parent.isPackage());
282 override void accept(Visitor v)
287 final Module isPackageMod()
289 if (isPkgMod == PKG.module_)
297 * Checks for the existence of a package.d to set isPkgMod appropriately
298 * if isPkgMod == PKG.unknown
300 final void resolvePKGunknown()
304 if (isPkgMod != PKG.unknown)
307 Identifier[] packages;
308 for (Dsymbol s = this.parent; s; s = s.parent)
312 if (Module.find(getFilename(packages, ident)))
313 Module.load(Loc.initial, packages, this.ident);
315 isPkgMod = PKG.package_;
319 /***********************************************************
321 extern (C++) final class Module : Package
323 extern (C++) __gshared Module rootModule;
324 extern (C++) __gshared DsymbolTable modules; // symbol table of all modules
325 extern (C++) __gshared Modules amodules; // array of all modules
326 extern (C++) __gshared Dsymbols deferred; // deferred Dsymbol's needing semantic() run on them
327 extern (C++) __gshared Dsymbols deferred2; // deferred Dsymbol's needing semantic2() run on them
328 extern (C++) __gshared Dsymbols deferred3; // deferred Dsymbol's needing semantic3() run on them
332 modules = new DsymbolTable();
336 * Deinitializes the global state of the compiler.
338 * This can be used to restore the state set by `_init` to its original
341 static void deinitialize()
343 modules = modules.init;
346 extern (C++) __gshared AggregateDeclaration moduleinfo;
348 const(char)[] arg; // original argument name
349 ModuleDeclaration* md; // if !=null, the contents of the ModuleDeclaration declaration
350 const FileName srcfile; // input source file
351 const FileName objfile; // output .obj file
352 const FileName hdrfile; // 'header' file
353 FileName docfile; // output documentation file
354 const(ubyte)[] src; /// Raw content of the file
355 uint errors; // if any errors in file
356 uint numlines; // number of lines in source file
357 FileType filetype; // source file type
358 bool hasAlwaysInlines; // contains references to functions that must be inlined
359 bool isPackageFile; // if it is a package.d
360 Package pkg; // if isPackageFile is true, the Package that contains this package.d
361 Strings contentImportedFiles; // array of files whose content was imported
363 private ThreeState selfimports;
364 private ThreeState rootimports;
365 Dsymbol[void*] tagSymTab; /// ImportC: tag symbols that conflict with other symbols used as the index
367 private OutBuffer defines; // collect all the #define lines here
370 /*************************************
371 * Return true if module imports itself.
375 //printf("Module::selfImports() %s\n", toChars());
376 if (selfimports == ThreeState.none)
378 foreach (Module m; amodules)
380 selfimports = imports(this) ? ThreeState.yes : ThreeState.no;
381 foreach (Module m; amodules)
384 return selfimports == ThreeState.yes;
387 /*************************************
388 * Return true if module imports root module.
392 //printf("Module::rootImports() %s\n", toChars());
393 if (rootimports == ThreeState.none)
395 foreach (Module m; amodules)
397 rootimports = ThreeState.no;
398 foreach (Module m; amodules)
400 if (m.isRoot() && imports(m))
402 rootimports = ThreeState.yes;
406 foreach (Module m; amodules)
409 return rootimports == ThreeState.yes;
412 Identifier searchCacheIdent;
413 Dsymbol searchCacheSymbol; // cached value of search
414 SearchOptFlags searchCacheFlags; // cached flags
418 * A root module is one that will be compiled all the way to
419 * object code. This field holds the root module that caused
420 * this module to be loaded. If this module is a root module,
421 * then it will be set to `this`. This is used to determine
422 * ownership of template instantiation.
426 Dsymbols* decldefs; // top level declarations for this Module
428 Modules aimports; // all imported modules
430 uint debuglevel; // debug level
431 Identifiers* debugids; // debug identifiers
432 Identifiers* debugidsNot; // forward referenced debug identifiers
434 uint versionlevel; // version level
435 Identifiers* versionids; // version identifiers
436 Identifiers* versionidsNot; // forward referenced version identifiers
438 MacroTable macrotable; // document comment macros
439 Escape* _escapetable; // document comment escapes
441 size_t nameoffset; // offset of module name from start of ModuleInfo
442 size_t namelen; // length of module name in characters
444 extern (D) this(const ref Loc loc, const(char)[] filename, Identifier ident, int doDocComment, int doHdrGen)
447 const(char)[] srcfilename;
448 //printf("Module::Module(filename = '%.*s', ident = '%s')\n", cast(int)filename.length, filename.ptr, ident.toChars());
450 srcfilename = FileName.defaultExt(filename, mars_ext);
451 if (target.run_noext && global.params.run &&
452 !FileName.ext(filename) &&
453 FileName.exists(srcfilename) == 0 &&
454 FileName.exists(filename) == 1)
456 FileName.free(srcfilename.ptr);
457 srcfilename = FileName.removeExt(filename); // just does a mem.strdup(filename)
459 else if (!FileName.equalsExt(srcfilename, mars_ext) &&
460 !FileName.equalsExt(srcfilename, hdr_ext) &&
461 !FileName.equalsExt(srcfilename, c_ext) &&
462 !FileName.equalsExt(srcfilename, i_ext) &&
463 !FileName.equalsExt(srcfilename, dd_ext))
466 error(loc, "%s `%s` source file name '%.*s' must have .%.*s extension",
468 cast(int)srcfilename.length, srcfilename.ptr,
469 cast(int)mars_ext.length, mars_ext.ptr);
473 srcfile = FileName(srcfilename);
474 objfile = setOutfilename(global.params.objname, global.params.objdir, filename, target.obj_ext);
478 hdrfile = setOutfilename(global.params.dihdr.name, global.params.dihdr.dir, arg, hdr_ext);
481 extern (D) this(const(char)[] filename, Identifier ident, int doDocComment, int doHdrGen)
483 this(Loc.initial, filename, ident, doDocComment, doHdrGen);
486 static Module create(const(char)* filename, Identifier ident, int doDocComment, int doHdrGen)
488 return create(filename.toDString, ident, doDocComment, doHdrGen);
491 extern (D) static Module create(const(char)[] filename, Identifier ident, int doDocComment, int doHdrGen)
493 return new Module(Loc.initial, filename, ident, doDocComment, doHdrGen);
496 static const(char)* find(const(char)* filename)
498 return find(filename.toDString).ptr;
501 extern (D) static const(char)[] find(const(char)[] filename)
503 return global.fileManager.lookForSourceFile(filename, global.path[]);
506 extern (C++) static Module load(const ref Loc loc, Identifiers* packages, Identifier ident)
508 return load(loc, packages ? (*packages)[] : null, ident);
511 extern (D) static Module load(const ref Loc loc, Identifier[] packages, Identifier ident)
513 //printf("Module::load(ident = '%s')\n", ident.toChars());
514 // Build module filename by turning:
518 const(char)[] filename = getFilename(packages, ident);
519 // Look for the source file
520 if (const result = find(filename))
521 filename = result; // leaks
523 auto m = new Module(loc, filename, ident, 0, 0);
527 if (global.params.v.verbose)
530 foreach (pid; packages)
532 buf.writestring(pid.toString());
535 buf.printf("%s\t(%s)", ident.toChars(), m.srcfile.toChars());
536 message("import %s", buf.peekChars());
538 if((m = m.parse()) is null) return null;
543 override const(char)* kind() const
548 /*********************************************
549 * Combines things into output file name for .html and .di files.
551 * name Command line name given for the file, NULL if none
552 * dir Command line directory given for the file, NULL if none
553 * arg Name of the source file
554 * ext File name extension to use if 'name' is NULL
555 * global.params.preservePaths get output path from arg
556 * srcfile Input file - output file name must not match input file
558 extern(D) FileName setOutfilename(const(char)[] name, const(char)[] dir, const(char)[] arg, const(char)[] ext)
560 const(char)[] docfilename;
567 const(char)[] argdoc;
569 if (arg == "__stdin.d")
571 buf.printf("__stdin_%d.d", getpid());
574 if (global.params.preservePaths)
577 argdoc = FileName.name(arg);
578 // If argdoc doesn't have an absolute path, make it relative to dir
579 if (!FileName.absolute(argdoc))
581 //FileName::ensurePathExists(dir);
582 argdoc = FileName.combine(dir, argdoc);
584 docfilename = FileName.forceExt(argdoc, ext);
586 if (FileName.equals(docfilename, srcfile.toString()))
588 error(loc, "%s `%s` source file and output file have same name '%s'",
589 kind, toPrettyChars, srcfile.toChars());
592 return FileName(docfilename);
595 extern (D) void setDocfile()
597 docfile = setOutfilename(global.params.ddoc.name, global.params.ddoc.dir, arg, doc_ext);
601 * Trigger the relevant semantic error when a file cannot be read
603 * We special case `object.d` as a failure is likely to be a rare
604 * but difficult to diagnose case for the user. Packages also require
605 * special handling to avoid exposing the compiler's internals.
608 * loc = The location at which the file read originated (e.g. import)
610 private void onFileReadError(const ref Loc loc)
612 if (FileName.equals(srcfile.toString(), "object.d"))
614 .error(loc, "cannot find source code for runtime library file 'object.d'");
617 errorSupplemental(loc, "ldc2 might not be correctly installed.");
618 errorSupplemental(loc, "Please check your ldc2.conf configuration file.");
619 errorSupplemental(loc, "Installation instructions can be found at http://wiki.dlang.org/LDC.");
623 errorSupplemental(loc, "dmd might not be correctly installed. Run 'dmd -man' for installation instructions.");
624 const dmdConfFile = global.inifilename.length ? FileName.canonicalName(global.inifilename) : "not found";
625 errorSupplemental(loc, "config file: %.*s", cast(int)dmdConfFile.length, dmdConfFile.ptr);
628 else if (FileName.ext(this.arg) || !loc.isValid())
630 // Modules whose original argument name has an extension, or do not
631 // have a valid location come from the command-line.
632 // Error that their file cannot be found and return early.
633 .error(loc, "cannot find input file `%s`", srcfile.toChars());
637 // if module is not named 'package' but we're trying to read 'package.d', we're looking for a package module
638 bool isPackageMod = (strcmp(toChars(), "package") != 0) && isPackageFileName(srcfile);
640 .error(loc, "importing package '%s' requires a 'package.d' file which cannot be found in '%s'", toChars(), srcfile.toChars());
643 .error(loc, "unable to read module `%s`", toChars());
644 const pkgfile = FileName.combine(FileName.removeExt(srcfile.toString()), package_d);
645 .errorSupplemental(loc, "Expected '%s' or '%s' in one of the following import paths:",
646 srcfile.toChars(), pkgfile.ptr);
653 if (global.path.length)
655 foreach (i, p; global.path[])
656 fprintf(stderr, "import path[%llu] = %s\n", cast(ulong)i, p);
660 fprintf(stderr, "Specify path to file '%s' with -I switch\n", srcfile.toChars());
663 removeHdrFilesAndFail(global.params, Module.amodules);
668 * Reads the file from `srcfile` and loads the source buffer.
670 * If makefile module dependency is requested, we add this module
671 * to the list of dependencies from here.
676 * Returns: `true` if successful
678 bool read(const ref Loc loc)
681 return true; // already read
683 //printf("Module::read('%s') file '%s'\n", toChars(), srcfile.toChars());
685 /* Preprocess the file if it's a .c file
687 FileName filename = srcfile;
689 const(ubyte)[] srctext;
690 if (global.preprocess &&
691 FileName.equalsExt(srcfile.toString(), c_ext) &&
692 FileName.exists(srcfile.toString()))
694 /* Apply C preprocessor to the .c file, returning the contents
695 * after preprocessing
697 srctext = global.preprocess(srcfile, loc, defines).data;
700 srctext = global.fileManager.getFileContents(filename);
705 if (global.params.makeDeps.doOutput)
706 global.params.makeDeps.files.push(srcfile.toChars());
710 this.onFileReadError(loc);
717 return parseModule!ASTCodegen();
721 extern (D) Module parseModule(AST)()
723 const(char)* srcname = srcfile.toChars();
724 //printf("Module::parse(srcname = '%s')\n", srcname);
725 isPackageFile = isPackageFileName(srcfile);
726 const(char)[] buf = processSource(src, this);
727 // an error happened on UTF conversion
728 if (buf is null) return null;
730 /* If it starts with the string "Ddoc", then it's a documentation
733 if (buf.length>= 4 && buf[0..4] == "Ddoc")
735 comment = buf.ptr + 4;
736 filetype = FileType.ddoc;
741 /* If it has the extension ".dd", it is also a documentation
742 * source file. Documentation source files may begin with "Ddoc"
743 * but do not have to if they have the .dd extension.
744 * https://issues.dlang.org/show_bug.cgi?id=15465
746 if (FileName.equalsExt(arg, dd_ext))
748 comment = buf.ptr; // the optional Ddoc, if present, is handled above.
749 filetype = FileType.ddoc;
754 /* If it has the extension ".di", it is a "header" file.
756 if (FileName.equalsExt(arg, hdr_ext))
757 filetype = FileType.dhdr;
759 /// Promote `this` to a root module if requested via `-i`
760 void checkCompiledImport()
762 if (!this.isRoot() && Compiler.onImport(this))
763 this.importedFrom = this;
767 Package ppack = null;
769 /* If it has the extension ".c", it is a "C" file.
770 * If it has the extension ".i", it is a preprocessed "C" file.
772 if (FileName.equalsExt(arg, c_ext) || FileName.equalsExt(arg, i_ext))
774 filetype = FileType.c;
776 global.compileEnv.masm = target.os == Target.OS.Windows && !target.omfobj; // Microsoft inline assembler format
777 scope p = new CParser!AST(this, buf, cast(bool) docfile, global.errorSink, target.c, &defines, &global.compileEnv);
778 global.compileEnv.masm = false;
780 checkCompiledImport();
781 members = p.parseModule();
782 assert(!p.md); // C doesn't have module declarations
783 numlines = p.scanloc.linnum;
787 const bool doUnittests = global.params.parsingUnittestsRequired();
788 scope p = new Parser!AST(this, buf, cast(bool) docfile, global.errorSink, &global.compileEnv, doUnittests);
789 p.transitionIn = global.params.v.vin;
791 p.parseModuleDeclaration();
796 /* A ModuleDeclaration, md, was provided.
797 * The ModuleDeclaration sets the packages this module appears in, and
798 * the name of this module.
801 dst = Package.resolve(md.packages, &this.parent, &ppack);
804 // Done after parsing the module header because `module x.y.z` may override the file name
805 checkCompiledImport();
807 members = p.parseModuleContent();
808 numlines = p.scanloc.linnum;
811 /* The symbol table into which the module is to be inserted.
816 // Mark the package path as accessible from the current module
817 // https://issues.dlang.org/show_bug.cgi?id=21661
818 // Code taken from Import.addPackageAccess()
819 if (md.packages.length > 0)
823 addAccessiblePackage(p, Visibility(Visibility.Kind.private_));
824 foreach (id; md.packages[1 .. $]) // [b, c]
826 p = cast(Package) p.symtab.lookup(id);
829 addAccessiblePackage(p, Visibility(Visibility.Kind.private_));
833 Module m = ppack ? ppack.isModule() : null;
834 if (m && !isPackageFileName(m.srcfile))
836 .error(md.loc, "package name '%s' conflicts with usage as a module name in file %s", ppack.toPrettyChars(), m.srcfile.toChars());
841 /* The name of the module is set to the source file name.
842 * There are no packages.
844 dst = modules; // and so this module goes into global module symbol table
845 /* Check to see if module name is a valid identifier
847 if (!Identifier.isValidIdentifier(this.ident.toChars()))
848 error(loc, "%s `%s` has non-identifier characters in filename, use module declaration instead", kind, toPrettyChars);
850 // Insert module into the symbol table
854 /* If the source tree is as follows:
858 * the 'pkg' will be incorporated to the internal package tree in two ways:
863 * If both are used in one compilation, 'pkg' as a module (== pkg/package.d)
864 * and a package name 'pkg' will conflict each other.
866 * To avoid the conflict:
867 * 1. If preceding package name insertion had occurred by Package::resolve,
868 * reuse the previous wrapping 'Package' if it exists
869 * 2. Otherwise, 'package.d' wrapped by 'Package' is inserted to the internal tree in here.
871 * Then change Package::isPkgMod to PKG.module_ and set Package::mod.
873 * Note that the 'wrapping Package' is the Package that contains package.d and other submodules,
874 * the one inserted to the symbol table.
876 auto ps = dst.lookup(ident);
877 Package p = ps ? ps.isPackage() : null;
880 p = new Package(Loc.initial, ident);
881 p.tag = this.tag; // reuse the same package tag
882 p.symtab = new DsymbolTable();
884 this.tag = p.tag; // reuse the 'older' package tag
886 p.parent = this.parent;
887 p.isPkgMod = PKG.module_;
893 /* It conflicts with a name that is already in the symbol table.
894 * Figure out what went wrong, and issue error message.
896 Dsymbol prev = dst.lookup(ident);
898 if (Module mprev = prev.isModule())
900 if (!FileName.equals(srcname, mprev.srcfile.toChars()))
901 error(loc, "%s `%s` from file %s conflicts with another module %s from file %s", kind, toPrettyChars, srcname, mprev.toChars(), mprev.srcfile.toChars());
902 else if (isRoot() && mprev.isRoot())
903 error(loc, "%s `%s` from file %s is specified twice on the command line", kind, toPrettyChars, srcname);
905 error(loc, "%s `%s` from file %s must be imported with 'import %s;'", kind, toPrettyChars, srcname, toPrettyChars());
906 // https://issues.dlang.org/show_bug.cgi?id=14446
907 // Return previously parsed module to avoid AST duplication ICE.
910 else if (Package pkg = prev.isPackage())
912 // 'package.d' loaded after a previous 'Package' insertion
914 amodules.push(this); // Add to global array of all modules
916 error(md ? md.loc : loc, "%s `%s` from file %s conflicts with package name %s", kind, toPrettyChars, srcname, pkg.toChars());
919 assert(global.errors);
923 // Add to global array of all modules
926 Compiler.onParseModule(this);
930 /**********************************
931 * Determine if we need to generate an instance of ModuleInfo
936 //printf("needModuleInfo() %s, %d, %d\n", toChars(), needmoduleinfo, global.params.cov);
937 return needmoduleinfo || global.params.cov;
940 /*******************************************
941 * Print deprecation warning if we're deprecated, when
942 * this module is imported from scope sc.
945 * sc = the scope into which we are imported
946 * loc = the location of the import statement
948 void checkImportDeprecation(const ref Loc loc, Scope* sc)
950 if (md && md.isdeprecated && !sc.isDeprecated)
952 Expression msg = md.msg;
953 if (StringExp se = msg ? msg.toStringExp() : null)
955 const slice = se.peekString();
958 deprecation(loc, "%s `%s` is deprecated - %.*s", kind, toPrettyChars, cast(int)slice.length, slice.ptr);
962 deprecation(loc, "%s `%s` is deprecated", kind, toPrettyChars);
966 override bool isPackageAccessible(Package p, Visibility visibility, SearchOptFlags flags = SearchOpt.all)
968 if (insearch) // don't follow import cycles
973 if (flags & SearchOpt.ignorePrivateImports)
974 visibility = Visibility(Visibility.Kind.public_); // only consider public imports
975 return super.isPackageAccessible(p, visibility);
978 override Dsymbol symtabInsert(Dsymbol s)
980 searchCacheIdent = null; // symbol is inserted, so invalidate cache
981 return Package.symtabInsert(s);
984 extern (D) void deleteObjFile()
986 if (global.params.obj)
987 File.remove(objfile.toChars());
989 File.remove(docfile.toChars());
992 /*******************************************
993 * Can't run semantic on s now, try again later.
995 extern (D) static void addDeferredSemantic(Dsymbol s)
997 //printf("Module::addDeferredSemantic('%s')\n", s.toChars());
998 if (!deferred.contains(s))
1002 extern (D) static void addDeferredSemantic2(Dsymbol s)
1004 //printf("Module::addDeferredSemantic2('%s')\n", s.toChars());
1005 if (!deferred2.contains(s))
1009 extern (D) static void addDeferredSemantic3(Dsymbol s)
1011 //printf("Module::addDeferredSemantic3('%s')\n", s.toChars());
1012 if (!deferred.contains(s))
1016 /******************************************
1017 * Run semantic() on deferred symbols.
1019 static void runDeferredSemantic()
1021 __gshared int nested;
1024 //if (deferred.length) printf("+Module::runDeferredSemantic(), len = %ld\n", deferred.length);
1030 len = deferred.length;
1035 Dsymbol* todoalloc = null;
1043 todo = cast(Dsymbol*)Mem.check(malloc(len * Dsymbol.sizeof));
1046 memcpy(todo, deferred.tdata(), len * Dsymbol.sizeof);
1051 Dsymbol s = todo[i];
1052 s.dsymbolSemantic(null);
1053 //printf("deferred: %s, parent = %s\n", s.toChars(), s.parent.toChars());
1055 //printf("\tdeferred.length = %ld, len = %ld\n", deferred.length, len);
1059 while (deferred.length != len); // while making progress
1061 //printf("-Module::runDeferredSemantic(), len = %ld\n", deferred.length);
1064 static void runDeferredSemantic2()
1066 Module.runDeferredSemantic();
1068 Dsymbols* a = &Module.deferred2;
1069 for (size_t i = 0; i < a.length; i++)
1071 Dsymbol s = (*a)[i];
1072 //printf("[%d] %s semantic2a\n", i, s.toPrettyChars());
1081 static void runDeferredSemantic3()
1083 Module.runDeferredSemantic2();
1085 Dsymbols* a = &Module.deferred3;
1086 for (size_t i = 0; i < a.length; i++)
1088 Dsymbol s = (*a)[i];
1089 //printf("[%d] %s semantic3a\n", i, s.toPrettyChars());
1098 extern (D) static void clearCache() nothrow
1100 foreach (Module m; amodules)
1101 m.searchCacheIdent = null;
1104 /************************************
1105 * Recursively look at every module this module imports,
1106 * return true if it imports m.
1107 * Can be used to detect circular imports.
1109 int imports(Module m) nothrow
1111 //printf("%s Module::imports(%s)\n", toChars(), m.toChars());
1114 foreach (i, Module mi; aimports)
1115 printf("\t[%d] %s\n", cast(int) i, mi.toChars());
1117 foreach (Module mi; aimports)
1124 int r = mi.imports(m);
1132 bool isRoot() nothrow
1134 return this.importedFrom == this;
1137 /// Returns: Whether this module is in the `core` package and has name `ident`
1138 bool isCoreModule(Identifier ident) nothrow
1140 return this.ident == ident && parent && parent.ident == Id.core && !parent.parent;
1144 int doppelganger; // sub-module
1145 Symbol* cov; // private uint[] __coverage;
1146 uint[] covb; // bit array of valid code line numbers
1147 Symbol* sictor; // module order independent constructor
1148 Symbol* sctor; // module constructor
1149 Symbol* sdtor; // module destructor
1150 Symbol* ssharedctor; // module shared constructor
1151 Symbol* sshareddtor; // module shared destructor
1152 Symbol* stest; // module unit test
1153 Symbol* sfilename; // symbol for filename
1155 uint[uint] ctfe_cov; /// coverage information from ctfe execution_count[line]
1157 override inout(Module) isModule() inout nothrow
1162 override void accept(Visitor v)
1167 /***********************************************
1168 * Writes this module's fully-qualified name to buf
1170 * buf = The buffer to write to
1172 void fullyQualifiedName(ref OutBuffer buf) nothrow
1174 buf.writestring(ident.toString());
1176 for (auto package_ = parent; package_ !is null; package_ = package_.parent)
1178 buf.prependstring(".");
1179 buf.prependstring(package_.ident.toChars());
1183 /** Lazily initializes and returns the escape table.
1184 Turns out it eats a lot of memory.
1186 extern(D) Escape* escapetable() nothrow
1189 _escapetable = new Escape();
1190 return _escapetable;
1193 /****************************
1194 * A Singleton that loads core.stdc.config
1196 * Module of core.stdc.config, null if couldn't find it
1198 extern (D) static Module loadCoreStdcConfig()
1200 __gshared Module core_stdc_config;
1201 auto pkgids = new Identifier[2];
1202 pkgids[0] = Id.core;
1203 pkgids[1] = Id.stdc;
1204 return loadModuleFromLibrary(core_stdc_config, pkgids, Id.config);
1207 /****************************
1208 * A Singleton that loads core.atomic
1210 * Module of core.atomic, null if couldn't find it
1212 extern (D) static Module loadCoreAtomic()
1214 __gshared Module core_atomic;
1215 auto pkgids = new Identifier[1];
1216 pkgids[0] = Id.core;
1217 return loadModuleFromLibrary(core_atomic, pkgids, Id.atomic);
1220 /****************************
1221 * A Singleton that loads std.math
1223 * Module of std.math, null if couldn't find it
1225 extern (D) static Module loadStdMath()
1227 __gshared Module std_math;
1228 auto pkgids = new Identifier[1];
1230 return loadModuleFromLibrary(std_math, pkgids, Id.math);
1233 /**********************************
1234 * Load a Module from the library.
1236 * mod = cached return value of this call
1237 * pkgids = package identifiers
1240 * Module loaded, null if cannot load it
1242 extern (D) private static Module loadModuleFromLibrary(ref Module mod, Identifier[] pkgids, Identifier modid)
1247 auto imp = new Import(Loc.initial, pkgids[], modid, null, true);
1248 // Module.load will call fatal() if there's no module available.
1249 // Gag the error here, pushing the error handling to the caller.
1250 const errors = global.startGagging();
1254 imp.mod.importAll(null);
1255 imp.mod.dsymbolSemantic(null);
1257 global.endGagging(errors);
1263 /***********************************************************
1265 extern (C++) struct ModuleDeclaration
1269 Identifier[] packages; // array of Identifier's representing packages
1270 bool isdeprecated; // if it is a deprecated module
1273 extern (D) this(const ref Loc loc, Identifier[] packages, Identifier id, Expression msg, bool isdeprecated) @safe
1276 this.packages = packages;
1279 this.isdeprecated = isdeprecated;
1282 extern (C++) const(char)* toChars() const @safe
1285 foreach (pid; packages)
1287 buf.writestring(pid.toString());
1290 buf.writestring(id.toString());
1291 return buf.extractChars();
1294 /// Provide a human readable representation
1295 extern (D) const(char)[] toString() const
1297 return this.toChars().toDString;
1301 /****************************************
1302 * Create array of the local classes in the Module, suitable
1303 * for inclusion in ModuleInfo
1306 * aclasses = array to fill in
1307 * Returns: array of local classes
1309 void getLocalClasses(Module mod, ref ClassDeclarations aclasses)
1311 //printf("members.length = %d\n", mod.members.length);
1312 int pushAddClassDg(size_t n, Dsymbol sm)
1317 if (auto cd = sm.isClassDeclaration())
1319 // compatibility with previous algorithm
1320 if (cd.parent && cd.parent.isTemplateMixin())
1323 if (cd.classKind != ClassKind.objc)
1329 _foreach(null, mod.members, &pushAddClassDg);
1333 alias ForeachDg = int delegate(size_t idx, Dsymbol s);
1335 /***************************************
1336 * Expands attribute declarations in members in depth first
1337 * order. Calls dg(size_t symidx, Dsymbol *sym) for each
1339 * If dg returns !=0, stops and returns that value else returns 0.
1340 * Use this function to avoid the O(N + N^2/2) complexity of
1341 * calculating dim and calling N times getNth.
1343 * last value returned by dg()
1345 int _foreach(Scope* sc, Dsymbols* members, scope ForeachDg dg, size_t* pn = null)
1350 size_t n = pn ? *pn : 0; // take over index
1352 foreach (size_t i; 0 .. members.length)
1354 import dmd.attrib : AttribDeclaration;
1355 import dmd.dtemplate : TemplateMixin;
1357 Dsymbol s = (*members)[i];
1358 if (AttribDeclaration a = s.isAttribDeclaration())
1359 result = _foreach(sc, a.include(sc), dg, &n);
1360 else if (TemplateMixin tm = s.isTemplateMixin())
1361 result = _foreach(sc, tm.members, dg, &n);
1362 else if (s.isTemplateInstance())
1365 else if (s.isUnitTestDeclaration())
1369 result = dg(n++, s);
1374 *pn = n; // update index
1379 * Process the content of a source file
1381 * Attempts to find which encoding it is using, if it has BOM,
1382 * and then normalize the source to UTF-8. If no encoding is required,
1383 * a slice of `src` will be returned without extra allocation.
1386 * src = Content of the source file to process
1387 * mod = Module matching `src`, used for error handling
1390 * UTF-8 encoded variant of `src`, stripped of any BOM,
1391 * or `null` if an error happened.
1393 private const(char)[] processSource (const(ubyte)[] src, Module mod)
1395 enum SourceEncoding { utf16, utf32}
1396 enum Endian { little, big}
1399 * Convert a buffer from UTF32 to UTF8
1401 * Endian = is the buffer big/little endian
1402 * buf = buffer of UTF32 data
1404 * input buffer reencoded as UTF8
1407 char[] UTF32ToUTF8(Endian endian)(const(char)[] buf)
1409 static if (endian == Endian.little)
1410 alias readNext = Port.readlongLE;
1412 alias readNext = Port.readlongBE;
1416 .error(mod.loc, "%s `%s` odd length of UTF-32 char source %llu",
1417 mod.kind, mod.toPrettyChars, cast(ulong) buf.length);
1421 const (uint)[] eBuf = cast(const(uint)[])buf;
1424 dbuf.reserve(eBuf.length);
1426 foreach (i; 0 .. eBuf.length)
1428 const u = readNext(&eBuf[i]);
1433 .error(mod.loc, "%s `%s` UTF-32 value %08x greater than 0x10FFFF", mod.kind, mod.toPrettyChars, u);
1441 dbuf.writeByte(0); //add null terminator
1442 return dbuf.extractSlice();
1446 * Convert a buffer from UTF16 to UTF8
1448 * Endian = is the buffer big/little endian
1449 * buf = buffer of UTF16 data
1451 * input buffer reencoded as UTF8
1454 char[] UTF16ToUTF8(Endian endian)(const(char)[] buf)
1456 static if (endian == Endian.little)
1457 alias readNext = Port.readwordLE;
1459 alias readNext = Port.readwordBE;
1463 .error(mod.loc, "%s `%s` odd length of UTF-16 char source %llu", mod.kind, mod.toPrettyChars, cast(ulong) buf.length);
1467 const (ushort)[] eBuf = cast(const(ushort)[])buf;
1470 dbuf.reserve(eBuf.length);
1472 //i will be incremented in the loop for high codepoints
1473 foreach (ref i; 0 .. eBuf.length)
1475 uint u = readNext(&eBuf[i]);
1478 if (0xD800 <= u && u < 0xDC00)
1481 if (i >= eBuf.length)
1483 .error(mod.loc, "%s `%s` surrogate UTF-16 high value %04x at end of file", mod.kind, mod.toPrettyChars, u);
1486 const u2 = readNext(&eBuf[i]);
1487 if (u2 < 0xDC00 || 0xE000 <= u2)
1489 .error(mod.loc, "%s `%s` surrogate UTF-16 low value %04x out of range", mod.kind, mod.toPrettyChars, u2);
1492 u = (u - 0xD7C0) << 10;
1495 else if (u >= 0xDC00 && u <= 0xDFFF)
1497 .error(mod.loc, "%s `%s` unpaired surrogate UTF-16 value %04x", mod.kind, mod.toPrettyChars, u);
1500 else if (u == 0xFFFE || u == 0xFFFF)
1502 .error(mod.loc, "%s `%s` illegal UTF-16 value %04x", mod.kind, mod.toPrettyChars, u);
1510 dbuf.writeByte(0); //add a terminating null byte
1511 return dbuf.extractSlice();
1514 const(char)[] buf = cast(const(char)[]) src;
1516 // Assume the buffer is from memory and has not be read from disk. Assume UTF-8.
1520 /* Convert all non-UTF-8 formats to UTF-8.
1521 * BOM : https://www.unicode.org/faq/utf_bom.html
1522 * 00 00 FE FF UTF-32BE, big-endian
1523 * FF FE 00 00 UTF-32LE, little-endian
1524 * FE FF UTF-16BE, big-endian
1525 * FF FE UTF-16LE, little-endian
1528 if (buf[0] == 0xFF && buf[1] == 0xFE)
1530 if (buf.length >= 4 && buf[2] == 0 && buf[3] == 0)
1531 return UTF32ToUTF8!(Endian.little)(buf[4 .. $]);
1532 return UTF16ToUTF8!(Endian.little)(buf[2 .. $]);
1535 if (buf[0] == 0xFE && buf[1] == 0xFF)
1536 return UTF16ToUTF8!(Endian.big)(buf[2 .. $]);
1538 if (buf.length >= 4 && buf[0] == 0 && buf[1] == 0 && buf[2] == 0xFE && buf[3] == 0xFF)
1539 return UTF32ToUTF8!(Endian.big)(buf[4 .. $]);
1541 if (buf.length >= 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF)
1544 /* There is no BOM. Make use of Arcane Jill's insight that
1545 * the first char of D source must be ASCII to
1546 * figure out the encoding.
1548 if (buf.length >= 4 && buf[1] == 0 && buf[2] == 0 && buf[3] == 0)
1549 return UTF32ToUTF8!(Endian.little)(buf);
1550 if (buf.length >= 4 && buf[0] == 0 && buf[1] == 0 && buf[2] == 0)
1551 return UTF32ToUTF8!(Endian.big)(buf);
1552 // try to check for UTF-16
1553 if (buf.length >= 2 && buf[1] == 0)
1554 return UTF16ToUTF8!(Endian.little)(buf);
1556 return UTF16ToUTF8!(Endian.big)(buf);
1561 auto loc = mod.getLoc();
1562 .error(loc, "%s `%s` source file must start with BOM or ASCII character, not \\x%02X", mod.kind, mod.toPrettyChars, buf[0]);
1569 /*******************************************
1570 * Look for member of the form:
1571 * const(MemberInfo)[] getMembers(string);
1572 * Returns NULL if not found
1574 FuncDeclaration findGetMembers(ScopeDsymbol dsym)
1576 import dmd.opover : search_function;
1577 Dsymbol s = search_function(dsym, Id.getmembers);
1578 FuncDeclaration fdx = s ? s.isFuncDeclaration() : null;
1582 __gshared TypeFunction tfgetmembers;
1586 sc.eSink = global.errorSink;
1587 auto parameters = new Parameters();
1588 Parameters* p = new Parameter(STC.in_, Type.tchar.constOf().arrayOf(), null, null);
1591 TypeFunction tf = new TypeFunction(parameters, tret, VarArg.none, LINK.d);
1592 tfgetmembers = tf.dsymbolSemantic(Loc.initial, &sc).isTypeFunction();
1595 fdx = fdx.overloadExactMatch(tfgetmembers);
1597 if (fdx && fdx.isVirtual())