<div>A)</div>Currently, D suffers from a high degree of interdependency between modules; when one wants to use a single symbol (say std.traits.isInputRange), we pull out all of std.traits, which in turn pulls out all of std.array,std.string, etc. This results in slow compile times (relatively to the case where we didn't have to pull all this), and fat binaries: see example in point "D)" below.<div>
<br><div>This has been discussed many times before, and some people have suggested breaking modules into submodules such as: std.range.traits, etc to mitigate this a little, however this requires people to change 'import std.range' to 'import std.range.traits' to benefit from it, and also in many cases this will be ineffective.</div>
<div><br></div><div>B)</div><div>I'd like to propose something different that can potentially dramatically reduce compile time/binary size, while not requiring users to scar their source code as above.</div><div><br></div>
<div><b>in short: </b>perform semantic analysis for a function/template/struct/class on demand, if that symbol is encountered starting from main().</div><div><b><br></b></div><div><b>in more details:</b></div><div>suppose we compile a binary (dmd -ofmain foo1.d foo2.d main.d)</div>
<div>input files are lexed, parsed (code should be syntactically valid)</div><div>semantic analysis is performed, but doesn't go inside at function/template/struct/class declaration</div><div>main() symbol is located in symbol table</div>
<div>start lazy semantic analysis from the main() function and using a breadth first search (BFS) propagation strategy:</div><div>a symbol (function/template/struct/class) 's body/return type/template constraints is only semantically analyzed when that symbol is encountered along the BFS path.</div>
<div><br></div><div>this strategy could be enabled by a switch -lazy_compilation in dmd. The only time it would differ from existing compilation model would be when some unused code triggers compile error: eg:</div><div>----</div>
<div>void foo(){int x=y;}</div><div>void main(){}</div><div><div>----</div></div><div><div>dmd main.d //error: y is undefined</div><div>dmd -lazy_compilation main.d //OK: foo is never mentioned starting from main(), so accept.</div>
</div><div><br></div><div>This would be very useful to speed up the edit/compile/debug cycle. </div><div><br></div><div>Example2:</div><div><div>----</div><div><div>auto foo(){return "import std.stdio;";}</div><div>
mixin(foo);</div><div>void fun2(){import b;}</div><div></div><div>void main(){writeln("ok");}</div></div><div>----</div><div>lazy semantic analysis will analyze main, foo but not fun2, which is not used. foo is analyzed because it is used in a module-level mixin declaration.</div>
<div><div><br></div></div></div><div>C)</div><div><b>caveats:</b></div><div>this works when compiling <b>binaries</b>, as we know which symbols end up in the final binary</div><div>for compiling libraries (-shared/-static), it works if we have a way to specify which symbols are meant to be exported (eg <a href="https://www.gnu.org/software/gnulib/manual/html_node/Exported-Symbols-of-Shared-Libraries.html">https://www.gnu.org/software/gnulib/manual/html_node/Exported-Symbols-of-Shared-Libraries.html</a>). Is there, currently? </div>
<div><br></div><div>We could specify a list of symbols to export to dmd via a command line flag. </div><div><br></div><div>This could be: </div><div>dmd -exported_symbols=filename.d main.d bar.d</div><div>with filename.d containing all exported symbols, eg: </div>
<div>----</div><div>module exported_symbols;</div><div>public import foo.d; //imports all symbols from foo</div><div>public import bar:baz;//imports just bar.baz</div><div>void fun(){}//imports fun</div><div><div>----</div>
</div><div><br></div><div><br></div><div>D)</div><div>Example showing problem with current situation:</div><div>----</div><div><div>module main;</div><div>version(A)</div><div><span class="Apple-tab-span" style="white-space:pre">       </span>import std.range;</div>
<div>else{</div><div>      //copy paste here body of 'isInputRange' from std.range</div><div>}</div><div>void fun(){<span class="Apple-tab-span" style="white-space:pre">   </span>auto a=isInputRange!string;}</div></div>
<div><div>----</div><div><div>dmd -c main.d:</div></div><div>nm main.o|wc -l: 8</div><div>file size of main.o: 1.1K</div><div><div>cpu time (10 runs): 0.119 s</div><div><br></div><div>dmd -c -version=A main.d:</div></div>
<div><div>nm main.o|wc -l: 324 => 40X</div><div>file size of main.o: 72K => 70X</div><div>cpu time (10 runs): 2.7 s => 23X</div></div><div><br></div><div>Q: Why do we care about compilation speed, etc, since dmd is already fast?</div>
<div>A1: Many cases where it matters, eg for the REPL I'm working on, that requires compiling on the fly and needs interactive speed. </div><div>A2: for large projects, where compilation can become slow</div><div><br>
</div><div><br></div><div><br></div><div><div></div></div></div></div>