2 * Defines a package and module.
4 * Specification: $(LINK2 https://dlang.org/spec/module.html, Modules)
6 * Copyright: Copyright (C) 1999-2023 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
;
20 import dmd
.arraytypes
;
21 import dmd
.astcodegen
;
30 import dmd
.dsymbolsem
;
33 import dmd
.expression
;
34 import dmd
.expressionsem
;
35 import dmd
.file_manager
;
38 import dmd
.identifier
;
42 import dmd
.root
.array
;
44 import dmd
.root
.filename
;
45 import dmd
.common
.outbuffer
;
48 import dmd
.root
.rootobject
;
49 import dmd
.root
.string
;
57 else version (IN_LLVM
) {}
60 // function used to call semantic3 on a module's dependencies
61 void semantic3OnDependencies(Module m
)
66 if (m
.semanticRun
> PASS
.semantic3
)
71 foreach (i
; 1 .. m
.aimports
.length
)
72 semantic3OnDependencies(m
.aimports
[i
]);
76 * Remove generated .di files on error and exit
78 void removeHdrFilesAndFail(ref Param params
, ref Modules modules
) nothrow
80 if (params
.dihdr
.doOutput
)
84 if (m
.filetype
== FileType
.dhdr
)
86 File
.remove(m
.hdrfile
.toChars());
94 * Converts a chain of identifiers to the filename of the module
97 * packages = the names of the "parent" packages
98 * ident = the name of the child package or module
101 * the filename of the child package or module
103 private const(char)[] getFilename(Identifier
[] packages
, Identifier ident
) nothrow
105 const(char)[] filename
= ident
.toString();
109 auto modAliases
= &global
.params
.modFileAliasStrings
;
111 if (packages
.length
== 0 && modAliases
.length
== 0)
114 void checkModFileAlias(const(char)[] p
)
116 /* Check and replace the contents of buf[] with
117 * an alias string from global.params.modFileAliasStrings[]
119 dotmods
.writestring(p
);
120 foreach_reverse (const m
; *modAliases
)
122 const q
= strchr(m
, '=');
124 if (dotmods
.length
== q
- m
&& memcmp(dotmods
.peekChars(), m
, q
- m
) == 0)
127 auto rhs
= q
[1 .. strlen(q
)];
128 if (rhs
.length
> 0 && (rhs
[$ - 1] == '/' || rhs
[$ - 1] == '\\'))
129 rhs
= rhs
[0 .. $ - 1]; // remove trailing separator
130 buf
.writestring(rhs
);
131 break; // last matching entry in ms[] wins
134 dotmods
.writeByte('.');
137 foreach (pid
; packages
)
139 const p
= pid
.toString();
141 if (modAliases
.length
)
142 checkModFileAlias(p
);
144 enum FileSeparator
= '\\';
146 enum FileSeparator
= '/';
147 buf
.writeByte(FileSeparator
);
149 buf
.writestring(filename
);
150 if (modAliases
.length
)
151 checkModFileAlias(filename
);
153 filename
= buf
.extractSlice()[0 .. $ - 1];
158 /***********************************************************
160 extern (C
++) class Package
: ScopeDsymbol
162 PKG isPkgMod
= PKG
.unknown
;
163 uint tag
; // auto incremented tag, used to mask package tree in scopes
164 Module mod
; // !=null if isPkgMod == PKG.module_
166 final extern (D
) this(const ref Loc loc
, Identifier ident
) nothrow
169 __gshared
uint packageTag
;
170 this.tag
= packageTag
++;
173 override const(char)* kind() const nothrow
178 override bool equals(const RootObject o
) const
180 // custom 'equals' for bug 17441. "package a" and "module a" are not equal
183 auto p
= cast(Package
)o
;
184 return p
&& isModule() == p
.isModule() && ident
.equals(p
.ident
);
187 /****************************************************
189 * packages[] the pkg1.pkg2 of pkg1.pkg2.mod
191 * the symbol table that mod should be inserted into
193 * *pparent the rightmost package, i.e. pkg2, or NULL if no packages
194 * *ppkg the leftmost package, i.e. pkg1, or NULL if no packages
196 extern (D
) static DsymbolTable
resolve(Identifier
[] packages
, Dsymbol
* pparent
, Package
* ppkg
)
198 DsymbolTable dst
= Module
.modules
;
199 Dsymbol parent
= null;
200 //printf("Package::resolve()\n");
203 foreach (pid
; packages
)
206 Dsymbol p
= dst
.lookup(pid
);
209 pkg
= new Package(Loc
.initial
, pid
);
212 pkg
.symtab
= new DsymbolTable();
218 // It might already be a module, not a package, but that needs
219 // to be checked at a higher level, where a nice error message
221 // dot net needs modules and packages with same name
222 // But we still need a symbol table for it
224 pkg
.symtab
= new DsymbolTable();
232 // Return the module so that a nice error message can be generated
234 *ppkg
= cast(Package
)p
;
244 override final inout(Package
) isPackage() inout
250 * Checks if pkg is a sub-package of this
252 * For example, if this qualifies to 'a1.a2' and pkg - to 'a1.a2.a3',
253 * this function returns 'true'. If it is other way around or qualified
254 * package paths conflict function returns 'false'.
257 * pkg = possible subpackage
262 final bool isAncestorPackageOf(const Package pkg
) const
266 if (!pkg ||
!pkg
.parent
)
268 return isAncestorPackageOf(pkg
.parent
.isPackage());
271 override Dsymbol
search(const ref Loc loc
, Identifier ident
, int flags
= SearchLocalsOnly
)
273 //printf("%s Package.search('%s', flags = x%x)\n", toChars(), ident.toChars(), flags);
274 flags
&= ~SearchLocalsOnly
; // searching an import is always transitive
275 if (!isModule() && mod
)
277 // Prefer full package name.
278 Dsymbol s
= symtab ? symtab
.lookup(ident
) : null;
281 //printf("[%s] through pkdmod: %s\n", loc.toChars(), toChars());
282 return mod
.search(loc
, ident
, flags
);
284 return ScopeDsymbol
.search(loc
, ident
, flags
);
287 override void accept(Visitor v
)
292 final Module
isPackageMod()
294 if (isPkgMod
== PKG
.module_
)
302 * Checks for the existence of a package.d to set isPkgMod appropriately
303 * if isPkgMod == PKG.unknown
305 final void resolvePKGunknown()
309 if (isPkgMod
!= PKG
.unknown
)
312 Identifier
[] packages
;
313 for (Dsymbol s
= this.parent
; s
; s
= s
.parent
)
317 if (Module
.find(getFilename(packages
, ident
)))
318 Module
.load(Loc
.initial
, packages
, this.ident
);
320 isPkgMod
= PKG
.package_
;
324 /***********************************************************
326 extern (C
++) final class Module
: Package
328 extern (C
++) __gshared Module rootModule
;
329 extern (C
++) __gshared DsymbolTable modules
; // symbol table of all modules
330 extern (C
++) __gshared Modules amodules
; // array of all modules
331 extern (C
++) __gshared Dsymbols deferred
; // deferred Dsymbol's needing semantic() run on them
332 extern (C
++) __gshared Dsymbols deferred2
; // deferred Dsymbol's needing semantic2() run on them
333 extern (C
++) __gshared Dsymbols deferred3
; // deferred Dsymbol's needing semantic3() run on them
337 modules
= new DsymbolTable();
341 * Deinitializes the global state of the compiler.
343 * This can be used to restore the state set by `_init` to its original
346 static void deinitialize()
348 modules
= modules
.init
;
351 extern (C
++) __gshared AggregateDeclaration moduleinfo
;
353 const(char)[] arg
; // original argument name
354 ModuleDeclaration
* md
; // if !=null, the contents of the ModuleDeclaration declaration
355 const FileName srcfile
; // input source file
356 const FileName objfile
; // output .obj file
357 const FileName hdrfile
; // 'header' file
358 FileName docfile
; // output documentation file
359 const(ubyte)[] src
; /// Raw content of the file
360 uint errors
; // if any errors in file
361 uint numlines
; // number of lines in source file
362 FileType filetype
; // source file type
363 bool hasAlwaysInlines
; // contains references to functions that must be inlined
364 bool isPackageFile
; // if it is a package.d
365 Package pkg
; // if isPackageFile is true, the Package that contains this package.d
366 Strings contentImportedFiles
; // array of files whose content was imported
368 private ThreeState selfimports
;
369 private ThreeState rootimports
;
370 Dsymbol
[void*] tagSymTab
; /// ImportC: tag symbols that conflict with other symbols used as the index
372 private OutBuffer defines
; // collect all the #define lines here
375 /*************************************
376 * Return true if module imports itself.
380 //printf("Module::selfImports() %s\n", toChars());
381 if (selfimports
== ThreeState
.none
)
383 foreach (Module m
; amodules
)
385 selfimports
= imports(this) ? ThreeState
.yes
: ThreeState
.no
;
386 foreach (Module m
; amodules
)
389 return selfimports
== ThreeState
.yes
;
392 /*************************************
393 * Return true if module imports root module.
397 //printf("Module::rootImports() %s\n", toChars());
398 if (rootimports
== ThreeState
.none
)
400 foreach (Module m
; amodules
)
402 rootimports
= ThreeState
.no
;
403 foreach (Module m
; amodules
)
405 if (m
.isRoot() && imports(m
))
407 rootimports
= ThreeState
.yes
;
411 foreach (Module m
; amodules
)
414 return rootimports
== ThreeState
.yes
;
417 private Identifier searchCacheIdent
;
418 private Dsymbol searchCacheSymbol
; // cached value of search
419 private int searchCacheFlags
; // cached flags
420 private bool insearch
;
423 * A root module is one that will be compiled all the way to
424 * object code. This field holds the root module that caused
425 * this module to be loaded. If this module is a root module,
426 * then it will be set to `this`. This is used to determine
427 * ownership of template instantiation.
431 Dsymbols
* decldefs
; // top level declarations for this Module
433 Modules aimports
; // all imported modules
435 uint debuglevel
; // debug level
436 Identifiers
* debugids
; // debug identifiers
437 Identifiers
* debugidsNot
; // forward referenced debug identifiers
439 uint versionlevel
; // version level
440 Identifiers
* versionids
; // version identifiers
441 Identifiers
* versionidsNot
; // forward referenced version identifiers
443 MacroTable macrotable
; // document comment macros
444 Escape
* _escapetable
; // document comment escapes
446 size_t nameoffset
; // offset of module name from start of ModuleInfo
447 size_t namelen
; // length of module name in characters
449 extern (D
) this(const ref Loc loc
, const(char)[] filename
, Identifier ident
, int doDocComment
, int doHdrGen
)
452 const(char)[] srcfilename
;
453 //printf("Module::Module(filename = '%.*s', ident = '%s')\n", cast(int)filename.length, filename.ptr, ident.toChars());
455 srcfilename
= FileName
.defaultExt(filename
, mars_ext
);
456 if (target
.run_noext
&& global
.params
.run
&&
457 !FileName
.ext(filename
) &&
458 FileName
.exists(srcfilename
) == 0 &&
459 FileName
.exists(filename
) == 1)
461 FileName
.free(srcfilename
.ptr
);
462 srcfilename
= FileName
.removeExt(filename
); // just does a mem.strdup(filename)
464 else if (!FileName
.equalsExt(srcfilename
, mars_ext
) &&
465 !FileName
.equalsExt(srcfilename
, hdr_ext
) &&
466 !FileName
.equalsExt(srcfilename
, c_ext
) &&
467 !FileName
.equalsExt(srcfilename
, i_ext
) &&
468 !FileName
.equalsExt(srcfilename
, dd_ext
))
471 error(loc
, "%s `%s` source file name '%.*s' must have .%.*s extension",
473 cast(int)srcfilename
.length
, srcfilename
.ptr
,
474 cast(int)mars_ext
.length
, mars_ext
.ptr
);
478 srcfile
= FileName(srcfilename
);
479 objfile
= setOutfilename(global
.params
.objname
, global
.params
.objdir
, filename
, target
.obj_ext
);
483 hdrfile
= setOutfilename(global
.params
.dihdr
.name
, global
.params
.dihdr
.dir
, arg
, hdr_ext
);
486 extern (D
) this(const(char)[] filename
, Identifier ident
, int doDocComment
, int doHdrGen
)
488 this(Loc
.initial
, filename
, ident
, doDocComment
, doHdrGen
);
491 static Module
create(const(char)* filename
, Identifier ident
, int doDocComment
, int doHdrGen
)
493 return create(filename
.toDString
, ident
, doDocComment
, doHdrGen
);
496 extern (D
) static Module
create(const(char)[] filename
, Identifier ident
, int doDocComment
, int doHdrGen
)
498 return new Module(Loc
.initial
, filename
, ident
, doDocComment
, doHdrGen
);
501 static const(char)* find(const(char)* filename
)
503 return find(filename
.toDString
).ptr
;
506 extern (D
) static const(char)[] find(const(char)[] filename
)
508 return global
.fileManager
.lookForSourceFile(filename
, global
.path ?
(*global
.path
)[] : null);
511 extern (C
++) static Module
load(const ref Loc loc
, Identifiers
* packages
, Identifier ident
)
513 return load(loc
, packages ?
(*packages
)[] : null, ident
);
516 extern (D
) static Module
load(const ref Loc loc
, Identifier
[] packages
, Identifier ident
)
518 //printf("Module::load(ident = '%s')\n", ident.toChars());
519 // Build module filename by turning:
523 const(char)[] filename
= getFilename(packages
, ident
);
524 // Look for the source file
525 if (const result
= find(filename
))
526 filename
= result
; // leaks
528 auto m
= new Module(loc
, filename
, ident
, 0, 0);
532 if (global
.params
.v
.verbose
)
535 foreach (pid
; packages
)
537 buf
.writestring(pid
.toString());
540 buf
.printf("%s\t(%s)", ident
.toChars(), m
.srcfile
.toChars());
541 message("import %s", buf
.peekChars());
543 if((m
= m
.parse()) is null) return null;
548 override const(char)* kind() const
553 /*********************************************
554 * Combines things into output file name for .html and .di files.
556 * name Command line name given for the file, NULL if none
557 * dir Command line directory given for the file, NULL if none
558 * arg Name of the source file
559 * ext File name extension to use if 'name' is NULL
560 * global.params.preservePaths get output path from arg
561 * srcfile Input file - output file name must not match input file
563 extern(D
) FileName
setOutfilename(const(char)[] name
, const(char)[] dir
, const(char)[] arg
, const(char)[] ext
)
565 const(char)[] docfilename
;
572 const(char)[] argdoc
;
574 if (arg
== "__stdin.d")
577 import core
.sys
.posix
.unistd
: getpid
;
578 else version (Windows
)
579 import core
.sys
.windows
.winbase
: getpid
= GetCurrentProcessId
;
580 buf
.printf("__stdin_%d.d", getpid());
583 if (global
.params
.preservePaths
)
586 argdoc
= FileName
.name(arg
);
587 // If argdoc doesn't have an absolute path, make it relative to dir
588 if (!FileName
.absolute(argdoc
))
590 //FileName::ensurePathExists(dir);
591 argdoc
= FileName
.combine(dir
, argdoc
);
593 docfilename
= FileName
.forceExt(argdoc
, ext
);
595 if (FileName
.equals(docfilename
, srcfile
.toString()))
597 error(loc
, "%s `%s` source file and output file have same name '%s'",
598 kind
, toPrettyChars
, srcfile
.toChars());
601 return FileName(docfilename
);
604 extern (D
) void setDocfile()
606 docfile
= setOutfilename(global
.params
.ddoc
.name
, global
.params
.ddoc
.dir
, arg
, doc_ext
);
610 * Trigger the relevant semantic error when a file cannot be read
612 * We special case `object.d` as a failure is likely to be a rare
613 * but difficult to diagnose case for the user. Packages also require
614 * special handling to avoid exposing the compiler's internals.
617 * loc = The location at which the file read originated (e.g. import)
619 private void onFileReadError(const ref Loc loc
)
621 if (FileName
.equals(srcfile
.toString(), "object.d"))
623 .error(loc
, "cannot find source code for runtime library file 'object.d'");
626 errorSupplemental(loc
, "ldc2 might not be correctly installed.");
627 errorSupplemental(loc
, "Please check your ldc2.conf configuration file.");
628 errorSupplemental(loc
, "Installation instructions can be found at http://wiki.dlang.org/LDC.");
632 errorSupplemental(loc
, "dmd might not be correctly installed. Run 'dmd -man' for installation instructions.");
633 const dmdConfFile
= global
.inifilename
.length ? FileName
.canonicalName(global
.inifilename
) : "not found";
634 errorSupplemental(loc
, "config file: %.*s", cast(int)dmdConfFile
.length
, dmdConfFile
.ptr
);
637 else if (FileName
.ext(this.arg
) ||
!loc
.isValid())
639 // Modules whose original argument name has an extension, or do not
640 // have a valid location come from the command-line.
641 // Error that their file cannot be found and return early.
642 .error(loc
, "cannot find input file `%s`", srcfile
.toChars());
646 // if module is not named 'package' but we're trying to read 'package.d', we're looking for a package module
647 bool isPackageMod
= (strcmp(toChars(), "package") != 0) && isPackageFileName(srcfile
);
649 .error(loc
, "importing package '%s' requires a 'package.d' file which cannot be found in '%s'", toChars(), srcfile
.toChars());
652 .error(loc
, "unable to read module `%s`", toChars());
653 const pkgfile
= FileName
.combine(FileName
.removeExt(srcfile
.toString()), package_d
);
654 .errorSupplemental(loc
, "Expected '%s' or '%s' in one of the following import paths:",
655 srcfile
.toChars(), pkgfile
.ptr
);
664 foreach (i
, p
; *global
.path
)
665 fprintf(stderr
, "import path[%llu] = %s\n", cast(ulong)i
, p
);
669 fprintf(stderr
, "Specify path to file '%s' with -I switch\n", srcfile
.toChars());
672 removeHdrFilesAndFail(global
.params
, Module
.amodules
);
677 * Reads the file from `srcfile` and loads the source buffer.
679 * If makefile module dependency is requested, we add this module
680 * to the list of dependencies from here.
685 * Returns: `true` if successful
687 bool read(const ref Loc loc
)
690 return true; // already read
692 //printf("Module::read('%s') file '%s'\n", toChars(), srcfile.toChars());
694 /* Preprocess the file if it's a .c file
696 FileName filename
= srcfile
;
697 bool ifile
= false; // did we generate a .i file
701 File
.remove(filename
.toChars()); // remove generated file
704 if (global
.preprocess
&&
705 FileName
.equalsExt(srcfile
.toString(), c_ext
) &&
706 FileName
.exists(srcfile
.toString()))
708 filename
= global
.preprocess(srcfile
, loc
, ifile
, &defines
); // run C preprocessor
711 if (auto result
= global
.fileManager
.lookup(filename
))
714 if (global
.params
.makeDeps
.doOutput
)
715 global
.params
.makeDeps
.files
.push(srcfile
.toChars());
719 this.onFileReadError(loc
);
726 return parseModule
!ASTCodegen();
730 extern (D
) Module
parseModule(AST
)()
732 const(char)* srcname
= srcfile
.toChars();
733 //printf("Module::parse(srcname = '%s')\n", srcname);
734 isPackageFile
= isPackageFileName(srcfile
);
735 const(char)[] buf
= processSource(src
, this);
736 // an error happened on UTF conversion
737 if (buf
is null) return null;
739 /* If it starts with the string "Ddoc", then it's a documentation
742 if (buf
.length
>= 4 && buf
[0..4] == "Ddoc")
744 comment
= buf
.ptr
+ 4;
745 filetype
= FileType
.ddoc
;
750 /* If it has the extension ".dd", it is also a documentation
751 * source file. Documentation source files may begin with "Ddoc"
752 * but do not have to if they have the .dd extension.
753 * https://issues.dlang.org/show_bug.cgi?id=15465
755 if (FileName
.equalsExt(arg
, dd_ext
))
757 comment
= buf
.ptr
; // the optional Ddoc, if present, is handled above.
758 filetype
= FileType
.ddoc
;
763 /* If it has the extension ".di", it is a "header" file.
765 if (FileName
.equalsExt(arg
, hdr_ext
))
766 filetype
= FileType
.dhdr
;
768 /// Promote `this` to a root module if requested via `-i`
769 void checkCompiledImport()
771 if (!this.isRoot() && Compiler
.onImport(this))
772 this.importedFrom
= this;
776 Package ppack
= null;
778 /* If it has the extension ".c", it is a "C" file.
779 * If it has the extension ".i", it is a preprocessed "C" file.
781 if (FileName
.equalsExt(arg
, c_ext
) || FileName
.equalsExt(arg
, i_ext
))
783 filetype
= FileType
.c
;
785 global
.compileEnv
.masm
= target
.os
== Target
.OS
.Windows
&& !target
.omfobj
; // Microsoft inline assembler format
786 scope p
= new CParser
!AST(this, buf
, cast(bool) docfile
, global
.errorSink
, target
.c
, &defines
, &global
.compileEnv
);
787 global
.compileEnv
.masm
= false;
789 checkCompiledImport();
790 members
= p
.parseModule();
791 assert(!p
.md
); // C doesn't have module declarations
792 numlines
= p
.scanloc
.linnum
;
796 const bool doUnittests
= global
.params
.useUnitTests || global
.params
.ddoc
.doOutput || global
.params
.dihdr
.doOutput
;
797 scope p
= new Parser
!AST(this, buf
, cast(bool) docfile
, global
.errorSink
, &global
.compileEnv
, doUnittests
);
798 p
.transitionIn
= global
.params
.v
.vin
;
800 p
.parseModuleDeclaration();
805 /* A ModuleDeclaration, md, was provided.
806 * The ModuleDeclaration sets the packages this module appears in, and
807 * the name of this module.
810 dst
= Package
.resolve(md
.packages
, &this.parent
, &ppack
);
813 // Done after parsing the module header because `module x.y.z` may override the file name
814 checkCompiledImport();
816 members
= p
.parseModuleContent();
817 numlines
= p
.scanloc
.linnum
;
820 /* The symbol table into which the module is to be inserted.
825 // Mark the package path as accessible from the current module
826 // https://issues.dlang.org/show_bug.cgi?id=21661
827 // Code taken from Import.addPackageAccess()
828 if (md
.packages
.length
> 0)
832 addAccessiblePackage(p
, Visibility(Visibility
.Kind
.private_
));
833 foreach (id
; md
.packages
[1 .. $]) // [b, c]
835 p
= cast(Package
) p
.symtab
.lookup(id
);
838 addAccessiblePackage(p
, Visibility(Visibility
.Kind
.private_
));
842 Module m
= ppack ? ppack
.isModule() : null;
843 if (m
&& !isPackageFileName(m
.srcfile
))
845 .error(md
.loc
, "package name '%s' conflicts with usage as a module name in file %s", ppack
.toPrettyChars(), m
.srcfile
.toChars());
850 /* The name of the module is set to the source file name.
851 * There are no packages.
853 dst
= modules
; // and so this module goes into global module symbol table
854 /* Check to see if module name is a valid identifier
856 if (!Identifier
.isValidIdentifier(this.ident
.toChars()))
857 error(loc
, "%s `%s` has non-identifier characters in filename, use module declaration instead", kind
, toPrettyChars
);
859 // Insert module into the symbol table
863 /* If the source tree is as follows:
867 * the 'pkg' will be incorporated to the internal package tree in two ways:
872 * If both are used in one compilation, 'pkg' as a module (== pkg/package.d)
873 * and a package name 'pkg' will conflict each other.
875 * To avoid the conflict:
876 * 1. If preceding package name insertion had occurred by Package::resolve,
877 * reuse the previous wrapping 'Package' if it exists
878 * 2. Otherwise, 'package.d' wrapped by 'Package' is inserted to the internal tree in here.
880 * Then change Package::isPkgMod to PKG.module_ and set Package::mod.
882 * Note that the 'wrapping Package' is the Package that contains package.d and other submodules,
883 * the one inserted to the symbol table.
885 auto ps
= dst
.lookup(ident
);
886 Package p
= ps ? ps
.isPackage() : null;
889 p
= new Package(Loc
.initial
, ident
);
890 p
.tag
= this.tag
; // reuse the same package tag
891 p
.symtab
= new DsymbolTable();
893 this.tag
= p
.tag
; // reuse the 'older' package tag
895 p
.parent
= this.parent
;
896 p
.isPkgMod
= PKG
.module_
;
902 /* It conflicts with a name that is already in the symbol table.
903 * Figure out what went wrong, and issue error message.
905 Dsymbol prev
= dst
.lookup(ident
);
907 if (Module mprev
= prev
.isModule())
909 if (!FileName
.equals(srcname
, mprev
.srcfile
.toChars()))
910 error(loc
, "%s `%s` from file %s conflicts with another module %s from file %s", kind
, toPrettyChars
, srcname
, mprev
.toChars(), mprev
.srcfile
.toChars());
911 else if (isRoot() && mprev
.isRoot())
912 error(loc
, "%s `%s` from file %s is specified twice on the command line", kind
, toPrettyChars
, srcname
);
914 error(loc
, "%s `%s` from file %s must be imported with 'import %s;'", kind
, toPrettyChars
, srcname
, toPrettyChars());
915 // https://issues.dlang.org/show_bug.cgi?id=14446
916 // Return previously parsed module to avoid AST duplication ICE.
919 else if (Package pkg
= prev
.isPackage())
921 // 'package.d' loaded after a previous 'Package' insertion
923 amodules
.push(this); // Add to global array of all modules
925 error(md ? md
.loc
: loc
, "%s `%s` from file %s conflicts with package name %s", kind
, toPrettyChars
, srcname
, pkg
.toChars());
928 assert(global
.errors
);
932 // Add to global array of all modules
935 Compiler
.onParseModule(this);
939 override void importAll(Scope
* prevsc
)
941 //printf("+Module::importAll(this = %p, '%s'): parent = %p\n", this, toChars(), parent);
943 return; // already done
944 if (filetype
== FileType
.ddoc
)
946 error(loc
, "%s `%s` is a Ddoc file, cannot import it", kind
, toPrettyChars
);
950 /* Note that modules get their own scope, from scratch.
951 * This is so regardless of where in the syntax a module
952 * gets imported, it is unaffected by context.
955 Scope
* sc
= Scope
.createGlobal(this, global
.errorSink
); // create root scope
958 md
.msg
= semanticString(sc
, md
.msg
, "deprecation message");
960 // Add import of "object", even for the "object" module.
961 // If it isn't there, some compiler rewrites, like
962 // classinst == classinst -> .object.opEquals(classinst, classinst)
963 // would fail inside object.d.
964 if (filetype
!= FileType
.c
&&
965 (members
.length
== 0 ||
966 (*members
)[0].ident
!= Id
.object ||
967 (*members
)[0].isImport() is null))
969 auto im
= new Import(Loc
.initial
, null, Id
.object
, null, 0);
974 // Add all symbols into module's symbol table
975 symtab
= new DsymbolTable();
976 for (size_t i
= 0; i
< members
.length
; i
++)
978 Dsymbol s
= (*members
)[i
];
979 s
.addMember(sc
, sc
.scopesym
);
982 // anything else should be run after addMember, so version/debug symbols are defined
983 /* Set scope for the symbols so that if we forward reference
984 * a symbol, it can possibly be resolved on the spot.
985 * If this works out well, it can be extended to all modules
986 * before any semantic() on any of them.
988 setScope(sc
); // remember module scope for semantic
989 for (size_t i
= 0; i
< members
.length
; i
++)
991 Dsymbol s
= (*members
)[i
];
994 for (size_t i
= 0; i
< members
.length
; i
++)
996 Dsymbol s
= (*members
)[i
];
1000 sc
.pop(); // 2 pops because Scope.createGlobal() created 2
1003 /**********************************
1004 * Determine if we need to generate an instance of ModuleInfo
1007 int needModuleInfo()
1009 //printf("needModuleInfo() %s, %d, %d\n", toChars(), needmoduleinfo, global.params.cov);
1010 return needmoduleinfo || global
.params
.cov
;
1013 /*******************************************
1014 * Print deprecation warning if we're deprecated, when
1015 * this module is imported from scope sc.
1018 * sc = the scope into which we are imported
1019 * loc = the location of the import statement
1021 void checkImportDeprecation(const ref Loc loc
, Scope
* sc
)
1023 if (md
&& md
.isdeprecated
&& !sc
.isDeprecated
)
1025 Expression msg
= md
.msg
;
1026 if (StringExp se
= msg ? msg
.toStringExp() : null)
1028 const slice
= se
.peekString();
1031 deprecation(loc
, "%s `%s` is deprecated - %.*s", kind
, toPrettyChars
, cast(int)slice
.length
, slice
.ptr
);
1035 deprecation(loc
, "%s `%s` is deprecated", kind
, toPrettyChars
);
1039 override Dsymbol
search(const ref Loc loc
, Identifier ident
, int flags
= SearchLocalsOnly
)
1041 /* Since modules can be circularly referenced,
1042 * need to stop infinite recursive searches.
1043 * This is done with the cache.
1045 //printf("%s Module.search('%s', flags = x%x) insearch = %d\n", toChars(), ident.toChars(), flags, insearch);
1049 /* Qualified module searches always search their imports,
1050 * even if SearchLocalsOnly
1052 if (!(flags
& SearchUnqualifiedModule
))
1053 flags
&= ~(SearchUnqualifiedModule | SearchLocalsOnly
);
1055 if (searchCacheIdent
== ident
&& searchCacheFlags
== flags
)
1057 //printf("%s Module::search('%s', flags = %d) insearch = %d searchCacheSymbol = %s\n",
1058 // toChars(), ident.toChars(), flags, insearch, searchCacheSymbol ? searchCacheSymbol.toChars() : "null");
1059 return searchCacheSymbol
;
1062 uint errors
= global
.errors
;
1065 Dsymbol s
= ScopeDsymbol
.search(loc
, ident
, flags
);
1068 if (errors
== global
.errors
)
1070 // https://issues.dlang.org/show_bug.cgi?id=10752
1071 // Can cache the result only when it does not cause
1072 // access error so the side-effect should be reproduced in later search.
1073 searchCacheIdent
= ident
;
1074 searchCacheSymbol
= s
;
1075 searchCacheFlags
= flags
;
1080 override bool isPackageAccessible(Package p
, Visibility visibility
, int flags
= 0)
1082 if (insearch
) // don't follow import cycles
1087 if (flags
& IgnorePrivateImports
)
1088 visibility
= Visibility(Visibility
.Kind
.public_
); // only consider public imports
1089 return super.isPackageAccessible(p
, visibility
);
1092 override Dsymbol
symtabInsert(Dsymbol s
)
1094 searchCacheIdent
= null; // symbol is inserted, so invalidate cache
1095 return Package
.symtabInsert(s
);
1098 void deleteObjFile()
1100 if (global
.params
.obj
)
1101 File
.remove(objfile
.toChars());
1103 File
.remove(docfile
.toChars());
1106 /*******************************************
1107 * Can't run semantic on s now, try again later.
1109 extern (D
) static void addDeferredSemantic(Dsymbol s
)
1111 //printf("Module::addDeferredSemantic('%s')\n", s.toChars());
1112 if (!deferred
.contains(s
))
1116 extern (D
) static void addDeferredSemantic2(Dsymbol s
)
1118 //printf("Module::addDeferredSemantic2('%s')\n", s.toChars());
1119 if (!deferred2
.contains(s
))
1123 extern (D
) static void addDeferredSemantic3(Dsymbol s
)
1125 //printf("Module::addDeferredSemantic3('%s')\n", s.toChars());
1126 if (!deferred
.contains(s
))
1130 /******************************************
1131 * Run semantic() on deferred symbols.
1133 static void runDeferredSemantic()
1135 __gshared
int nested
;
1138 //if (deferred.length) printf("+Module::runDeferredSemantic(), len = %ld\n", deferred.length);
1144 len
= deferred
.length
;
1149 Dsymbol
* todoalloc
= null;
1157 todo
= cast(Dsymbol
*)Mem
.check(malloc(len
* Dsymbol
.sizeof
));
1160 memcpy(todo
, deferred
.tdata(), len
* Dsymbol
.sizeof
);
1165 Dsymbol s
= todo
[i
];
1166 s
.dsymbolSemantic(null);
1167 //printf("deferred: %s, parent = %s\n", s.toChars(), s.parent.toChars());
1169 //printf("\tdeferred.length = %ld, len = %ld\n", deferred.length, len);
1173 while (deferred
.length
!= len
); // while making progress
1175 //printf("-Module::runDeferredSemantic(), len = %ld\n", deferred.length);
1178 static void runDeferredSemantic2()
1180 Module
.runDeferredSemantic();
1182 Dsymbols
* a
= &Module
.deferred2
;
1183 for (size_t i
= 0; i
< a
.length
; i
++)
1185 Dsymbol s
= (*a
)[i
];
1186 //printf("[%d] %s semantic2a\n", i, s.toPrettyChars());
1195 static void runDeferredSemantic3()
1197 Module
.runDeferredSemantic2();
1199 Dsymbols
* a
= &Module
.deferred3
;
1200 for (size_t i
= 0; i
< a
.length
; i
++)
1202 Dsymbol s
= (*a
)[i
];
1203 //printf("[%d] %s semantic3a\n", i, s.toPrettyChars());
1212 extern (D
) static void clearCache() nothrow
1214 foreach (Module m
; amodules
)
1215 m
.searchCacheIdent
= null;
1218 /************************************
1219 * Recursively look at every module this module imports,
1220 * return true if it imports m.
1221 * Can be used to detect circular imports.
1223 int imports(Module m
) nothrow
1225 //printf("%s Module::imports(%s)\n", toChars(), m.toChars());
1228 foreach (i
, Module mi
; aimports
)
1229 printf("\t[%d] %s\n", cast(int) i
, mi
.toChars());
1231 foreach (Module mi
; aimports
)
1238 int r
= mi
.imports(m
);
1246 bool isRoot() nothrow
1248 return this.importedFrom
== this;
1251 /// Returns: Whether this module is in the `core` package and has name `ident`
1252 bool isCoreModule(Identifier ident
) nothrow
1254 return this.ident
== ident
&& parent
&& parent
.ident
== Id
.core
&& !parent
.parent
;
1258 int doppelganger
; // sub-module
1259 Symbol
* cov
; // private uint[] __coverage;
1260 uint[] covb
; // bit array of valid code line numbers
1261 Symbol
* sictor
; // module order independent constructor
1262 Symbol
* sctor
; // module constructor
1263 Symbol
* sdtor
; // module destructor
1264 Symbol
* ssharedctor
; // module shared constructor
1265 Symbol
* sshareddtor
; // module shared destructor
1266 Symbol
* stest
; // module unit test
1267 Symbol
* sfilename
; // symbol for filename
1269 uint[uint] ctfe_cov
; /// coverage information from ctfe execution_count[line]
1271 override inout(Module
) isModule() inout nothrow
1276 override void accept(Visitor v
)
1281 /***********************************************
1282 * Writes this module's fully-qualified name to buf
1284 * buf = The buffer to write to
1286 void fullyQualifiedName(ref OutBuffer buf
) nothrow
1288 buf
.writestring(ident
.toString());
1290 for (auto package_
= parent
; package_
!is null; package_
= package_
.parent
)
1292 buf
.prependstring(".");
1293 buf
.prependstring(package_
.ident
.toChars());
1297 /** Lazily initializes and returns the escape table.
1298 Turns out it eats a lot of memory.
1300 extern(D
) Escape
* escapetable() nothrow
1303 _escapetable
= new Escape();
1304 return _escapetable
;
1307 /****************************
1308 * A Singleton that loads core.stdc.config
1310 * Module of core.stdc.config, null if couldn't find it
1312 extern (D
) static Module
loadCoreStdcConfig()
1314 __gshared Module core_stdc_config
;
1315 auto pkgids
= new Identifier
[2];
1316 pkgids
[0] = Id
.core
;
1317 pkgids
[1] = Id
.stdc
;
1318 return loadModuleFromLibrary(core_stdc_config
, pkgids
, Id
.config
);
1321 /****************************
1322 * A Singleton that loads core.atomic
1324 * Module of core.atomic, null if couldn't find it
1326 extern (D
) static Module
loadCoreAtomic()
1328 __gshared Module core_atomic
;
1329 auto pkgids
= new Identifier
[1];
1330 pkgids
[0] = Id
.core
;
1331 return loadModuleFromLibrary(core_atomic
, pkgids
, Id
.atomic
);
1334 /****************************
1335 * A Singleton that loads std.math
1337 * Module of std.math, null if couldn't find it
1339 extern (D
) static Module
loadStdMath()
1341 __gshared Module std_math
;
1342 auto pkgids
= new Identifier
[1];
1344 return loadModuleFromLibrary(std_math
, pkgids
, Id
.math
);
1347 /**********************************
1348 * Load a Module from the library.
1350 * mod = cached return value of this call
1351 * pkgids = package identifiers
1354 * Module loaded, null if cannot load it
1356 extern (D
) private static Module
loadModuleFromLibrary(ref Module mod
, Identifier
[] pkgids
, Identifier modid
)
1361 auto imp
= new Import(Loc
.initial
, pkgids
[], modid
, null, true);
1362 // Module.load will call fatal() if there's no module available.
1363 // Gag the error here, pushing the error handling to the caller.
1364 const errors
= global
.startGagging();
1368 imp
.mod
.importAll(null);
1369 imp
.mod
.dsymbolSemantic(null);
1371 global
.endGagging(errors
);
1377 /***********************************************************
1379 extern (C
++) struct ModuleDeclaration
1383 Identifier
[] packages
; // array of Identifier's representing packages
1384 bool isdeprecated
; // if it is a deprecated module
1387 extern (D
) this(const ref Loc loc
, Identifier
[] packages
, Identifier id
, Expression msg
, bool isdeprecated
) @safe
1390 this.packages
= packages
;
1393 this.isdeprecated
= isdeprecated
;
1396 extern (C
++) const(char)* toChars() const @safe
1399 foreach (pid
; packages
)
1401 buf
.writestring(pid
.toString());
1404 buf
.writestring(id
.toString());
1405 return buf
.extractChars();
1408 /// Provide a human readable representation
1409 extern (D
) const(char)[] toString() const
1411 return this.toChars().toDString
;
1415 /****************************************
1416 * Create array of the local classes in the Module, suitable
1417 * for inclusion in ModuleInfo
1420 * aclasses = array to fill in
1421 * Returns: array of local classes
1423 extern (C
++) void getLocalClasses(Module mod
, ref ClassDeclarations aclasses
)
1425 //printf("members.length = %d\n", mod.members.length);
1426 int pushAddClassDg(size_t n
, Dsymbol sm
)
1431 if (auto cd
= sm
.isClassDeclaration())
1433 // compatibility with previous algorithm
1434 if (cd
.parent
&& cd
.parent
.isTemplateMixin())
1437 if (cd
.classKind
!= ClassKind
.objc
)
1443 ScopeDsymbol
._foreach(null, mod
.members
, &pushAddClassDg
);
1447 * Process the content of a source file
1449 * Attempts to find which encoding it is using, if it has BOM,
1450 * and then normalize the source to UTF-8. If no encoding is required,
1451 * a slice of `src` will be returned without extra allocation.
1454 * src = Content of the source file to process
1455 * mod = Module matching `src`, used for error handling
1458 * UTF-8 encoded variant of `src`, stripped of any BOM,
1459 * or `null` if an error happened.
1461 private const(char)[] processSource (const(ubyte)[] src
, Module mod
)
1463 enum SourceEncoding
{ utf16
, utf32
}
1464 enum Endian
{ little
, big
}
1467 * Convert a buffer from UTF32 to UTF8
1469 * Endian = is the buffer big/little endian
1470 * buf = buffer of UTF32 data
1472 * input buffer reencoded as UTF8
1475 char[] UTF32ToUTF8(Endian endian
)(const(char)[] buf
)
1477 static if (endian
== Endian
.little
)
1478 alias readNext
= Port
.readlongLE
;
1480 alias readNext
= Port
.readlongBE
;
1484 .error(mod
.loc
, "%s `%s` odd length of UTF-32 char source %llu",
1485 mod
.kind
, mod
.toPrettyChars
, cast(ulong) buf
.length
);
1489 const (uint)[] eBuf
= cast(const(uint)[])buf
;
1492 dbuf
.reserve(eBuf
.length
);
1494 foreach (i
; 0 .. eBuf
.length
)
1496 const u
= readNext(&eBuf
[i
]);
1501 .error(mod
.loc
, "%s `%s` UTF-32 value %08x greater than 0x10FFFF", mod
.kind
, mod
.toPrettyChars
, u
);
1509 dbuf
.writeByte(0); //add null terminator
1510 return dbuf
.extractSlice();
1514 * Convert a buffer from UTF16 to UTF8
1516 * Endian = is the buffer big/little endian
1517 * buf = buffer of UTF16 data
1519 * input buffer reencoded as UTF8
1522 char[] UTF16ToUTF8(Endian endian
)(const(char)[] buf
)
1524 static if (endian
== Endian
.little
)
1525 alias readNext
= Port
.readwordLE
;
1527 alias readNext
= Port
.readwordBE
;
1531 .error(mod
.loc
, "%s `%s` odd length of UTF-16 char source %llu", mod
.kind
, mod
.toPrettyChars
, cast(ulong) buf
.length
);
1535 const (ushort)[] eBuf
= cast(const(ushort)[])buf
;
1538 dbuf
.reserve(eBuf
.length
);
1540 //i will be incremented in the loop for high codepoints
1541 foreach (ref i
; 0 .. eBuf
.length
)
1543 uint u
= readNext(&eBuf
[i
]);
1546 if (0xD800 <= u
&& u
< 0xDC00)
1549 if (i
>= eBuf
.length
)
1551 .error(mod
.loc
, "%s `%s` surrogate UTF-16 high value %04x at end of file", mod
.kind
, mod
.toPrettyChars
, u
);
1554 const u2
= readNext(&eBuf
[i
]);
1555 if (u2
< 0xDC00 ||
0xE000 <= u2
)
1557 .error(mod
.loc
, "%s `%s` surrogate UTF-16 low value %04x out of range", mod
.kind
, mod
.toPrettyChars
, u2
);
1560 u
= (u
- 0xD7C0) << 10;
1563 else if (u
>= 0xDC00 && u
<= 0xDFFF)
1565 .error(mod
.loc
, "%s `%s` unpaired surrogate UTF-16 value %04x", mod
.kind
, mod
.toPrettyChars
, u
);
1568 else if (u
== 0xFFFE || u
== 0xFFFF)
1570 .error(mod
.loc
, "%s `%s` illegal UTF-16 value %04x", mod
.kind
, mod
.toPrettyChars
, u
);
1578 dbuf
.writeByte(0); //add a terminating null byte
1579 return dbuf
.extractSlice();
1582 const(char)[] buf
= cast(const(char)[]) src
;
1584 // Assume the buffer is from memory and has not be read from disk. Assume UTF-8.
1588 /* Convert all non-UTF-8 formats to UTF-8.
1589 * BOM : https://www.unicode.org/faq/utf_bom.html
1590 * 00 00 FE FF UTF-32BE, big-endian
1591 * FF FE 00 00 UTF-32LE, little-endian
1592 * FE FF UTF-16BE, big-endian
1593 * FF FE UTF-16LE, little-endian
1596 if (buf
[0] == 0xFF && buf
[1] == 0xFE)
1598 if (buf
.length
>= 4 && buf
[2] == 0 && buf
[3] == 0)
1599 return UTF32ToUTF8
!(Endian
.little
)(buf
[4 .. $]);
1600 return UTF16ToUTF8
!(Endian
.little
)(buf
[2 .. $]);
1603 if (buf
[0] == 0xFE && buf
[1] == 0xFF)
1604 return UTF16ToUTF8
!(Endian
.big
)(buf
[2 .. $]);
1606 if (buf
.length
>= 4 && buf
[0] == 0 && buf
[1] == 0 && buf
[2] == 0xFE && buf
[3] == 0xFF)
1607 return UTF32ToUTF8
!(Endian
.big
)(buf
[4 .. $]);
1609 if (buf
.length
>= 3 && buf
[0] == 0xEF && buf
[1] == 0xBB && buf
[2] == 0xBF)
1612 /* There is no BOM. Make use of Arcane Jill's insight that
1613 * the first char of D source must be ASCII to
1614 * figure out the encoding.
1616 if (buf
.length
>= 4 && buf
[1] == 0 && buf
[2] == 0 && buf
[3] == 0)
1617 return UTF32ToUTF8
!(Endian
.little
)(buf
);
1618 if (buf
.length
>= 4 && buf
[0] == 0 && buf
[1] == 0 && buf
[2] == 0)
1619 return UTF32ToUTF8
!(Endian
.big
)(buf
);
1620 // try to check for UTF-16
1621 if (buf
.length
>= 2 && buf
[1] == 0)
1622 return UTF16ToUTF8
!(Endian
.little
)(buf
);
1624 return UTF16ToUTF8
!(Endian
.big
)(buf
);
1629 auto loc
= mod
.getLoc();
1630 .error(loc
, "%s `%s` source file must start with BOM or ASCII character, not \\x%02X", mod
.kind
, mod
.toPrettyChars
, buf
[0]);