1 /**
2  * Written in the D programming language.
3  * Module initialization routines.
4  *
5  * Copyright: Copyright Digital Mars 2000 - 2013.
6  * License: Distributed under the
7  *      $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
8  *    (See accompanying file LICENSE)
9  * Authors:   Walter Bright, Sean Kelly
10  * Source: $(DRUNTIMESRC rt/_minfo.d)
11  */
12 
13 module rt.minfo;
14 
15 import core.stdc.stdlib;  // alloca
16 import core.stdc.string;  // memcpy
17 import rt.sections;
18 
19 enum
20 {
21     MIctorstart  = 0x1,   // we've started constructing it
22     MIctordone   = 0x2,   // finished construction
23     MIstandalone = 0x4,   // module ctor does not depend on other module
24                         // ctors being done first
25     MItlsctor    = 8,
26     MItlsdtor    = 0x10,
27     MIctor       = 0x20,
28     MIdtor       = 0x40,
29     MIxgetMembers = 0x80,
30     MIictor      = 0x100,
31     MIunitTest   = 0x200,
32     MIimportedModules = 0x400,
33     MIlocalClasses = 0x800,
34     MIname       = 0x1000,
35 }
36 
37 /*****
38  * A ModuleGroup is an unordered collection of modules.
39  * There is exactly one for:
40  *  1. all statically linked in D modules, either directely or as shared libraries
41  *  2. each call to rt_loadLibrary()
42  */
43 
44 struct ModuleGroup
45 {
46     this(immutable(ModuleInfo*)[] modules) nothrow @nogc
47     {
48         _modules = modules;
49     }
50 
51     @property immutable(ModuleInfo*)[] modules() const nothrow @nogc
52     {
53         return _modules;
54     }
55 
56     // this function initializes the bookeeping necessary to create the
57     // cycle path, and then creates it. It is a precondition that src and
58     // target modules are involved in a cycle.
59     //
60     // The return value is malloc'd using C, so it must be freed after use.
61     private size_t[] genCyclePath(size_t srcidx, size_t targetidx, int[][] edges) nothrow
62     {
63         import core.bitop : bt, btc, bts;
64 
65         // set up all the arrays.
66         size_t[] cyclePath = (cast(size_t*)malloc(size_t.sizeof * _modules.length * 2))[0 .. _modules.length * 2];
67         size_t totalMods;
68         int[] distance = (cast(int*)malloc(int.sizeof * _modules.length))[0 .. _modules.length];
69         scope(exit)
70             .free(distance.ptr);
71 
72         // determine the shortest path between two modules. Uses dijkstra
73         // without a priority queue. (we can be a bit slow here, in order to
74         // get a better printout).
75         void shortest(size_t start, size_t target)
76         {
77             // initial setup
78             distance[] = int.max;
79             int curdist = 0;
80             distance[start] = 0;
81             while (true)
82             {
83                 bool done = true;
84                 foreach (i, x; distance)
85                 {
86                     if (x == curdist)
87                     {
88                         if (i == target)
89                         {
90                             done = true;
91                             break;
92                         }
93                         foreach (n; edges[i])
94                         {
95                             if (distance[n] == int.max)
96                             {
97                                 distance[n] = curdist + 1;
98                                 done = false;
99                             }
100                         }
101                     }
102                 }
103                 if (done)
104                     break;
105                 ++curdist;
106             }
107             // it should be impossible to not get to target, this is just a
108             // sanity check. Not an assert, because druntime is compiled in
109             // release mode.
110             if (distance[target] != curdist)
111             {
112                 assert(0, "internal error printing module cycle");
113             }
114 
115             // determine the path. This is tricky, because we have to
116             // follow the edges in reverse to get back to the original. We
117             // don't have a reverse mapping, so it takes a bit of looping.
118             totalMods += curdist;
119             auto subpath = cyclePath[totalMods - curdist .. totalMods];
120             while (true)
121             {
122                 --curdist;
123                 subpath[curdist] = target;
124                 if (curdist == 0)
125                     break;
126             distloop:
127                 // search for next (previous) module in cycle.
128                 foreach (m, d; distance)
129                 {
130                     if (d == curdist)
131                     {
132                         // determine if m can reach target
133                         foreach (e; edges[m])
134                         {
135                             if (e == target)
136                             {
137                                 // recurse
138                                 target = m;
139                                 break distloop;
140                             }
141                         }
142                     }
143                 }
144             }
145         }
146 
147         // first get to the target
148         shortest(srcidx, targetidx);
149         // now get back.
150         shortest(targetidx, srcidx);
151 
152         return cyclePath[0 .. totalMods];
153     }
154 
155     /******************************
156      * Allocate and fill in _ctors[] and _tlsctors[].
157      * Modules are inserted into the arrays in the order in which the constructors
158      * need to be run.
159      *
160      * Params:
161      *  cycleHandling - string indicating option for cycle handling
162      * Throws:
163      *  Exception if it fails.
164      */
165     void sortCtors(string cycleHandling) nothrow
166     {
167         import core.bitop : bts, btr, bt, BitRange;
168         import core.internal.container.hashtab;
169 
170         enum OnCycle
171         {
172             abort,
173             print,
174             ignore
175         }
176 
177         auto onCycle = OnCycle.abort;
178 
179         switch (cycleHandling) with(OnCycle)
180         {
181         case "deprecate":
182             import core.stdc.stdio : fprintf, stderr;
183             // Option deprecated in 2.101, remove in 2.111
184             fprintf(stderr, "`--DRT-oncycle=deprecate` is no longer supported, using `abort` instead\n");
185             break;
186         case "abort":
187             onCycle = abort;
188             break;
189         case "print":
190             onCycle = print;
191             break;
192         case "ignore":
193             onCycle = ignore;
194             break;
195         case "":
196             // no option passed
197             break;
198         default:
199             // invalid cycle handling option.
200             assert(0, "DRT invalid cycle handling option: " ~ cycleHandling);
201         }
202 
203         debug (printModuleDependencies)
204         {
205             import core.stdc.stdio : printf;
206 
207             foreach (_m; _modules)
208             {
209                 printf("%s%s%s:", _m.name.ptr, (_m.flags & MIstandalone)
210                         ? "+".ptr : "".ptr, (_m.flags & (MIctor | MIdtor)) ? "*".ptr : "".ptr);
211                 foreach (_i; _m.importedModules)
212                     printf(" %s", _i.name.ptr);
213                 printf("\n");
214             }
215         }
216 
217         immutable uint len = cast(uint) _modules.length;
218         if (!len)
219             return; // nothing to do.
220 
221         // allocate some stack arrays that will be used throughout the process.
222         immutable nwords = (len + 8 * size_t.sizeof - 1) / (8 * size_t.sizeof);
223         immutable flagbytes = nwords * size_t.sizeof;
224         auto ctorstart = cast(size_t*) malloc(flagbytes); // ctor/dtor seen
225         auto ctordone = cast(size_t*) malloc(flagbytes); // ctor/dtor processed
226         auto relevant = cast(size_t*) malloc(flagbytes); // has ctors/dtors
227         scope (exit)
228         {
229             .free(ctorstart);
230             .free(ctordone);
231             .free(relevant);
232         }
233 
234         void clearFlags(size_t* flags)
235         {
236             memset(flags, 0, flagbytes);
237         }
238 
239 
240         // build the edges between each module. We may need this for printing,
241         // and also allows avoiding keeping a hash around for module lookups.
242         int[][] edges = (cast(int[]*)malloc((int[]).sizeof * _modules.length))[0 .. _modules.length];
243         {
244             HashTab!(immutable(ModuleInfo)*, int) modIndexes;
245             foreach (i, m; _modules)
246                 modIndexes[m] = cast(int) i;
247 
248             auto reachable = cast(size_t*) malloc(flagbytes);
249             scope(exit)
250                 .free(reachable);
251 
252             foreach (i, m; _modules)
253             {
254                 // use bit array to prevent duplicates
255                 // https://issues.dlang.org/show_bug.cgi?id=16208
256                 clearFlags(reachable);
257                 // preallocate enough space to store all the indexes
258                 int *edge = cast(int*)malloc(int.sizeof * _modules.length);
259                 size_t nEdges = 0;
260                 foreach (imp; m.importedModules)
261                 {
262                     if (imp is m) // self-import
263                         continue;
264                     if (auto impidx = imp in modIndexes)
265                     {
266                         if (!bts(reachable, *impidx))
267                             edge[nEdges++] = *impidx;
268                     }
269                 }
270                 if (nEdges > 0)
271                 {
272                     // trim space to what is needed
273                     edges[i] = (cast(int*)realloc(edge, int.sizeof * nEdges))[0 .. nEdges];
274                 }
275                 else
276                 {
277                     edges[i] = null;
278                     .free(edge);
279                 }
280             }
281         }
282 
283         // free all the edges after we are done
284         scope(exit)
285         {
286             foreach (e; edges)
287                 if (e.ptr)
288                     .free(e.ptr);
289             .free(edges.ptr);
290         }
291 
292         void buildCycleMessage(size_t sourceIdx, size_t cycleIdx, scope void delegate(string) nothrow sink)
293         {
294             version (Windows)
295                 enum EOL = "\r\n";
296             else
297                 enum EOL = "\n";
298 
299             sink("Cyclic dependency between module constructors/destructors of ");
300             sink(_modules[sourceIdx].name);
301             sink(" and ");
302             sink(_modules[cycleIdx].name);
303             sink(EOL);
304             auto cyclePath = genCyclePath(sourceIdx, cycleIdx, edges);
305             scope(exit) .free(cyclePath.ptr);
306 
307             sink(_modules[sourceIdx].name);
308             sink("* ->" ~ EOL);
309             foreach (x; cyclePath[0 .. $ - 1])
310             {
311                 sink(_modules[x].name);
312                 sink(bt(relevant, x) ? "* ->" ~ EOL : " ->" ~ EOL);
313             }
314             sink(_modules[sourceIdx].name);
315             sink("*" ~ EOL);
316         }
317 
318         // find all the non-trivial dependencies (that is, dependencies that have a
319         // ctor or dtor) of a given module.  Doing this, we can 'skip over' the
320         // trivial modules to get at the non-trivial ones.
321         //
322         // If a cycle is detected, returns the index of the module that completes the cycle.
323         // Returns: true for success, false for a deprecated cycle error
324         bool findDeps(size_t idx, size_t* reachable) nothrow
325         {
326             static struct stackFrame
327             {
328                 size_t curMod;
329                 size_t curDep;
330             }
331 
332             // initialize "stack"
333             auto stack = cast(stackFrame*) malloc(stackFrame.sizeof * len);
334             scope (exit)
335                 .free(stack);
336             auto stacktop = stack + len;
337             auto sp = stack;
338             sp.curMod = cast(int) idx;
339             sp.curDep = 0;
340 
341             // initialize reachable by flagging source module
342             clearFlags(reachable);
343             bts(reachable, idx);
344 
345             for (;;)
346             {
347                 auto m = _modules[sp.curMod];
348                 if (sp.curDep >= edges[sp.curMod].length)
349                 {
350                     // return
351                     if (sp == stack) // finished the algorithm
352                         break;
353                     --sp;
354                 }
355                 else
356                 {
357                     auto midx = edges[sp.curMod][sp.curDep];
358                     if (!bts(reachable, midx))
359                     {
360                         if (bt(relevant, midx))
361                         {
362                             // need to process this node, don't recurse.
363                             if (bt(ctorstart, midx))
364                             {
365                                 // was already started, this is a cycle.
366                                 final switch (onCycle) with(OnCycle)
367                                 {
368                                 case abort:
369 
370                                     string errmsg = "";
371                                     buildCycleMessage(idx, midx, (string x) {errmsg ~= x;});
372                                     throw new Error(errmsg, __FILE__, __LINE__);
373                                 case ignore:
374                                     break;
375                                 case print:
376                                     // print the message
377                                     buildCycleMessage(idx, midx, (string x) {
378                                                       import core.stdc.stdio : fprintf, stderr;
379                                                       fprintf(stderr, "%.*s", cast(int) x.length, x.ptr);
380                                                       });
381                                     // continue on as if this is correct.
382                                     break;
383                                 }
384                             }
385                         }
386                         else if (!bt(ctordone, midx))
387                         {
388                             // non-relevant, and hasn't been exhaustively processed, recurse.
389                             if (++sp >= stacktop)
390                             {
391                                 // stack overflow, this shouldn't happen.
392                                 import core.internal.abort : abort;
393 
394                                 abort("stack overflow on dependency search");
395                             }
396                             sp.curMod = midx;
397                             sp.curDep = 0;
398                             continue;
399                         }
400                     }
401                 }
402 
403                 // next dependency
404                 ++sp.curDep;
405             }
406             return true; // success
407         }
408 
409         // The list of constructors that will be returned by the sorting.
410         immutable(ModuleInfo)** ctors;
411         // current element being inserted into ctors list.
412         size_t ctoridx = 0;
413 
414         // This function will determine the order of construction/destruction and
415         // check for cycles. If a cycle is found, the cycle path is transformed
416         // into a string and thrown as an error.
417         //
418         // Each call into this function is given a module that has static
419         // ctor/dtors that must be dealt with. It recurses only when it finds
420         // dependencies that also have static ctor/dtors.
421         // Returns: true for success, false for a deprecated cycle error
422         bool processMod(size_t curidx) nothrow
423         {
424             immutable ModuleInfo* current = _modules[curidx];
425 
426             // First, determine what modules are reachable.
427             auto reachable = cast(size_t*) malloc(flagbytes);
428             scope (exit)
429                 .free(reachable);
430             if (!findDeps(curidx, reachable))
431                 return false;   // deprecated cycle error
432 
433             // process the dependencies. First, we process all relevant ones
434             bts(ctorstart, curidx);
435             auto brange = BitRange(reachable, len);
436             foreach (i; brange)
437             {
438                 // note, don't check for cycles here, because the config could have been set to ignore cycles.
439                 // however, don't recurse if there is one, so still check for started ctor.
440                 if (i != curidx && bt(relevant, i) && !bt(ctordone, i) && !bt(ctorstart, i))
441                 {
442                     if (!processMod(i))
443                         return false; // deprecated cycle error
444                 }
445             }
446 
447             // now mark this node, and all nodes reachable from this module as done.
448             bts(ctordone, curidx);
449             btr(ctorstart, curidx);
450             foreach (i; brange)
451             {
452                 // Since relevant dependencies are already marked as done
453                 // from recursion above (or are going to be handled up the call
454                 // stack), no reason to check for relevance, that is a wasted
455                 // op.
456                 bts(ctordone, i);
457             }
458 
459             // add this module to the construction order list
460             ctors[ctoridx++] = current;
461             return true;
462         }
463 
464         // returns `false` if deprecated cycle error otherwise set `result`.
465         bool doSort(size_t relevantFlags, ref immutable(ModuleInfo)*[] result) nothrow
466         {
467             clearFlags(relevant);
468             clearFlags(ctorstart);
469             clearFlags(ctordone);
470 
471             // pre-allocate enough space to hold all modules.
472             ctors = (cast(immutable(ModuleInfo)**).malloc(len * (void*).sizeof));
473             ctoridx = 0;
474             foreach (idx, m; _modules)
475             {
476                 if (m.flags & relevantFlags)
477                 {
478                     if (m.flags & MIstandalone)
479                     {
480                         // can run at any time. Just run it first.
481                         ctors[ctoridx++] = m;
482                     }
483                     else
484                     {
485                         bts(relevant, idx);
486                     }
487                 }
488             }
489 
490             // now run the algorithm in the relevant ones
491             foreach (idx; BitRange(relevant, len))
492             {
493                 if (!bt(ctordone, idx))
494                 {
495                     if (!processMod(idx))
496                         return false;
497                 }
498             }
499 
500             if (ctoridx == 0)
501             {
502                 // no ctors in the list.
503                 .free(ctors);
504             }
505             else
506             {
507                 ctors = cast(immutable(ModuleInfo)**).realloc(ctors, ctoridx * (void*).sizeof);
508                 if (ctors is null)
509                     assert(0);
510                 result = ctors[0 .. ctoridx];
511             }
512             return true;
513         }
514 
515         // finally, do the sorting for both shared and tls ctors. If either returns false,
516         // print the deprecation warning.
517         if (!doSort(MIctor | MIdtor, _ctors) ||
518             !doSort(MItlsctor | MItlsdtor, _tlsctors))
519         {
520             // print a warning
521             import core.stdc.stdio : fprintf, stderr;
522             fprintf(stderr, "Deprecation 16211 warning:\n"
523                 ~ "A cycle has been detected in your program that was undetected prior to DMD\n"
524                 ~ "2.072. This program will continue, but will not operate when using DMD 2.074\n"
525                 ~ "to compile. Use runtime option --DRT-oncycle=print to see the cycle details.\n");
526 
527         }
528     }
529 
530     /// ditto
531     void sortCtors()
532     {
533         import rt.config : rt_configOption;
534         sortCtors(rt_configOption("oncycle"));
535     }
536 
537     void runCtors()
538     {
539         // run independent ctors
540         runModuleFuncs!(m => m.ictor)(_modules);
541         // sorted module ctors
542         runModuleFuncs!(m => m.ctor)(_ctors);
543     }
544 
545     void runTlsCtors()
546     {
547         runModuleFuncs!(m => m.tlsctor)(_tlsctors);
548     }
549 
550     void runTlsDtors()
551     {
552         runModuleFuncsRev!(m => m.tlsdtor)(_tlsctors);
553     }
554 
555     void runDtors()
556     {
557         runModuleFuncsRev!(m => m.dtor)(_ctors);
558     }
559 
560     void free()
561     {
562         if (_ctors.ptr)
563             .free(_ctors.ptr);
564         _ctors = null;
565         if (_tlsctors.ptr)
566             .free(_tlsctors.ptr);
567         _tlsctors = null;
568         // _modules = null; // let the owner free it
569     }
570 
571 private:
572     immutable(ModuleInfo*)[]  _modules;
573     immutable(ModuleInfo)*[]    _ctors;
574     immutable(ModuleInfo)*[] _tlsctors;
575 }
576 
577 
578 /********************************************
579  * Iterate over all module infos.
580  */
581 
582 int moduleinfos_apply(scope int delegate(immutable(ModuleInfo*)) dg)
583 {
584     foreach (ref sg; SectionGroup)
585     {
586         foreach (m; sg.modules)
587         {
588             // TODO: Should null ModuleInfo be allowed?
589             if (m !is null)
590             {
591                 if (auto res = dg(m))
592                     return res;
593             }
594         }
595     }
596     return 0;
597 }
598 
599 /********************************************
600  * Module constructor and destructor routines.
601  */
602 
603 extern (C)
604 {
605 void rt_moduleCtor()
606 {
607     foreach (ref sg; SectionGroup)
608     {
609         sg.moduleGroup.sortCtors();
610         sg.moduleGroup.runCtors();
611     }
612 }
613 
614 void rt_moduleTlsCtor()
615 {
616     foreach (ref sg; SectionGroup)
617     {
618         sg.moduleGroup.runTlsCtors();
619     }
620 }
621 
622 void rt_moduleTlsDtor()
623 {
624     foreach_reverse (ref sg; SectionGroup)
625     {
626         sg.moduleGroup.runTlsDtors();
627     }
628 }
629 
630 void rt_moduleDtor()
631 {
632     foreach_reverse (ref sg; SectionGroup)
633     {
634         sg.moduleGroup.runDtors();
635         sg.moduleGroup.free();
636     }
637 }
638 
639 version (Win32)
640 {
641     // Alternate names for backwards compatibility with older DLL code
642     void _moduleCtor()
643     {
644         rt_moduleCtor();
645     }
646 
647     void _moduleDtor()
648     {
649         rt_moduleDtor();
650     }
651 
652     void _moduleTlsCtor()
653     {
654         rt_moduleTlsCtor();
655     }
656 
657     void _moduleTlsDtor()
658     {
659         rt_moduleTlsDtor();
660     }
661 }
662 }
663 
664 /********************************************
665  */
666 
667 void runModuleFuncs(alias getfp)(const(immutable(ModuleInfo)*)[] modules)
668 {
669     foreach (m; modules)
670     {
671         if (auto fp = getfp(m))
672             (*fp)();
673     }
674 }
675 
676 void runModuleFuncsRev(alias getfp)(const(immutable(ModuleInfo)*)[] modules)
677 {
678     foreach_reverse (m; modules)
679     {
680         if (auto fp = getfp(m))
681             (*fp)();
682     }
683 }
684 
685 unittest
686 {
687     static void assertThrown(T : Throwable, E)(lazy E expr, string msg)
688     {
689         try
690             expr;
691         catch (T)
692             return;
693         assert(0, msg);
694     }
695 
696     static void stub()
697     {
698     }
699 
700     static struct UTModuleInfo
701     {
702         this(uint flags)
703         {
704             mi._flags = flags;
705         }
706 
707         void setImports(immutable(ModuleInfo)*[] imports...)
708         {
709             import core.bitop;
710             assert(flags & MIimportedModules);
711 
712             immutable nfuncs = popcnt(flags & (MItlsctor|MItlsdtor|MIctor|MIdtor|MIictor));
713             immutable size = nfuncs * (void function()).sizeof +
714                 size_t.sizeof + imports.length * (ModuleInfo*).sizeof;
715             assert(size <= pad.sizeof);
716 
717             pad[nfuncs] = imports.length;
718             .memcpy(&pad[nfuncs+1], imports.ptr, imports.length * imports[0].sizeof);
719         }
720 
721         immutable ModuleInfo mi;
722         size_t[8] pad;
723         alias mi this;
724     }
725 
726     static UTModuleInfo mockMI(uint flags)
727     {
728         auto mi = UTModuleInfo(flags | MIimportedModules);
729         auto p = cast(void function()*)&mi.pad;
730         if (flags & MItlsctor) *p++ = &stub;
731         if (flags & MItlsdtor) *p++ = &stub;
732         if (flags & MIctor) *p++ = &stub;
733         if (flags & MIdtor) *p++ = &stub;
734         if (flags & MIictor) *p++ = &stub;
735         *cast(size_t*)p++ = 0; // number of imported modules
736         assert(cast(void*)p <= &mi + 1);
737         return mi;
738     }
739 
740     static void checkExp2(string testname, bool shouldThrow, string oncycle,
741         immutable(ModuleInfo*)[] modules,
742         immutable(ModuleInfo*)[] dtors=null,
743         immutable(ModuleInfo*)[] tlsdtors=null)
744     {
745         auto mgroup = ModuleGroup(modules);
746         mgroup.sortCtors(oncycle);
747 
748         // if we are expecting sort to throw, don't throw because of unexpected
749         // success!
750         if (!shouldThrow)
751         {
752             foreach (m; mgroup._modules)
753                 assert(!(m.flags & (MIctorstart | MIctordone)), testname);
754             assert(mgroup._ctors    == dtors, testname);
755             assert(mgroup._tlsctors == tlsdtors, testname);
756         }
757     }
758 
759     static void checkExp(string testname, bool shouldThrow,
760         immutable(ModuleInfo*)[] modules,
761         immutable(ModuleInfo*)[] dtors=null,
762         immutable(ModuleInfo*)[] tlsdtors=null)
763     {
764         checkExp2(testname, shouldThrow, "abort", modules, dtors, tlsdtors);
765     }
766 
767 
768     {
769         auto m0 = mockMI(0);
770         auto m1 = mockMI(0);
771         auto m2 = mockMI(0);
772         checkExp("no ctors", false, [&m0.mi, &m1.mi, &m2.mi]);
773     }
774 
775     {
776         auto m0 = mockMI(MIictor);
777         auto m1 = mockMI(0);
778         auto m2 = mockMI(MIictor);
779         auto mgroup = ModuleGroup([&m0.mi, &m1.mi, &m2.mi]);
780         checkExp("independent ctors", false, [&m0.mi, &m1.mi, &m2.mi]);
781     }
782 
783     {
784         auto m0 = mockMI(MIstandalone | MIctor);
785         auto m1 = mockMI(0);
786         auto m2 = mockMI(0);
787         auto mgroup = ModuleGroup([&m0.mi, &m1.mi, &m2.mi]);
788         checkExp("standalone ctor", false, [&m0.mi, &m1.mi, &m2.mi], [&m0.mi]);
789     }
790 
791     {
792         auto m0 = mockMI(MIstandalone | MIctor);
793         auto m1 = mockMI(MIstandalone | MIctor);
794         auto m2 = mockMI(0);
795         m1.setImports(&m0.mi);
796         checkExp("imported standalone => no dependency", false,
797                  [&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi]);
798     }
799 
800     {
801         auto m0 = mockMI(MIstandalone | MIctor);
802         auto m1 = mockMI(MIstandalone | MIctor);
803         auto m2 = mockMI(0);
804         m0.setImports(&m1.mi);
805         checkExp("imported standalone => no dependency (2)", false,
806                 [&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi]);
807     }
808 
809     {
810         auto m0 = mockMI(MIstandalone | MIctor);
811         auto m1 = mockMI(MIstandalone | MIctor);
812         auto m2 = mockMI(0);
813         m0.setImports(&m1.mi);
814         m1.setImports(&m0.mi);
815         checkExp("standalone may have cycle", false,
816                 [&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi]);
817     }
818 
819     {
820         auto m0 = mockMI(MIctor);
821         auto m1 = mockMI(MIctor);
822         auto m2 = mockMI(0);
823         m1.setImports(&m0.mi);
824         checkExp("imported ctor => ordered ctors", false,
825                 [&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi], []);
826     }
827 
828     {
829         auto m0 = mockMI(MIctor);
830         auto m1 = mockMI(MIctor);
831         auto m2 = mockMI(0);
832         m0.setImports(&m1.mi);
833         checkExp("imported ctor => ordered ctors (2)", false,
834                 [&m0.mi, &m1.mi, &m2.mi], [&m1.mi, &m0.mi], []);
835     }
836 
837     {
838         auto m0 = mockMI(MIctor);
839         auto m1 = mockMI(MIctor);
840         auto m2 = mockMI(0);
841         m0.setImports(&m1.mi);
842         m1.setImports(&m0.mi);
843         assertThrown!Throwable(checkExp("", true, [&m0.mi, &m1.mi, &m2.mi]),
844                 "detects ctors cycles");
845         assertThrown!Throwable(checkExp2("", true, "deprecate",
846                                         [&m0.mi, &m1.mi, &m2.mi]),
847                 "detects ctors cycles (dep)");
848     }
849 
850     {
851         auto m0 = mockMI(MIctor);
852         auto m1 = mockMI(MIctor);
853         auto m2 = mockMI(0);
854         m0.setImports(&m2.mi);
855         m1.setImports(&m2.mi);
856         m2.setImports(&m0.mi, &m1.mi);
857         assertThrown!Throwable(checkExp("", true, [&m0.mi, &m1.mi, &m2.mi]),
858                 "detects cycle with repeats");
859     }
860 
861     {
862         auto m0 = mockMI(MIctor);
863         auto m1 = mockMI(MIctor);
864         auto m2 = mockMI(MItlsctor);
865         m0.setImports(&m1.mi, &m2.mi);
866         checkExp("imported ctor/tlsctor => ordered ctors/tlsctors", false,
867                 [&m0.mi, &m1.mi, &m2.mi], [&m1.mi, &m0.mi], [&m2.mi]);
868     }
869 
870     {
871         auto m0 = mockMI(MIctor | MItlsctor);
872         auto m1 = mockMI(MIctor);
873         auto m2 = mockMI(MItlsctor);
874         m0.setImports(&m1.mi, &m2.mi);
875         checkExp("imported ctor/tlsctor => ordered ctors/tlsctors (2)", false,
876                 [&m0.mi, &m1.mi, &m2.mi], [&m1.mi, &m0.mi], [&m2.mi, &m0.mi]);
877     }
878 
879     {
880         auto m0 = mockMI(MIctor);
881         auto m1 = mockMI(MIctor);
882         auto m2 = mockMI(MItlsctor);
883         m0.setImports(&m1.mi, &m2.mi);
884         m2.setImports(&m0.mi);
885         checkExp("no cycle between ctors/tlsctors", false,
886                 [&m0.mi, &m1.mi, &m2.mi], [&m1.mi, &m0.mi], [&m2.mi]);
887     }
888 
889     {
890         auto m0 = mockMI(MItlsctor);
891         auto m1 = mockMI(MIctor);
892         auto m2 = mockMI(MItlsctor);
893         m0.setImports(&m2.mi);
894         m2.setImports(&m0.mi);
895         assertThrown!Throwable(checkExp("", true, [&m0.mi, &m1.mi, &m2.mi]),
896                 "detects tlsctors cycle");
897         assertThrown!Throwable(checkExp2("", true, "deprecate",
898                                          [&m0.mi, &m1.mi, &m2.mi]),
899                 "detects tlsctors cycle (dep)");
900     }
901 
902     {
903         auto m0 = mockMI(MItlsctor);
904         auto m1 = mockMI(MIctor);
905         auto m2 = mockMI(MItlsctor);
906         m0.setImports(&m1.mi);
907         m1.setImports(&m0.mi, &m2.mi);
908         m2.setImports(&m1.mi);
909         assertThrown!Throwable(checkExp("", true, [&m0.mi, &m1.mi, &m2.mi]),
910                 "detects tlsctors cycle with repeats");
911     }
912 
913     {
914         auto m0 = mockMI(MIctor);
915         auto m1 = mockMI(MIstandalone | MIctor);
916         auto m2 = mockMI(MIstandalone | MIctor);
917         m0.setImports(&m1.mi);
918         m1.setImports(&m2.mi);
919         m2.setImports(&m0.mi);
920         // NOTE: this is implementation dependent, sorted order shouldn't be tested.
921         checkExp("closed ctors cycle", false, [&m0.mi, &m1.mi, &m2.mi],
922                 [&m1.mi, &m2.mi, &m0.mi]);
923         //checkExp("closed ctors cycle", false, [&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi, &m2.mi]);
924     }
925 }
926 
927 version (CRuntime_Microsoft)
928 {
929     // Dummy so Win32 code can still call it
930     extern(C) void _minit() { }
931 }