www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Adding empty static this() causes exception

reply Joseph <JE342 gmail.com> writes:
I have two nearly duplicate files I added a static this() to 
initialize some static members of an interface.

On one file when I add an empty static this() it crashes while 
the other one does not.

The exception that happens is
Cyclic dependency between module A and B.

Why does this occur on an empty static this? Is it being ran 
twice or something? Anyway to fix this?

Seriously, simply adding static this() { } to module B crashes 
the program ;/ module A and module B both import each other 
because there are types that they need to share but that is 
all(one uses an enum of the other and vice versa).
Sep 12 2017
next sibling parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Tuesday, 12 September 2017 at 09:11:20 UTC, Joseph wrote:
 I have two nearly duplicate files I added a static this() to 
 initialize some static members of an interface.

 On one file when I add an empty static this() it crashes while 
 the other one does not.

 The exception that happens is
 Cyclic dependency between module A and B.

 Why does this occur on an empty static this? Is it being ran 
 twice or something? Anyway to fix this?
The compiler errors because the spec states [1]
 Each module is assumed to depend on any imported modules being 
 statically constructed first
, which means two modules that import each other and both use static construction have no valid static construction order. One reason, I think, why the spec states that is because in theory it would not always be possible for the compiler to decide the order, e.g. when executing them changes the ("shared") execution environment's state: --- module a; import b; static this() { // Does something to the OS state syscall_a(); } --- --- module b; import a; static this() { // Also does something to the OS state syscall_b(); } --- The "fix" as I see it would be to either not use static construction in modules that import each other, or propose a set of rules for the spec that define a always solvable subset for the compiler. [1] https://dlang.org/spec/module.html#order_of_static_ctor
Sep 12 2017
parent reply Joseph <JE342 gmail.com> writes:
On Tuesday, 12 September 2017 at 10:08:11 UTC, Moritz Maxeiner 
wrote:
 On Tuesday, 12 September 2017 at 09:11:20 UTC, Joseph wrote:
 I have two nearly duplicate files I added a static this() to 
 initialize some static members of an interface.

 On one file when I add an empty static this() it crashes while 
 the other one does not.

 The exception that happens is
 Cyclic dependency between module A and B.

 Why does this occur on an empty static this? Is it being ran 
 twice or something? Anyway to fix this?
The compiler errors because the spec states [1]
 Each module is assumed to depend on any imported modules 
 being statically constructed first
, which means two modules that import each other and both use static construction have no valid static construction order. One reason, I think, why the spec states that is because in theory it would not always be possible for the compiler to decide the order, e.g. when executing them changes the ("shared") execution environment's state: --- module a; import b; static this() { // Does something to the OS state syscall_a(); } --- --- module b; import a; static this() { // Also does something to the OS state syscall_b(); } --- The "fix" as I see it would be to either not use static construction in modules that import each other, or propose a set of rules for the spec that define a always solvable subset for the compiler. [1] https://dlang.org/spec/module.html#order_of_static_ctor
The compiler shouldn't arbitrarily force one to make arbitrary decisions that waste time and money. My solution was to turn those static this's in to functions and simply call them at at the start of main(). Same effect yet doesn't crash. The compiler should only run the static this's once per module load anyways, right? If it is such a problem then some way around it should be included: force static this() { } ? The compiler shouldn't make assumptions about the code I write and always choose the worse case, it becomes an unfriendly relationship at that point.
Sep 12 2017
next sibling parent Moritz Maxeiner <moritz ucworks.org> writes:
On Tuesday, 12 September 2017 at 19:59:52 UTC, Joseph wrote:
 On Tuesday, 12 September 2017 at 10:08:11 UTC, Moritz Maxeiner 
 wrote:
 On Tuesday, 12 September 2017 at 09:11:20 UTC, Joseph wrote:
 I have two nearly duplicate files I added a static this() to 
 initialize some static members of an interface.

 On one file when I add an empty static this() it crashes 
 while the other one does not.

 The exception that happens is
 Cyclic dependency between module A and B.

 Why does this occur on an empty static this? Is it being ran 
 twice or something? Anyway to fix this?
The compiler errors because the spec states [1]
 Each module is assumed to depend on any imported modules 
 being statically constructed first
, which means two modules that import each other and both use static construction have no valid static construction order. One reason, I think, why the spec states that is because in theory it would not always be possible for the compiler to decide the order, e.g. when executing them changes the ("shared") execution environment's state: --- module a; import b; static this() { // Does something to the OS state syscall_a(); } --- --- module b; import a; static this() { // Also does something to the OS state syscall_b(); } --- The "fix" as I see it would be to either not use static construction in modules that import each other, or propose a set of rules for the spec that define a always solvable subset for the compiler. [1] https://dlang.org/spec/module.html#order_of_static_ctor
The compiler shouldn't arbitrarily force one to make arbitrary decisions that waste time and money.
My apologies, I confused compiler and runtime when writing that reply (the detection algorithm resulting in your crash is built into druntime). The runtime, however, is compliant with the spec on this AFAICT.
 The compiler should only run the static this's once per module 
 load anyways, right?
Static module constructors are run once per module per thread [1] (if you want once per module you need shared static module constructors).
 If it is such a problem then some way around it should be 
 included:  force static this() { } ?
The only current workaround is what Biotronic mentioned: You can customize the druntime cycle detection via the --DRT-oncycle command line option [2].
 The compiler shouldn't make assumptions about the code I write 
 and always choose the worse case, it becomes an unfriendly 
 relationship at that point.
If your point remains when replacing 'compiler' with 'runtime': It makes no assumptions in the case you described, it enforces the language specification. [1] https://dlang.org/spec/module.html#staticorder [2] https://dlang.org/spec/module.html#override_cycle_abort
Sep 12 2017
prev sibling next sibling parent nkm1 <t4nk074 openmailbox.org> writes:
On Tuesday, 12 September 2017 at 19:59:52 UTC, Joseph wrote:
 The compiler shouldn't arbitrarily force one to make arbitrary 
 decisions that waste time and money.
You might want to educate yourself about arbitrary decisions that waste time and money: https://isocpp.org/wiki/faq/ctors#static-init-order
 My solution was to turn those static this's in to functions and 
 simply call them at at the start of main(). Same effect yet 
 doesn't crash. The compiler should only run the static this's 
 once per module load anyways, right? If it is such a problem 
 then some way around it should be included:  force static 
 this() { } ?
There is a way, turn static this's into functions and simply call them at the start of main().
 The compiler shouldn't make assumptions about the code I write 
 and always choose the worse case, it becomes an unfriendly 
 relationship at that point.
If you want C++, you know where to find it, although I wouldn't exactly call it "friendly".
Sep 12 2017
prev sibling next sibling parent lobo <swamp.lobo gmail.com> writes:
On Tuesday, 12 September 2017 at 19:59:52 UTC, Joseph wrote:
 On Tuesday, 12 September 2017 at 10:08:11 UTC, Moritz Maxeiner 
 wrote:
 [...]
The compiler shouldn't arbitrarily force one to make arbitrary decisions that waste time and money. My solution was to turn those static this's in to functions and simply call them at at the start of main(). Same effect yet doesn't crash. The compiler should only run the static this's once per module load anyways, right? If it is such a problem then some way around it should be included: force static this() { } ? The compiler shouldn't make assumptions about the code I write and always choose the worse case, it becomes an unfriendly relationship at that point.
Solution is to redesign so there is no cycle because it is brittle and generally due to poor architecture. Like goto cyclic dependencies are occasionally useful, rarely necessary and avoid by default. bye, lobo
Sep 12 2017
prev sibling parent Biotronic <simen.kjaras gmail.com> writes:
On Tuesday, 12 September 2017 at 19:59:52 UTC, Joseph wrote:
 The compiler shouldn't arbitrarily force one to make arbitrary 
 decisions that waste time and money.
Like having a type system? Having to do *cast(int*)&s to interpret a string as an int isn't strictly necessary, and wastes dev time when they have to type extra letters. Throwing an exception when there are import cycles that may cause problems is absolutely the correct thing to do. It's a choice between hard-to-figure-out errors that may depend on the order in which files were passed to the linker, and getting an exception before you've even started running your tests. If we could get this message at compile-time, that would of course be better, but that cannot be done in the general case due to separate compilation. If you want such a message in the cases where it is possible, feel free to create a pull request. It would be possible to add a way to say 'ignore cycles for this module ctor', but as has been pointed out, cycles are generally a symptom of poor architecture, are brittle, and can almost always be avoided (and when they can't, there's a way around that too). -- Biotronic
Sep 13 2017
prev sibling parent reply Biotronic <simen.kjaras gmail.com> writes:
On Tuesday, 12 September 2017 at 09:11:20 UTC, Joseph wrote:
 I have two nearly duplicate files I added a static this() to 
 initialize some static members of an interface.

 On one file when I add an empty static this() it crashes while 
 the other one does not.

 The exception that happens is
 Cyclic dependency between module A and B.

 Why does this occur on an empty static this? Is it being ran 
 twice or something? Anyway to fix this?

 Seriously, simply adding static this() { } to module B crashes 
 the program ;/ module A and module B both import each other 
 because there are types that they need to share but that is 
 all(one uses an enum of the other and vice versa).
https://dlang.org/spec/module.html#order_of_static_ctor "Cycles (circular dependencies) in the import declarations are allowed as long as not both of the modules contain static constructors or static destructors. Violation of this rule will result in a runtime exception." So if you have a static this() in both A and B, and A imports B and B imports A, you will get this error message. You can pass --DRT-oncycle=ignore to the program to hide the problem, but a better solution is to find a way to live with fewer static this()s. The reason this exception is thrown is that one module's static this() might otherwise depend on another module's static this that depends on the first module again, and there is no good way to check if it actually depends or just potentially. -- Biotronic
Sep 12 2017
parent Biotronic <simen.kjaras gmail.com> writes:
The simplest example of a cycle is probably this:

module A;
import B;

int n1 = 17;
static this() {
     n1 = n2;
}

//
module B;
import A;

int n2 = 42;
static this() {
     n2 = n1;
}

What's the value of n1 and n2 after module constructors are run? 
Since both module constructors can run arbitrary code, it's 
impossible to prove in the general case whether one of them 
depends on the other.

--
   Biotronic
Sep 12 2017