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                 // trim space to what is needed.
271                 edges[i] = (cast(int*)realloc(edge, int.sizeof * nEdges))[0 .. nEdges];
272             }
273         }
274 
275         // free all the edges after we are done
276         scope(exit)
277         {
278             foreach (e; edges)
279                 if (e.ptr)
280                     .free(e.ptr);
281             .free(edges.ptr);
282         }
283 
284         void buildCycleMessage(size_t sourceIdx, size_t cycleIdx, scope void delegate(string) nothrow sink)
285         {
286             version (Windows)
287                 enum EOL = "\r\n";
288             else
289                 enum EOL = "\n";
290 
291             sink("Cyclic dependency between module constructors/destructors of ");
292             sink(_modules[sourceIdx].name);
293             sink(" and ");
294             sink(_modules[cycleIdx].name);
295             sink(EOL);
296             auto cyclePath = genCyclePath(sourceIdx, cycleIdx, edges);
297             scope(exit) .free(cyclePath.ptr);
298 
299             sink(_modules[sourceIdx].name);
300             sink("* ->" ~ EOL);
301             foreach (x; cyclePath[0 .. $ - 1])
302             {
303                 sink(_modules[x].name);
304                 sink(bt(relevant, x) ? "* ->" ~ EOL : " ->" ~ EOL);
305             }
306             sink(_modules[sourceIdx].name);
307             sink("*" ~ EOL);
308         }
309 
310         // find all the non-trivial dependencies (that is, dependencies that have a
311         // ctor or dtor) of a given module.  Doing this, we can 'skip over' the
312         // trivial modules to get at the non-trivial ones.
313         //
314         // If a cycle is detected, returns the index of the module that completes the cycle.
315         // Returns: true for success, false for a deprecated cycle error
316         bool findDeps(size_t idx, size_t* reachable) nothrow
317         {
318             static struct stackFrame
319             {
320                 size_t curMod;
321                 size_t curDep;
322             }
323 
324             // initialize "stack"
325             auto stack = cast(stackFrame*) malloc(stackFrame.sizeof * len);
326             scope (exit)
327                 .free(stack);
328             auto stacktop = stack + len;
329             auto sp = stack;
330             sp.curMod = cast(int) idx;
331             sp.curDep = 0;
332 
333             // initialize reachable by flagging source module
334             clearFlags(reachable);
335             bts(reachable, idx);
336 
337             for (;;)
338             {
339                 auto m = _modules[sp.curMod];
340                 if (sp.curDep >= edges[sp.curMod].length)
341                 {
342                     // return
343                     if (sp == stack) // finished the algorithm
344                         break;
345                     --sp;
346                 }
347                 else
348                 {
349                     auto midx = edges[sp.curMod][sp.curDep];
350                     if (!bts(reachable, midx))
351                     {
352                         if (bt(relevant, midx))
353                         {
354                             // need to process this node, don't recurse.
355                             if (bt(ctorstart, midx))
356                             {
357                                 // was already started, this is a cycle.
358                                 final switch (onCycle) with(OnCycle)
359                                 {
360                                 case abort:
361 
362                                     string errmsg = "";
363                                     buildCycleMessage(idx, midx, (string x) {errmsg ~= x;});
364                                     throw new Error(errmsg, __FILE__, __LINE__);
365                                 case ignore:
366                                     break;
367                                 case print:
368                                     // print the message
369                                     buildCycleMessage(idx, midx, (string x) {
370                                                       import core.stdc.stdio : fprintf, stderr;
371                                                       fprintf(stderr, "%.*s", cast(int) x.length, x.ptr);
372                                                       });
373                                     // continue on as if this is correct.
374                                     break;
375                                 }
376                             }
377                         }
378                         else if (!bt(ctordone, midx))
379                         {
380                             // non-relevant, and hasn't been exhaustively processed, recurse.
381                             if (++sp >= stacktop)
382                             {
383                                 // stack overflow, this shouldn't happen.
384                                 import core.internal.abort : abort;
385 
386                                 abort("stack overflow on dependency search");
387                             }
388                             sp.curMod = midx;
389                             sp.curDep = 0;
390                             continue;
391                         }
392                     }
393                 }
394 
395                 // next dependency
396                 ++sp.curDep;
397             }
398             return true; // success
399         }
400 
401         // The list of constructors that will be returned by the sorting.
402         immutable(ModuleInfo)** ctors;
403         // current element being inserted into ctors list.
404         size_t ctoridx = 0;
405 
406         // This function will determine the order of construction/destruction and
407         // check for cycles. If a cycle is found, the cycle path is transformed
408         // into a string and thrown as an error.
409         //
410         // Each call into this function is given a module that has static
411         // ctor/dtors that must be dealt with. It recurses only when it finds
412         // dependencies that also have static ctor/dtors.
413         // Returns: true for success, false for a deprecated cycle error
414         bool processMod(size_t curidx) nothrow
415         {
416             immutable ModuleInfo* current = _modules[curidx];
417 
418             // First, determine what modules are reachable.
419             auto reachable = cast(size_t*) malloc(flagbytes);
420             scope (exit)
421                 .free(reachable);
422             if (!findDeps(curidx, reachable))
423                 return false;   // deprecated cycle error
424 
425             // process the dependencies. First, we process all relevant ones
426             bts(ctorstart, curidx);
427             auto brange = BitRange(reachable, len);
428             foreach (i; brange)
429             {
430                 // note, don't check for cycles here, because the config could have been set to ignore cycles.
431                 // however, don't recurse if there is one, so still check for started ctor.
432                 if (i != curidx && bt(relevant, i) && !bt(ctordone, i) && !bt(ctorstart, i))
433                 {
434                     if (!processMod(i))
435                         return false; // deprecated cycle error
436                 }
437             }
438 
439             // now mark this node, and all nodes reachable from this module as done.
440             bts(ctordone, curidx);
441             btr(ctorstart, curidx);
442             foreach (i; brange)
443             {
444                 // Since relevant dependencies are already marked as done
445                 // from recursion above (or are going to be handled up the call
446                 // stack), no reason to check for relevance, that is a wasted
447                 // op.
448                 bts(ctordone, i);
449             }
450 
451             // add this module to the construction order list
452             ctors[ctoridx++] = current;
453             return true;
454         }
455 
456         // returns `false` if deprecated cycle error otherwise set `result`.
457         bool doSort(size_t relevantFlags, ref immutable(ModuleInfo)*[] result) nothrow
458         {
459             clearFlags(relevant);
460             clearFlags(ctorstart);
461             clearFlags(ctordone);
462 
463             // pre-allocate enough space to hold all modules.
464             ctors = (cast(immutable(ModuleInfo)**).malloc(len * (void*).sizeof));
465             ctoridx = 0;
466             foreach (idx, m; _modules)
467             {
468                 if (m.flags & relevantFlags)
469                 {
470                     if (m.flags & MIstandalone)
471                     {
472                         // can run at any time. Just run it first.
473                         ctors[ctoridx++] = m;
474                     }
475                     else
476                     {
477                         bts(relevant, idx);
478                     }
479                 }
480             }
481 
482             // now run the algorithm in the relevant ones
483             foreach (idx; BitRange(relevant, len))
484             {
485                 if (!bt(ctordone, idx))
486                 {
487                     if (!processMod(idx))
488                         return false;
489                 }
490             }
491 
492             if (ctoridx == 0)
493             {
494                 // no ctors in the list.
495                 .free(ctors);
496             }
497             else
498             {
499                 ctors = cast(immutable(ModuleInfo)**).realloc(ctors, ctoridx * (void*).sizeof);
500                 if (ctors is null)
501                     assert(0);
502                 result = ctors[0 .. ctoridx];
503             }
504             return true;
505         }
506 
507         // finally, do the sorting for both shared and tls ctors. If either returns false,
508         // print the deprecation warning.
509         if (!doSort(MIctor | MIdtor, _ctors) ||
510             !doSort(MItlsctor | MItlsdtor, _tlsctors))
511         {
512             // print a warning
513             import core.stdc.stdio : fprintf, stderr;
514             fprintf(stderr, "Deprecation 16211 warning:\n"
515                 ~ "A cycle has been detected in your program that was undetected prior to DMD\n"
516                 ~ "2.072. This program will continue, but will not operate when using DMD 2.074\n"
517                 ~ "to compile. Use runtime option --DRT-oncycle=print to see the cycle details.\n");
518 
519         }
520     }
521 
522     /// ditto
523     void sortCtors()
524     {
525         import rt.config : rt_configOption;
526         sortCtors(rt_configOption("oncycle"));
527     }
528 
529     void runCtors()
530     {
531         // run independent ctors
532         runModuleFuncs!(m => m.ictor)(_modules);
533         // sorted module ctors
534         runModuleFuncs!(m => m.ctor)(_ctors);
535     }
536 
537     void runTlsCtors()
538     {
539         runModuleFuncs!(m => m.tlsctor)(_tlsctors);
540     }
541 
542     void runTlsDtors()
543     {
544         runModuleFuncsRev!(m => m.tlsdtor)(_tlsctors);
545     }
546 
547     void runDtors()
548     {
549         runModuleFuncsRev!(m => m.dtor)(_ctors);
550     }
551 
552     void free()
553     {
554         if (_ctors.ptr)
555             .free(_ctors.ptr);
556         _ctors = null;
557         if (_tlsctors.ptr)
558             .free(_tlsctors.ptr);
559         _tlsctors = null;
560         // _modules = null; // let the owner free it
561     }
562 
563 private:
564     immutable(ModuleInfo*)[]  _modules;
565     immutable(ModuleInfo)*[]    _ctors;
566     immutable(ModuleInfo)*[] _tlsctors;
567 }
568 
569 
570 /********************************************
571  * Iterate over all module infos.
572  */
573 
574 int moduleinfos_apply(scope int delegate(immutable(ModuleInfo*)) dg)
575 {
576     foreach (ref sg; SectionGroup)
577     {
578         foreach (m; sg.modules)
579         {
580             // TODO: Should null ModuleInfo be allowed?
581             if (m !is null)
582             {
583                 if (auto res = dg(m))
584                     return res;
585             }
586         }
587     }
588     return 0;
589 }
590 
591 /********************************************
592  * Module constructor and destructor routines.
593  */
594 
595 extern (C)
596 {
597 void rt_moduleCtor()
598 {
599     foreach (ref sg; SectionGroup)
600     {
601         sg.moduleGroup.sortCtors();
602         sg.moduleGroup.runCtors();
603     }
604 }
605 
606 void rt_moduleTlsCtor()
607 {
608     foreach (ref sg; SectionGroup)
609     {
610         sg.moduleGroup.runTlsCtors();
611     }
612 }
613 
614 void rt_moduleTlsDtor()
615 {
616     foreach_reverse (ref sg; SectionGroup)
617     {
618         sg.moduleGroup.runTlsDtors();
619     }
620 }
621 
622 void rt_moduleDtor()
623 {
624     foreach_reverse (ref sg; SectionGroup)
625     {
626         sg.moduleGroup.runDtors();
627         sg.moduleGroup.free();
628     }
629 }
630 
631 version (Win32)
632 {
633     // Alternate names for backwards compatibility with older DLL code
634     void _moduleCtor()
635     {
636         rt_moduleCtor();
637     }
638 
639     void _moduleDtor()
640     {
641         rt_moduleDtor();
642     }
643 
644     void _moduleTlsCtor()
645     {
646         rt_moduleTlsCtor();
647     }
648 
649     void _moduleTlsDtor()
650     {
651         rt_moduleTlsDtor();
652     }
653 }
654 }
655 
656 /********************************************
657  */
658 
659 void runModuleFuncs(alias getfp)(const(immutable(ModuleInfo)*)[] modules)
660 {
661     foreach (m; modules)
662     {
663         if (auto fp = getfp(m))
664             (*fp)();
665     }
666 }
667 
668 void runModuleFuncsRev(alias getfp)(const(immutable(ModuleInfo)*)[] modules)
669 {
670     foreach_reverse (m; modules)
671     {
672         if (auto fp = getfp(m))
673             (*fp)();
674     }
675 }
676 
677 unittest
678 {
679     static void assertThrown(T : Throwable, E)(lazy E expr, string msg)
680     {
681         try
682             expr;
683         catch (T)
684             return;
685         assert(0, msg);
686     }
687 
688     static void stub()
689     {
690     }
691 
692     static struct UTModuleInfo
693     {
694         this(uint flags)
695         {
696             mi._flags = flags;
697         }
698 
699         void setImports(immutable(ModuleInfo)*[] imports...)
700         {
701             import core.bitop;
702             assert(flags & MIimportedModules);
703 
704             immutable nfuncs = popcnt(flags & (MItlsctor|MItlsdtor|MIctor|MIdtor|MIictor));
705             immutable size = nfuncs * (void function()).sizeof +
706                 size_t.sizeof + imports.length * (ModuleInfo*).sizeof;
707             assert(size <= pad.sizeof);
708 
709             pad[nfuncs] = imports.length;
710             .memcpy(&pad[nfuncs+1], imports.ptr, imports.length * imports[0].sizeof);
711         }
712 
713         immutable ModuleInfo mi;
714         size_t[8] pad;
715         alias mi this;
716     }
717 
718     static UTModuleInfo mockMI(uint flags)
719     {
720         auto mi = UTModuleInfo(flags | MIimportedModules);
721         auto p = cast(void function()*)&mi.pad;
722         if (flags & MItlsctor) *p++ = &stub;
723         if (flags & MItlsdtor) *p++ = &stub;
724         if (flags & MIctor) *p++ = &stub;
725         if (flags & MIdtor) *p++ = &stub;
726         if (flags & MIictor) *p++ = &stub;
727         *cast(size_t*)p++ = 0; // number of imported modules
728         assert(cast(void*)p <= &mi + 1);
729         return mi;
730     }
731 
732     static void checkExp2(string testname, bool shouldThrow, string oncycle,
733         immutable(ModuleInfo*)[] modules,
734         immutable(ModuleInfo*)[] dtors=null,
735         immutable(ModuleInfo*)[] tlsdtors=null)
736     {
737         auto mgroup = ModuleGroup(modules);
738         mgroup.sortCtors(oncycle);
739 
740         // if we are expecting sort to throw, don't throw because of unexpected
741         // success!
742         if (!shouldThrow)
743         {
744             foreach (m; mgroup._modules)
745                 assert(!(m.flags & (MIctorstart | MIctordone)), testname);
746             assert(mgroup._ctors    == dtors, testname);
747             assert(mgroup._tlsctors == tlsdtors, testname);
748         }
749     }
750 
751     static void checkExp(string testname, bool shouldThrow,
752         immutable(ModuleInfo*)[] modules,
753         immutable(ModuleInfo*)[] dtors=null,
754         immutable(ModuleInfo*)[] tlsdtors=null)
755     {
756         checkExp2(testname, shouldThrow, "abort", modules, dtors, tlsdtors);
757     }
758 
759 
760     {
761         auto m0 = mockMI(0);
762         auto m1 = mockMI(0);
763         auto m2 = mockMI(0);
764         checkExp("no ctors", false, [&m0.mi, &m1.mi, &m2.mi]);
765     }
766 
767     {
768         auto m0 = mockMI(MIictor);
769         auto m1 = mockMI(0);
770         auto m2 = mockMI(MIictor);
771         auto mgroup = ModuleGroup([&m0.mi, &m1.mi, &m2.mi]);
772         checkExp("independent ctors", false, [&m0.mi, &m1.mi, &m2.mi]);
773     }
774 
775     {
776         auto m0 = mockMI(MIstandalone | MIctor);
777         auto m1 = mockMI(0);
778         auto m2 = mockMI(0);
779         auto mgroup = ModuleGroup([&m0.mi, &m1.mi, &m2.mi]);
780         checkExp("standalone ctor", false, [&m0.mi, &m1.mi, &m2.mi], [&m0.mi]);
781     }
782 
783     {
784         auto m0 = mockMI(MIstandalone | MIctor);
785         auto m1 = mockMI(MIstandalone | MIctor);
786         auto m2 = mockMI(0);
787         m1.setImports(&m0.mi);
788         checkExp("imported standalone => no dependency", false,
789                  [&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi]);
790     }
791 
792     {
793         auto m0 = mockMI(MIstandalone | MIctor);
794         auto m1 = mockMI(MIstandalone | MIctor);
795         auto m2 = mockMI(0);
796         m0.setImports(&m1.mi);
797         checkExp("imported standalone => no dependency (2)", false,
798                 [&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi]);
799     }
800 
801     {
802         auto m0 = mockMI(MIstandalone | MIctor);
803         auto m1 = mockMI(MIstandalone | MIctor);
804         auto m2 = mockMI(0);
805         m0.setImports(&m1.mi);
806         m1.setImports(&m0.mi);
807         checkExp("standalone may have cycle", false,
808                 [&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi]);
809     }
810 
811     {
812         auto m0 = mockMI(MIctor);
813         auto m1 = mockMI(MIctor);
814         auto m2 = mockMI(0);
815         m1.setImports(&m0.mi);
816         checkExp("imported ctor => ordered ctors", false,
817                 [&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi], []);
818     }
819 
820     {
821         auto m0 = mockMI(MIctor);
822         auto m1 = mockMI(MIctor);
823         auto m2 = mockMI(0);
824         m0.setImports(&m1.mi);
825         checkExp("imported ctor => ordered ctors (2)", false,
826                 [&m0.mi, &m1.mi, &m2.mi], [&m1.mi, &m0.mi], []);
827     }
828 
829     {
830         auto m0 = mockMI(MIctor);
831         auto m1 = mockMI(MIctor);
832         auto m2 = mockMI(0);
833         m0.setImports(&m1.mi);
834         m1.setImports(&m0.mi);
835         assertThrown!Throwable(checkExp("", true, [&m0.mi, &m1.mi, &m2.mi]),
836                 "detects ctors cycles");
837         assertThrown!Throwable(checkExp2("", true, "deprecate",
838                                         [&m0.mi, &m1.mi, &m2.mi]),
839                 "detects ctors cycles (dep)");
840     }
841 
842     {
843         auto m0 = mockMI(MIctor);
844         auto m1 = mockMI(MIctor);
845         auto m2 = mockMI(0);
846         m0.setImports(&m2.mi);
847         m1.setImports(&m2.mi);
848         m2.setImports(&m0.mi, &m1.mi);
849         assertThrown!Throwable(checkExp("", true, [&m0.mi, &m1.mi, &m2.mi]),
850                 "detects cycle with repeats");
851     }
852 
853     {
854         auto m0 = mockMI(MIctor);
855         auto m1 = mockMI(MIctor);
856         auto m2 = mockMI(MItlsctor);
857         m0.setImports(&m1.mi, &m2.mi);
858         checkExp("imported ctor/tlsctor => ordered ctors/tlsctors", false,
859                 [&m0.mi, &m1.mi, &m2.mi], [&m1.mi, &m0.mi], [&m2.mi]);
860     }
861 
862     {
863         auto m0 = mockMI(MIctor | MItlsctor);
864         auto m1 = mockMI(MIctor);
865         auto m2 = mockMI(MItlsctor);
866         m0.setImports(&m1.mi, &m2.mi);
867         checkExp("imported ctor/tlsctor => ordered ctors/tlsctors (2)", false,
868                 [&m0.mi, &m1.mi, &m2.mi], [&m1.mi, &m0.mi], [&m2.mi, &m0.mi]);
869     }
870 
871     {
872         auto m0 = mockMI(MIctor);
873         auto m1 = mockMI(MIctor);
874         auto m2 = mockMI(MItlsctor);
875         m0.setImports(&m1.mi, &m2.mi);
876         m2.setImports(&m0.mi);
877         checkExp("no cycle between ctors/tlsctors", false,
878                 [&m0.mi, &m1.mi, &m2.mi], [&m1.mi, &m0.mi], [&m2.mi]);
879     }
880 
881     {
882         auto m0 = mockMI(MItlsctor);
883         auto m1 = mockMI(MIctor);
884         auto m2 = mockMI(MItlsctor);
885         m0.setImports(&m2.mi);
886         m2.setImports(&m0.mi);
887         assertThrown!Throwable(checkExp("", true, [&m0.mi, &m1.mi, &m2.mi]),
888                 "detects tlsctors cycle");
889         assertThrown!Throwable(checkExp2("", true, "deprecate",
890                                          [&m0.mi, &m1.mi, &m2.mi]),
891                 "detects tlsctors cycle (dep)");
892     }
893 
894     {
895         auto m0 = mockMI(MItlsctor);
896         auto m1 = mockMI(MIctor);
897         auto m2 = mockMI(MItlsctor);
898         m0.setImports(&m1.mi);
899         m1.setImports(&m0.mi, &m2.mi);
900         m2.setImports(&m1.mi);
901         assertThrown!Throwable(checkExp("", true, [&m0.mi, &m1.mi, &m2.mi]),
902                 "detects tlsctors cycle with repeats");
903     }
904 
905     {
906         auto m0 = mockMI(MIctor);
907         auto m1 = mockMI(MIstandalone | MIctor);
908         auto m2 = mockMI(MIstandalone | MIctor);
909         m0.setImports(&m1.mi);
910         m1.setImports(&m2.mi);
911         m2.setImports(&m0.mi);
912         // NOTE: this is implementation dependent, sorted order shouldn't be tested.
913         checkExp("closed ctors cycle", false, [&m0.mi, &m1.mi, &m2.mi],
914                 [&m1.mi, &m2.mi, &m0.mi]);
915         //checkExp("closed ctors cycle", false, [&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi, &m2.mi]);
916     }
917 }
918 
919 version (CRuntime_Microsoft)
920 {
921     // Dummy so Win32 code can still call it
922     extern(C) void _minit() { }
923 }