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
;
39 import dmd
.identifier
;
43 import dmd
.root
.array
;
45 import dmd
.root
.filename
;
46 import dmd
.common
.outbuffer
;
49 import dmd
.rootobject
;
50 import dmd
.root
.string
;
58 else version (IN_LLVM
) {}
61 // function used to call semantic3 on a module's dependencies
62 void semantic3OnDependencies(Module m
)
67 if (m
.semanticRun
> PASS
.semantic3
)
72 foreach (i
; 1 .. m
.aimports
.length
)
73 semantic3OnDependencies(m
.aimports
[i
]);
77 * Remove generated .di files on error and exit
79 void removeHdrFilesAndFail(ref Param params
, ref Modules modules
) nothrow
81 if (params
.dihdr
.doOutput
)
85 if (m
.filetype
== FileType
.dhdr
)
87 File
.remove(m
.hdrfile
.toChars());
95 * Converts a chain of identifiers to the filename of the module
98 * packages = the names of the "parent" packages
99 * ident = the name of the child package or module
102 * the filename of the child package or module
104 private const(char)[] getFilename(Identifier
[] packages
, Identifier ident
) nothrow
106 const(char)[] filename
= ident
.toString();
110 auto modAliases
= &global
.params
.modFileAliasStrings
;
112 if (packages
.length
== 0 && modAliases
.length
== 0)
115 void checkModFileAlias(const(char)[] p
)
117 /* Check and replace the contents of buf[] with
118 * an alias string from global.params.modFileAliasStrings[]
120 dotmods
.writestring(p
);
121 foreach_reverse (const m
; *modAliases
)
123 const q
= strchr(m
, '=');
125 if (dotmods
.length
== q
- m
&& memcmp(dotmods
.peekChars(), m
, q
- m
) == 0)
128 auto rhs
= q
[1 .. strlen(q
)];
129 if (rhs
.length
> 0 && (rhs
[$ - 1] == '/' || rhs
[$ - 1] == '\\'))
130 rhs
= rhs
[0 .. $ - 1]; // remove trailing separator
131 buf
.writestring(rhs
);
132 break; // last matching entry in ms[] wins
135 dotmods
.writeByte('.');
138 foreach (pid
; packages
)
140 const p
= pid
.toString();
142 if (modAliases
.length
)
143 checkModFileAlias(p
);
145 enum FileSeparator
= '\\';
147 enum FileSeparator
= '/';
148 buf
.writeByte(FileSeparator
);
150 buf
.writestring(filename
);
151 if (modAliases
.length
)
152 checkModFileAlias(filename
);
154 filename
= buf
.extractSlice()[0 .. $ - 1];
159 /***********************************************************
161 extern (C
++) class Package
: ScopeDsymbol
163 PKG isPkgMod
= PKG
.unknown
;
164 uint tag
; // auto incremented tag, used to mask package tree in scopes
165 Module mod
; // !=null if isPkgMod == PKG.module_
167 final extern (D
) this(const ref Loc loc
, Identifier ident
) nothrow
170 __gshared
uint packageTag
;
171 this.tag
= packageTag
++;
174 override const(char)* kind() const nothrow
179 override bool equals(const RootObject o
) const
181 // custom 'equals' for bug 17441. "package a" and "module a" are not equal
184 auto p
= cast(Package
)o
;
185 return p
&& isModule() == p
.isModule() && ident
.equals(p
.ident
);
188 /****************************************************
190 * packages[] the pkg1.pkg2 of pkg1.pkg2.mod
192 * the symbol table that mod should be inserted into
194 * *pparent the rightmost package, i.e. pkg2, or NULL if no packages
195 * *ppkg the leftmost package, i.e. pkg1, or NULL if no packages
197 extern (D
) static DsymbolTable
resolve(Identifier
[] packages
, Dsymbol
* pparent
, Package
* ppkg
)
199 DsymbolTable dst
= Module
.modules
;
200 Dsymbol parent
= null;
201 //printf("Package::resolve()\n");
204 foreach (pid
; packages
)
207 Dsymbol p
= dst
.lookup(pid
);
210 pkg
= new Package(Loc
.initial
, pid
);
213 pkg
.symtab
= new DsymbolTable();
219 // It might already be a module, not a package, but that needs
220 // to be checked at a higher level, where a nice error message
222 // dot net needs modules and packages with same name
223 // But we still need a symbol table for it
225 pkg
.symtab
= new DsymbolTable();
233 // Return the module so that a nice error message can be generated
235 *ppkg
= cast(Package
)p
;
245 override final inout(Package
) isPackage() inout
251 * Checks if pkg is a sub-package of this
253 * For example, if this qualifies to 'a1.a2' and pkg - to 'a1.a2.a3',
254 * this function returns 'true'. If it is other way around or qualified
255 * package paths conflict function returns 'false'.
258 * pkg = possible subpackage
263 final bool isAncestorPackageOf(const Package pkg
) const
267 if (!pkg ||
!pkg
.parent
)
269 return isAncestorPackageOf(pkg
.parent
.isPackage());
272 override void accept(Visitor v
)
277 final Module
isPackageMod()
279 if (isPkgMod
== PKG
.module_
)
287 * Checks for the existence of a package.d to set isPkgMod appropriately
288 * if isPkgMod == PKG.unknown
290 final void resolvePKGunknown()
294 if (isPkgMod
!= PKG
.unknown
)
297 Identifier
[] packages
;
298 for (Dsymbol s
= this.parent
; s
; s
= s
.parent
)
302 if (Module
.find(getFilename(packages
, ident
)))
303 Module
.load(Loc
.initial
, packages
, this.ident
);
305 isPkgMod
= PKG
.package_
;
309 /***********************************************************
311 extern (C
++) final class Module
: Package
313 extern (C
++) __gshared Module rootModule
;
314 extern (C
++) __gshared DsymbolTable modules
; // symbol table of all modules
315 extern (C
++) __gshared Modules amodules
; // array of all modules
316 extern (C
++) __gshared Dsymbols deferred
; // deferred Dsymbol's needing semantic() run on them
317 extern (C
++) __gshared Dsymbols deferred2
; // deferred Dsymbol's needing semantic2() run on them
318 extern (C
++) __gshared Dsymbols deferred3
; // deferred Dsymbol's needing semantic3() run on them
322 modules
= new DsymbolTable();
326 * Deinitializes the global state of the compiler.
328 * This can be used to restore the state set by `_init` to its original
331 static void deinitialize()
333 modules
= modules
.init
;
336 extern (C
++) __gshared AggregateDeclaration moduleinfo
;
338 const(char)[] arg
; // original argument name
339 ModuleDeclaration
* md
; // if !=null, the contents of the ModuleDeclaration declaration
340 const FileName srcfile
; // input source file
341 const FileName objfile
; // output .obj file
342 const FileName hdrfile
; // 'header' file
343 FileName docfile
; // output documentation file
344 const(ubyte)[] src
; /// Raw content of the file
345 uint errors
; // if any errors in file
346 uint numlines
; // number of lines in source file
347 FileType filetype
; // source file type
348 bool hasAlwaysInlines
; // contains references to functions that must be inlined
349 bool isPackageFile
; // if it is a package.d
350 Package pkg
; // if isPackageFile is true, the Package that contains this package.d
351 Strings contentImportedFiles
; // array of files whose content was imported
353 private ThreeState selfimports
;
354 private ThreeState rootimports
;
355 Dsymbol
[void*] tagSymTab
; /// ImportC: tag symbols that conflict with other symbols used as the index
357 private OutBuffer defines
; // collect all the #define lines here
360 /*************************************
361 * Return true if module imports itself.
365 //printf("Module::selfImports() %s\n", toChars());
366 if (selfimports
== ThreeState
.none
)
368 foreach (Module m
; amodules
)
370 selfimports
= imports(this) ? ThreeState
.yes
: ThreeState
.no
;
371 foreach (Module m
; amodules
)
374 return selfimports
== ThreeState
.yes
;
377 /*************************************
378 * Return true if module imports root module.
382 //printf("Module::rootImports() %s\n", toChars());
383 if (rootimports
== ThreeState
.none
)
385 foreach (Module m
; amodules
)
387 rootimports
= ThreeState
.no
;
388 foreach (Module m
; amodules
)
390 if (m
.isRoot() && imports(m
))
392 rootimports
= ThreeState
.yes
;
396 foreach (Module m
; amodules
)
399 return rootimports
== ThreeState
.yes
;
402 Identifier searchCacheIdent
;
403 Dsymbol searchCacheSymbol
; // cached value of search
404 SearchOptFlags searchCacheFlags
; // cached flags
408 * A root module is one that will be compiled all the way to
409 * object code. This field holds the root module that caused
410 * this module to be loaded. If this module is a root module,
411 * then it will be set to `this`. This is used to determine
412 * ownership of template instantiation.
416 Dsymbols
* decldefs
; // top level declarations for this Module
418 Modules aimports
; // all imported modules
420 uint debuglevel
; // debug level
421 Identifiers
* debugids
; // debug identifiers
422 Identifiers
* debugidsNot
; // forward referenced debug identifiers
424 uint versionlevel
; // version level
425 Identifiers
* versionids
; // version identifiers
426 Identifiers
* versionidsNot
; // forward referenced version identifiers
428 MacroTable macrotable
; // document comment macros
429 Escape
* _escapetable
; // document comment escapes
431 size_t nameoffset
; // offset of module name from start of ModuleInfo
432 size_t namelen
; // length of module name in characters
434 extern (D
) this(const ref Loc loc
, const(char)[] filename
, Identifier ident
, int doDocComment
, int doHdrGen
)
437 const(char)[] srcfilename
;
438 //printf("Module::Module(filename = '%.*s', ident = '%s')\n", cast(int)filename.length, filename.ptr, ident.toChars());
440 srcfilename
= FileName
.defaultExt(filename
, mars_ext
);
441 if (target
.run_noext
&& global
.params
.run
&&
442 !FileName
.ext(filename
) &&
443 FileName
.exists(srcfilename
) == 0 &&
444 FileName
.exists(filename
) == 1)
446 FileName
.free(srcfilename
.ptr
);
447 srcfilename
= FileName
.removeExt(filename
); // just does a mem.strdup(filename)
449 else if (!FileName
.equalsExt(srcfilename
, mars_ext
) &&
450 !FileName
.equalsExt(srcfilename
, hdr_ext
) &&
451 !FileName
.equalsExt(srcfilename
, c_ext
) &&
452 !FileName
.equalsExt(srcfilename
, i_ext
) &&
453 !FileName
.equalsExt(srcfilename
, dd_ext
))
456 error(loc
, "%s `%s` source file name '%.*s' must have .%.*s extension",
458 cast(int)srcfilename
.length
, srcfilename
.ptr
,
459 cast(int)mars_ext
.length
, mars_ext
.ptr
);
463 srcfile
= FileName(srcfilename
);
464 objfile
= setOutfilename(global
.params
.objname
, global
.params
.objdir
, filename
, target
.obj_ext
);
468 hdrfile
= setOutfilename(global
.params
.dihdr
.name
, global
.params
.dihdr
.dir
, arg
, hdr_ext
);
471 extern (D
) this(const(char)[] filename
, Identifier ident
, int doDocComment
, int doHdrGen
)
473 this(Loc
.initial
, filename
, ident
, doDocComment
, doHdrGen
);
476 static Module
create(const(char)* filename
, Identifier ident
, int doDocComment
, int doHdrGen
)
478 return create(filename
.toDString
, ident
, doDocComment
, doHdrGen
);
481 extern (D
) static Module
create(const(char)[] filename
, Identifier ident
, int doDocComment
, int doHdrGen
)
483 return new Module(Loc
.initial
, filename
, ident
, doDocComment
, doHdrGen
);
486 static const(char)* find(const(char)* filename
)
488 return find(filename
.toDString
).ptr
;
491 extern (D
) static const(char)[] find(const(char)[] filename
)
493 return global
.fileManager
.lookForSourceFile(filename
, global
.path ?
(*global
.path
)[] : null);
496 extern (C
++) static Module
load(const ref Loc loc
, Identifiers
* packages
, Identifier ident
)
498 return load(loc
, packages ?
(*packages
)[] : null, ident
);
501 extern (D
) static Module
load(const ref Loc loc
, Identifier
[] packages
, Identifier ident
)
503 //printf("Module::load(ident = '%s')\n", ident.toChars());
504 // Build module filename by turning:
508 const(char)[] filename
= getFilename(packages
, ident
);
509 // Look for the source file
510 if (const result
= find(filename
))
511 filename
= result
; // leaks
513 auto m
= new Module(loc
, filename
, ident
, 0, 0);
517 if (global
.params
.v
.verbose
)
520 foreach (pid
; packages
)
522 buf
.writestring(pid
.toString());
525 buf
.printf("%s\t(%s)", ident
.toChars(), m
.srcfile
.toChars());
526 message("import %s", buf
.peekChars());
528 if((m
= m
.parse()) is null) return null;
533 override const(char)* kind() const
538 /*********************************************
539 * Combines things into output file name for .html and .di files.
541 * name Command line name given for the file, NULL if none
542 * dir Command line directory given for the file, NULL if none
543 * arg Name of the source file
544 * ext File name extension to use if 'name' is NULL
545 * global.params.preservePaths get output path from arg
546 * srcfile Input file - output file name must not match input file
548 extern(D
) FileName
setOutfilename(const(char)[] name
, const(char)[] dir
, const(char)[] arg
, const(char)[] ext
)
550 const(char)[] docfilename
;
557 const(char)[] argdoc
;
559 if (arg
== "__stdin.d")
562 import core
.sys
.posix
.unistd
: getpid
;
563 else version (Windows
)
564 import core
.sys
.windows
.winbase
: getpid
= GetCurrentProcessId
;
565 buf
.printf("__stdin_%d.d", getpid());
568 if (global
.params
.preservePaths
)
571 argdoc
= FileName
.name(arg
);
572 // If argdoc doesn't have an absolute path, make it relative to dir
573 if (!FileName
.absolute(argdoc
))
575 //FileName::ensurePathExists(dir);
576 argdoc
= FileName
.combine(dir
, argdoc
);
578 docfilename
= FileName
.forceExt(argdoc
, ext
);
580 if (FileName
.equals(docfilename
, srcfile
.toString()))
582 error(loc
, "%s `%s` source file and output file have same name '%s'",
583 kind
, toPrettyChars
, srcfile
.toChars());
586 return FileName(docfilename
);
589 extern (D
) void setDocfile()
591 docfile
= setOutfilename(global
.params
.ddoc
.name
, global
.params
.ddoc
.dir
, arg
, doc_ext
);
595 * Trigger the relevant semantic error when a file cannot be read
597 * We special case `object.d` as a failure is likely to be a rare
598 * but difficult to diagnose case for the user. Packages also require
599 * special handling to avoid exposing the compiler's internals.
602 * loc = The location at which the file read originated (e.g. import)
604 private void onFileReadError(const ref Loc loc
)
606 if (FileName
.equals(srcfile
.toString(), "object.d"))
608 .error(loc
, "cannot find source code for runtime library file 'object.d'");
611 errorSupplemental(loc
, "ldc2 might not be correctly installed.");
612 errorSupplemental(loc
, "Please check your ldc2.conf configuration file.");
613 errorSupplemental(loc
, "Installation instructions can be found at http://wiki.dlang.org/LDC.");
617 errorSupplemental(loc
, "dmd might not be correctly installed. Run 'dmd -man' for installation instructions.");
618 const dmdConfFile
= global
.inifilename
.length ? FileName
.canonicalName(global
.inifilename
) : "not found";
619 errorSupplemental(loc
, "config file: %.*s", cast(int)dmdConfFile
.length
, dmdConfFile
.ptr
);
622 else if (FileName
.ext(this.arg
) ||
!loc
.isValid())
624 // Modules whose original argument name has an extension, or do not
625 // have a valid location come from the command-line.
626 // Error that their file cannot be found and return early.
627 .error(loc
, "cannot find input file `%s`", srcfile
.toChars());
631 // if module is not named 'package' but we're trying to read 'package.d', we're looking for a package module
632 bool isPackageMod
= (strcmp(toChars(), "package") != 0) && isPackageFileName(srcfile
);
634 .error(loc
, "importing package '%s' requires a 'package.d' file which cannot be found in '%s'", toChars(), srcfile
.toChars());
637 .error(loc
, "unable to read module `%s`", toChars());
638 const pkgfile
= FileName
.combine(FileName
.removeExt(srcfile
.toString()), package_d
);
639 .errorSupplemental(loc
, "Expected '%s' or '%s' in one of the following import paths:",
640 srcfile
.toChars(), pkgfile
.ptr
);
649 foreach (i
, p
; *global
.path
)
650 fprintf(stderr
, "import path[%llu] = %s\n", cast(ulong)i
, p
);
654 fprintf(stderr
, "Specify path to file '%s' with -I switch\n", srcfile
.toChars());
657 removeHdrFilesAndFail(global
.params
, Module
.amodules
);
662 * Reads the file from `srcfile` and loads the source buffer.
664 * If makefile module dependency is requested, we add this module
665 * to the list of dependencies from here.
670 * Returns: `true` if successful
672 bool read(const ref Loc loc
)
675 return true; // already read
677 //printf("Module::read('%s') file '%s'\n", toChars(), srcfile.toChars());
679 /* Preprocess the file if it's a .c file
681 FileName filename
= srcfile
;
682 bool ifile
= false; // did we generate a .i file
686 File
.remove(filename
.toChars()); // remove generated file
689 if (global
.preprocess
&&
690 FileName
.equalsExt(srcfile
.toString(), c_ext
) &&
691 FileName
.exists(srcfile
.toString()))
693 filename
= global
.preprocess(srcfile
, loc
, ifile
, &defines
); // run C preprocessor
696 if (auto result
= global
.fileManager
.lookup(filename
))
699 if (global
.params
.makeDeps
.doOutput
)
700 global
.params
.makeDeps
.files
.push(srcfile
.toChars());
704 this.onFileReadError(loc
);
711 return parseModule
!ASTCodegen();
715 extern (D
) Module
parseModule(AST
)()
717 const(char)* srcname
= srcfile
.toChars();
718 //printf("Module::parse(srcname = '%s')\n", srcname);
719 isPackageFile
= isPackageFileName(srcfile
);
720 const(char)[] buf
= processSource(src
, this);
721 // an error happened on UTF conversion
722 if (buf
is null) return null;
724 /* If it starts with the string "Ddoc", then it's a documentation
727 if (buf
.length
>= 4 && buf
[0..4] == "Ddoc")
729 comment
= buf
.ptr
+ 4;
730 filetype
= FileType
.ddoc
;
735 /* If it has the extension ".dd", it is also a documentation
736 * source file. Documentation source files may begin with "Ddoc"
737 * but do not have to if they have the .dd extension.
738 * https://issues.dlang.org/show_bug.cgi?id=15465
740 if (FileName
.equalsExt(arg
, dd_ext
))
742 comment
= buf
.ptr
; // the optional Ddoc, if present, is handled above.
743 filetype
= FileType
.ddoc
;
748 /* If it has the extension ".di", it is a "header" file.
750 if (FileName
.equalsExt(arg
, hdr_ext
))
751 filetype
= FileType
.dhdr
;
753 /// Promote `this` to a root module if requested via `-i`
754 void checkCompiledImport()
756 if (!this.isRoot() && Compiler
.onImport(this))
757 this.importedFrom
= this;
761 Package ppack
= null;
763 /* If it has the extension ".c", it is a "C" file.
764 * If it has the extension ".i", it is a preprocessed "C" file.
766 if (FileName
.equalsExt(arg
, c_ext
) || FileName
.equalsExt(arg
, i_ext
))
768 filetype
= FileType
.c
;
770 global
.compileEnv
.masm
= target
.os
== Target
.OS
.Windows
&& !target
.omfobj
; // Microsoft inline assembler format
771 scope p
= new CParser
!AST(this, buf
, cast(bool) docfile
, global
.errorSink
, target
.c
, &defines
, &global
.compileEnv
);
772 global
.compileEnv
.masm
= false;
774 checkCompiledImport();
775 members
= p
.parseModule();
776 assert(!p
.md
); // C doesn't have module declarations
777 numlines
= p
.scanloc
.linnum
;
781 const bool doUnittests
= global
.params
.parsingUnittestsRequired();
782 scope p
= new Parser
!AST(this, buf
, cast(bool) docfile
, global
.errorSink
, &global
.compileEnv
, doUnittests
);
783 p
.transitionIn
= global
.params
.v
.vin
;
785 p
.parseModuleDeclaration();
790 /* A ModuleDeclaration, md, was provided.
791 * The ModuleDeclaration sets the packages this module appears in, and
792 * the name of this module.
795 dst
= Package
.resolve(md
.packages
, &this.parent
, &ppack
);
798 // Done after parsing the module header because `module x.y.z` may override the file name
799 checkCompiledImport();
801 members
= p
.parseModuleContent();
802 numlines
= p
.scanloc
.linnum
;
805 /* The symbol table into which the module is to be inserted.
810 // Mark the package path as accessible from the current module
811 // https://issues.dlang.org/show_bug.cgi?id=21661
812 // Code taken from Import.addPackageAccess()
813 if (md
.packages
.length
> 0)
817 addAccessiblePackage(p
, Visibility(Visibility
.Kind
.private_
));
818 foreach (id
; md
.packages
[1 .. $]) // [b, c]
820 p
= cast(Package
) p
.symtab
.lookup(id
);
823 addAccessiblePackage(p
, Visibility(Visibility
.Kind
.private_
));
827 Module m
= ppack ? ppack
.isModule() : null;
828 if (m
&& !isPackageFileName(m
.srcfile
))
830 .error(md
.loc
, "package name '%s' conflicts with usage as a module name in file %s", ppack
.toPrettyChars(), m
.srcfile
.toChars());
835 /* The name of the module is set to the source file name.
836 * There are no packages.
838 dst
= modules
; // and so this module goes into global module symbol table
839 /* Check to see if module name is a valid identifier
841 if (!Identifier
.isValidIdentifier(this.ident
.toChars()))
842 error(loc
, "%s `%s` has non-identifier characters in filename, use module declaration instead", kind
, toPrettyChars
);
844 // Insert module into the symbol table
848 /* If the source tree is as follows:
852 * the 'pkg' will be incorporated to the internal package tree in two ways:
857 * If both are used in one compilation, 'pkg' as a module (== pkg/package.d)
858 * and a package name 'pkg' will conflict each other.
860 * To avoid the conflict:
861 * 1. If preceding package name insertion had occurred by Package::resolve,
862 * reuse the previous wrapping 'Package' if it exists
863 * 2. Otherwise, 'package.d' wrapped by 'Package' is inserted to the internal tree in here.
865 * Then change Package::isPkgMod to PKG.module_ and set Package::mod.
867 * Note that the 'wrapping Package' is the Package that contains package.d and other submodules,
868 * the one inserted to the symbol table.
870 auto ps
= dst
.lookup(ident
);
871 Package p
= ps ? ps
.isPackage() : null;
874 p
= new Package(Loc
.initial
, ident
);
875 p
.tag
= this.tag
; // reuse the same package tag
876 p
.symtab
= new DsymbolTable();
878 this.tag
= p
.tag
; // reuse the 'older' package tag
880 p
.parent
= this.parent
;
881 p
.isPkgMod
= PKG
.module_
;
887 /* It conflicts with a name that is already in the symbol table.
888 * Figure out what went wrong, and issue error message.
890 Dsymbol prev
= dst
.lookup(ident
);
892 if (Module mprev
= prev
.isModule())
894 if (!FileName
.equals(srcname
, mprev
.srcfile
.toChars()))
895 error(loc
, "%s `%s` from file %s conflicts with another module %s from file %s", kind
, toPrettyChars
, srcname
, mprev
.toChars(), mprev
.srcfile
.toChars());
896 else if (isRoot() && mprev
.isRoot())
897 error(loc
, "%s `%s` from file %s is specified twice on the command line", kind
, toPrettyChars
, srcname
);
899 error(loc
, "%s `%s` from file %s must be imported with 'import %s;'", kind
, toPrettyChars
, srcname
, toPrettyChars());
900 // https://issues.dlang.org/show_bug.cgi?id=14446
901 // Return previously parsed module to avoid AST duplication ICE.
904 else if (Package pkg
= prev
.isPackage())
906 // 'package.d' loaded after a previous 'Package' insertion
908 amodules
.push(this); // Add to global array of all modules
910 error(md ? md
.loc
: loc
, "%s `%s` from file %s conflicts with package name %s", kind
, toPrettyChars
, srcname
, pkg
.toChars());
913 assert(global
.errors
);
917 // Add to global array of all modules
920 Compiler
.onParseModule(this);
924 /**********************************
925 * Determine if we need to generate an instance of ModuleInfo
930 //printf("needModuleInfo() %s, %d, %d\n", toChars(), needmoduleinfo, global.params.cov);
931 return needmoduleinfo || global
.params
.cov
;
934 /*******************************************
935 * Print deprecation warning if we're deprecated, when
936 * this module is imported from scope sc.
939 * sc = the scope into which we are imported
940 * loc = the location of the import statement
942 void checkImportDeprecation(const ref Loc loc
, Scope
* sc
)
944 if (md
&& md
.isdeprecated
&& !sc
.isDeprecated
)
946 Expression msg
= md
.msg
;
947 if (StringExp se
= msg ? msg
.toStringExp() : null)
949 const slice
= se
.peekString();
952 deprecation(loc
, "%s `%s` is deprecated - %.*s", kind
, toPrettyChars
, cast(int)slice
.length
, slice
.ptr
);
956 deprecation(loc
, "%s `%s` is deprecated", kind
, toPrettyChars
);
960 override bool isPackageAccessible(Package p
, Visibility visibility
, SearchOptFlags flags
= SearchOpt
.all
)
962 if (insearch
) // don't follow import cycles
967 if (flags
& SearchOpt
.ignorePrivateImports
)
968 visibility
= Visibility(Visibility
.Kind
.public_
); // only consider public imports
969 return super.isPackageAccessible(p
, visibility
);
972 override Dsymbol
symtabInsert(Dsymbol s
)
974 searchCacheIdent
= null; // symbol is inserted, so invalidate cache
975 return Package
.symtabInsert(s
);
978 extern (D
) void deleteObjFile()
980 if (global
.params
.obj
)
981 File
.remove(objfile
.toChars());
983 File
.remove(docfile
.toChars());
986 /*******************************************
987 * Can't run semantic on s now, try again later.
989 extern (D
) static void addDeferredSemantic(Dsymbol s
)
991 //printf("Module::addDeferredSemantic('%s')\n", s.toChars());
992 if (!deferred
.contains(s
))
996 extern (D
) static void addDeferredSemantic2(Dsymbol s
)
998 //printf("Module::addDeferredSemantic2('%s')\n", s.toChars());
999 if (!deferred2
.contains(s
))
1003 extern (D
) static void addDeferredSemantic3(Dsymbol s
)
1005 //printf("Module::addDeferredSemantic3('%s')\n", s.toChars());
1006 if (!deferred
.contains(s
))
1010 /******************************************
1011 * Run semantic() on deferred symbols.
1013 static void runDeferredSemantic()
1015 __gshared
int nested
;
1018 //if (deferred.length) printf("+Module::runDeferredSemantic(), len = %ld\n", deferred.length);
1024 len
= deferred
.length
;
1029 Dsymbol
* todoalloc
= null;
1037 todo
= cast(Dsymbol
*)Mem
.check(malloc(len
* Dsymbol
.sizeof
));
1040 memcpy(todo
, deferred
.tdata(), len
* Dsymbol
.sizeof
);
1045 Dsymbol s
= todo
[i
];
1046 s
.dsymbolSemantic(null);
1047 //printf("deferred: %s, parent = %s\n", s.toChars(), s.parent.toChars());
1049 //printf("\tdeferred.length = %ld, len = %ld\n", deferred.length, len);
1053 while (deferred
.length
!= len
); // while making progress
1055 //printf("-Module::runDeferredSemantic(), len = %ld\n", deferred.length);
1058 static void runDeferredSemantic2()
1060 Module
.runDeferredSemantic();
1062 Dsymbols
* a
= &Module
.deferred2
;
1063 for (size_t i
= 0; i
< a
.length
; i
++)
1065 Dsymbol s
= (*a
)[i
];
1066 //printf("[%d] %s semantic2a\n", i, s.toPrettyChars());
1075 static void runDeferredSemantic3()
1077 Module
.runDeferredSemantic2();
1079 Dsymbols
* a
= &Module
.deferred3
;
1080 for (size_t i
= 0; i
< a
.length
; i
++)
1082 Dsymbol s
= (*a
)[i
];
1083 //printf("[%d] %s semantic3a\n", i, s.toPrettyChars());
1092 extern (D
) static void clearCache() nothrow
1094 foreach (Module m
; amodules
)
1095 m
.searchCacheIdent
= null;
1098 /************************************
1099 * Recursively look at every module this module imports,
1100 * return true if it imports m.
1101 * Can be used to detect circular imports.
1103 int imports(Module m
) nothrow
1105 //printf("%s Module::imports(%s)\n", toChars(), m.toChars());
1108 foreach (i
, Module mi
; aimports
)
1109 printf("\t[%d] %s\n", cast(int) i
, mi
.toChars());
1111 foreach (Module mi
; aimports
)
1118 int r
= mi
.imports(m
);
1126 bool isRoot() nothrow
1128 return this.importedFrom
== this;
1131 /// Returns: Whether this module is in the `core` package and has name `ident`
1132 bool isCoreModule(Identifier ident
) nothrow
1134 return this.ident
== ident
&& parent
&& parent
.ident
== Id
.core
&& !parent
.parent
;
1138 int doppelganger
; // sub-module
1139 Symbol
* cov
; // private uint[] __coverage;
1140 uint[] covb
; // bit array of valid code line numbers
1141 Symbol
* sictor
; // module order independent constructor
1142 Symbol
* sctor
; // module constructor
1143 Symbol
* sdtor
; // module destructor
1144 Symbol
* ssharedctor
; // module shared constructor
1145 Symbol
* sshareddtor
; // module shared destructor
1146 Symbol
* stest
; // module unit test
1147 Symbol
* sfilename
; // symbol for filename
1149 uint[uint] ctfe_cov
; /// coverage information from ctfe execution_count[line]
1151 override inout(Module
) isModule() inout nothrow
1156 override void accept(Visitor v
)
1161 /***********************************************
1162 * Writes this module's fully-qualified name to buf
1164 * buf = The buffer to write to
1166 void fullyQualifiedName(ref OutBuffer buf
) nothrow
1168 buf
.writestring(ident
.toString());
1170 for (auto package_
= parent
; package_
!is null; package_
= package_
.parent
)
1172 buf
.prependstring(".");
1173 buf
.prependstring(package_
.ident
.toChars());
1177 /** Lazily initializes and returns the escape table.
1178 Turns out it eats a lot of memory.
1180 extern(D
) Escape
* escapetable() nothrow
1183 _escapetable
= new Escape();
1184 return _escapetable
;
1187 /****************************
1188 * A Singleton that loads core.stdc.config
1190 * Module of core.stdc.config, null if couldn't find it
1192 extern (D
) static Module
loadCoreStdcConfig()
1194 __gshared Module core_stdc_config
;
1195 auto pkgids
= new Identifier
[2];
1196 pkgids
[0] = Id
.core
;
1197 pkgids
[1] = Id
.stdc
;
1198 return loadModuleFromLibrary(core_stdc_config
, pkgids
, Id
.config
);
1201 /****************************
1202 * A Singleton that loads core.atomic
1204 * Module of core.atomic, null if couldn't find it
1206 extern (D
) static Module
loadCoreAtomic()
1208 __gshared Module core_atomic
;
1209 auto pkgids
= new Identifier
[1];
1210 pkgids
[0] = Id
.core
;
1211 return loadModuleFromLibrary(core_atomic
, pkgids
, Id
.atomic
);
1214 /****************************
1215 * A Singleton that loads std.math
1217 * Module of std.math, null if couldn't find it
1219 extern (D
) static Module
loadStdMath()
1221 __gshared Module std_math
;
1222 auto pkgids
= new Identifier
[1];
1224 return loadModuleFromLibrary(std_math
, pkgids
, Id
.math
);
1227 /**********************************
1228 * Load a Module from the library.
1230 * mod = cached return value of this call
1231 * pkgids = package identifiers
1234 * Module loaded, null if cannot load it
1236 extern (D
) private static Module
loadModuleFromLibrary(ref Module mod
, Identifier
[] pkgids
, Identifier modid
)
1241 auto imp
= new Import(Loc
.initial
, pkgids
[], modid
, null, true);
1242 // Module.load will call fatal() if there's no module available.
1243 // Gag the error here, pushing the error handling to the caller.
1244 const errors
= global
.startGagging();
1248 imp
.mod
.importAll(null);
1249 imp
.mod
.dsymbolSemantic(null);
1251 global
.endGagging(errors
);
1257 /***********************************************************
1259 extern (C
++) struct ModuleDeclaration
1263 Identifier
[] packages
; // array of Identifier's representing packages
1264 bool isdeprecated
; // if it is a deprecated module
1267 extern (D
) this(const ref Loc loc
, Identifier
[] packages
, Identifier id
, Expression msg
, bool isdeprecated
) @safe
1270 this.packages
= packages
;
1273 this.isdeprecated
= isdeprecated
;
1276 extern (C
++) const(char)* toChars() const @safe
1279 foreach (pid
; packages
)
1281 buf
.writestring(pid
.toString());
1284 buf
.writestring(id
.toString());
1285 return buf
.extractChars();
1288 /// Provide a human readable representation
1289 extern (D
) const(char)[] toString() const
1291 return this.toChars().toDString
;
1295 /****************************************
1296 * Create array of the local classes in the Module, suitable
1297 * for inclusion in ModuleInfo
1300 * aclasses = array to fill in
1301 * Returns: array of local classes
1303 extern (C
++) void getLocalClasses(Module mod
, ref ClassDeclarations aclasses
)
1305 //printf("members.length = %d\n", mod.members.length);
1306 int pushAddClassDg(size_t n
, Dsymbol sm
)
1311 if (auto cd
= sm
.isClassDeclaration())
1313 // compatibility with previous algorithm
1314 if (cd
.parent
&& cd
.parent
.isTemplateMixin())
1317 if (cd
.classKind
!= ClassKind
.objc
)
1323 _foreach(null, mod
.members
, &pushAddClassDg
);
1327 alias ForeachDg
= int delegate(size_t idx
, Dsymbol s
);
1329 /***************************************
1330 * Expands attribute declarations in members in depth first
1331 * order. Calls dg(size_t symidx, Dsymbol *sym) for each
1333 * If dg returns !=0, stops and returns that value else returns 0.
1334 * Use this function to avoid the O(N + N^2/2) complexity of
1335 * calculating dim and calling N times getNth.
1337 * last value returned by dg()
1339 int _foreach(Scope
* sc
, Dsymbols
* members
, scope ForeachDg dg
, size_t
* pn
= null)
1344 size_t n
= pn ?
*pn
: 0; // take over index
1346 foreach (size_t i
; 0 .. members
.length
)
1348 import dmd
.attrib
: AttribDeclaration
;
1349 import dmd
.dtemplate
: TemplateMixin
;
1351 Dsymbol s
= (*members
)[i
];
1352 if (AttribDeclaration a
= s
.isAttribDeclaration())
1353 result
= _foreach(sc
, a
.include(sc
), dg
, &n
);
1354 else if (TemplateMixin tm
= s
.isTemplateMixin())
1355 result
= _foreach(sc
, tm
.members
, dg
, &n
);
1356 else if (s
.isTemplateInstance())
1359 else if (s
.isUnitTestDeclaration())
1363 result
= dg(n
++, s
);
1368 *pn
= n
; // update index
1373 * Process the content of a source file
1375 * Attempts to find which encoding it is using, if it has BOM,
1376 * and then normalize the source to UTF-8. If no encoding is required,
1377 * a slice of `src` will be returned without extra allocation.
1380 * src = Content of the source file to process
1381 * mod = Module matching `src`, used for error handling
1384 * UTF-8 encoded variant of `src`, stripped of any BOM,
1385 * or `null` if an error happened.
1387 private const(char)[] processSource (const(ubyte)[] src
, Module mod
)
1389 enum SourceEncoding
{ utf16
, utf32
}
1390 enum Endian
{ little
, big
}
1393 * Convert a buffer from UTF32 to UTF8
1395 * Endian = is the buffer big/little endian
1396 * buf = buffer of UTF32 data
1398 * input buffer reencoded as UTF8
1401 char[] UTF32ToUTF8(Endian endian
)(const(char)[] buf
)
1403 static if (endian
== Endian
.little
)
1404 alias readNext
= Port
.readlongLE
;
1406 alias readNext
= Port
.readlongBE
;
1410 .error(mod
.loc
, "%s `%s` odd length of UTF-32 char source %llu",
1411 mod
.kind
, mod
.toPrettyChars
, cast(ulong) buf
.length
);
1415 const (uint)[] eBuf
= cast(const(uint)[])buf
;
1418 dbuf
.reserve(eBuf
.length
);
1420 foreach (i
; 0 .. eBuf
.length
)
1422 const u
= readNext(&eBuf
[i
]);
1427 .error(mod
.loc
, "%s `%s` UTF-32 value %08x greater than 0x10FFFF", mod
.kind
, mod
.toPrettyChars
, u
);
1435 dbuf
.writeByte(0); //add null terminator
1436 return dbuf
.extractSlice();
1440 * Convert a buffer from UTF16 to UTF8
1442 * Endian = is the buffer big/little endian
1443 * buf = buffer of UTF16 data
1445 * input buffer reencoded as UTF8
1448 char[] UTF16ToUTF8(Endian endian
)(const(char)[] buf
)
1450 static if (endian
== Endian
.little
)
1451 alias readNext
= Port
.readwordLE
;
1453 alias readNext
= Port
.readwordBE
;
1457 .error(mod
.loc
, "%s `%s` odd length of UTF-16 char source %llu", mod
.kind
, mod
.toPrettyChars
, cast(ulong) buf
.length
);
1461 const (ushort)[] eBuf
= cast(const(ushort)[])buf
;
1464 dbuf
.reserve(eBuf
.length
);
1466 //i will be incremented in the loop for high codepoints
1467 foreach (ref i
; 0 .. eBuf
.length
)
1469 uint u
= readNext(&eBuf
[i
]);
1472 if (0xD800 <= u
&& u
< 0xDC00)
1475 if (i
>= eBuf
.length
)
1477 .error(mod
.loc
, "%s `%s` surrogate UTF-16 high value %04x at end of file", mod
.kind
, mod
.toPrettyChars
, u
);
1480 const u2
= readNext(&eBuf
[i
]);
1481 if (u2
< 0xDC00 ||
0xE000 <= u2
)
1483 .error(mod
.loc
, "%s `%s` surrogate UTF-16 low value %04x out of range", mod
.kind
, mod
.toPrettyChars
, u2
);
1486 u
= (u
- 0xD7C0) << 10;
1489 else if (u
>= 0xDC00 && u
<= 0xDFFF)
1491 .error(mod
.loc
, "%s `%s` unpaired surrogate UTF-16 value %04x", mod
.kind
, mod
.toPrettyChars
, u
);
1494 else if (u
== 0xFFFE || u
== 0xFFFF)
1496 .error(mod
.loc
, "%s `%s` illegal UTF-16 value %04x", mod
.kind
, mod
.toPrettyChars
, u
);
1504 dbuf
.writeByte(0); //add a terminating null byte
1505 return dbuf
.extractSlice();
1508 const(char)[] buf
= cast(const(char)[]) src
;
1510 // Assume the buffer is from memory and has not be read from disk. Assume UTF-8.
1514 /* Convert all non-UTF-8 formats to UTF-8.
1515 * BOM : https://www.unicode.org/faq/utf_bom.html
1516 * 00 00 FE FF UTF-32BE, big-endian
1517 * FF FE 00 00 UTF-32LE, little-endian
1518 * FE FF UTF-16BE, big-endian
1519 * FF FE UTF-16LE, little-endian
1522 if (buf
[0] == 0xFF && buf
[1] == 0xFE)
1524 if (buf
.length
>= 4 && buf
[2] == 0 && buf
[3] == 0)
1525 return UTF32ToUTF8
!(Endian
.little
)(buf
[4 .. $]);
1526 return UTF16ToUTF8
!(Endian
.little
)(buf
[2 .. $]);
1529 if (buf
[0] == 0xFE && buf
[1] == 0xFF)
1530 return UTF16ToUTF8
!(Endian
.big
)(buf
[2 .. $]);
1532 if (buf
.length
>= 4 && buf
[0] == 0 && buf
[1] == 0 && buf
[2] == 0xFE && buf
[3] == 0xFF)
1533 return UTF32ToUTF8
!(Endian
.big
)(buf
[4 .. $]);
1535 if (buf
.length
>= 3 && buf
[0] == 0xEF && buf
[1] == 0xBB && buf
[2] == 0xBF)
1538 /* There is no BOM. Make use of Arcane Jill's insight that
1539 * the first char of D source must be ASCII to
1540 * figure out the encoding.
1542 if (buf
.length
>= 4 && buf
[1] == 0 && buf
[2] == 0 && buf
[3] == 0)
1543 return UTF32ToUTF8
!(Endian
.little
)(buf
);
1544 if (buf
.length
>= 4 && buf
[0] == 0 && buf
[1] == 0 && buf
[2] == 0)
1545 return UTF32ToUTF8
!(Endian
.big
)(buf
);
1546 // try to check for UTF-16
1547 if (buf
.length
>= 2 && buf
[1] == 0)
1548 return UTF16ToUTF8
!(Endian
.little
)(buf
);
1550 return UTF16ToUTF8
!(Endian
.big
)(buf
);
1555 auto loc
= mod
.getLoc();
1556 .error(loc
, "%s `%s` source file must start with BOM or ASCII character, not \\x%02X", mod
.kind
, mod
.toPrettyChars
, buf
[0]);
1563 /*******************************************
1564 * Look for member of the form:
1565 * const(MemberInfo)[] getMembers(string);
1566 * Returns NULL if not found
1568 extern(C
++) FuncDeclaration
findGetMembers(ScopeDsymbol dsym
)
1570 import dmd
.opover
: search_function
;
1571 Dsymbol s
= search_function(dsym
, Id
.getmembers
);
1572 FuncDeclaration fdx
= s ? s
.isFuncDeclaration() : null;
1576 __gshared TypeFunction tfgetmembers
;
1580 sc
.eSink
= global
.errorSink
;
1581 auto parameters
= new Parameters();
1582 Parameters
* p
= new Parameter(STC
.in_
, Type
.tchar
.constOf().arrayOf(), null, null);
1585 TypeFunction tf
= new TypeFunction(parameters
, tret
, VarArg
.none
, LINK
.d
);
1586 tfgetmembers
= tf
.dsymbolSemantic(Loc
.initial
, &sc
).isTypeFunction();
1589 fdx
= fdx
.overloadExactMatch(tfgetmembers
);
1591 if (fdx
&& fdx
.isVirtual())