www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Could static ctors / module init be a little more intelligent?

reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
Say I have two modules named A and B.

Each module needs to know of a class from the other module.

Each module also needs a static ctor.

Neither static ctor refers to the other module, only to stuff in the current 
module.

However, when run, I get the error "Error: circular initialization 
dependency with module A."

This is the result of some rather simplistic dependence determination on the 
part of the compiler.  Both have static ctors, and both import each other, 
therefore, it _must_ be a circular dependency.

But look at this:

[A.d]
module a;

private import b;

int x;

static this()
{
 x=5;
}

void fork()
{
    x=y;
}

[B.d]
module b;

private import b;

int y;

static this()
{
 y=10;
}

void knife()
{
    y=x;
}

As you can see, neither module requires for the other module to be 
initialized when it is.  But I get the error at runtime, nonetheless.

This is irritating.  Could the check for "depended-upon module 
initialization" be done when a member is accessed in the static ctor() 
rather than unconditionally? 
Jul 14 2005
parent reply "Ben Hinkle" <bhinkle mathworks.com> writes:
"Jarrett Billingsley" <kb3ctd2 yahoo.com> wrote in message 
news:db6867$4oa$1 digitaldaemon.com...
 Say I have two modules named A and B.

 Each module needs to know of a class from the other module.

 Each module also needs a static ctor.

 Neither static ctor refers to the other module, only to stuff in the 
 current module.

 However, when run, I get the error "Error: circular initialization 
 dependency with module A."

 This is the result of some rather simplistic dependence determination on 
 the part of the compiler.  Both have static ctors, and both import each 
 other, therefore, it _must_ be a circular dependency.

 This is irritating.  Could the check for "depended-upon module 
 initialization" be done when a member is accessed in the static ctor() 
 rather than unconditionally?

What about when the static ctor calls something that accesses the other module? I think the current rule is the most reasonable since trying to figure out the run-time dependencies is impossible in general.
Jul 14 2005
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Ben Hinkle" <bhinkle mathworks.com> wrote in message 
news:db6k70$fd1$1 digitaldaemon.com...
 This is irritating.  Could the check for "depended-upon module 
 initialization" be done when a member is accessed in the static ctor() 
 rather than unconditionally?

What about when the static ctor calls something that accesses the other module?

But that's what I said - the check to see if the other module is initialized should be done before a member of it is accessed in the module ctor, instead of before the module ctor is run.
 I think the current rule is the most reasonable since trying to figure out 
 the run-time dependencies is impossible in general.

That's not entirely true. At least for the static ctors, it would be possible to see if any members of an imported module are used. In the semantic pass, see if anything resolves to a symbol declared in the imported module - if so, that module depends upon the imported module to be initialized before. This way, the compiler would be able to determine the dependence graphs, and true circular dependencies could be reported at compile-time.
Jul 14 2005
parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
"Jarrett Billingsley" <kb3ctd2 yahoo.com> wrote in message 
news:db6m5e$gi6$1 digitaldaemon.com...
 "Ben Hinkle" <bhinkle mathworks.com> wrote in message 
 news:db6k70$fd1$1 digitaldaemon.com...
 This is irritating.  Could the check for "depended-upon module 
 initialization" be done when a member is accessed in the static ctor() 
 rather than unconditionally?

What about when the static ctor calls something that accesses the other module?

But that's what I said - the check to see if the other module is initialized should be done before a member of it is accessed in the module ctor, instead of before the module ctor is run.

Maybe I don't understand. What I meant was: module a; import b; static this() { foo(); } void foo(){bar();} module b; import a; statis this(){bar();} void bar(){printf("who wins?\n");} In order for dmd to know that a's ctor depends on b but b's ctor doesn't it has to track the run-time behavior. Imagine now that the logic before calling bar is arbitrarily complex. Imagine if foo was void foo(){ if (hell is frozen) bar(); }
 I think the current rule is the most reasonable since trying to figure 
 out the run-time dependencies is impossible in general.

That's not entirely true. At least for the static ctors, it would be possible to see if any members of an imported module are used. In the semantic pass, see if anything resolves to a symbol declared in the imported module - if so, that module depends upon the imported module to be initialized before. This way, the compiler would be able to determine the dependence graphs, and true circular dependencies could be reported at compile-time.

It just seems like a slippeery slope to me.
Jul 14 2005
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Ben Hinkle" <ben.hinkle gmail.com> wrote in message 
news:db6q3a$jfk$1 digitaldaemon.com...
 Maybe I don't understand. What I meant was:
  module a;
  import b;
  static this() { foo(); }
  void foo(){bar();}

  module b;
  import a;
  statis this(){bar();}
  void bar(){printf("who wins?\n");}

 In order for dmd to know that a's ctor depends on b but b's ctor doesn't 
 it has to track the run-time behavior. Imagine now that the logic before 
 calling bar is arbitrarily complex. Imagine if foo was
  void foo(){ if (hell is frozen) bar(); }

I didn't think about having code like you posted. Now this fits right in with determining whether to issue an error for "function does not return a value" or not. I'm sorry for cluttering up the board with so many half-thought-out suggestions..
Jul 14 2005
parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
"Jarrett Billingsley" <kb3ctd2 yahoo.com> wrote in message 
news:db6um6$mn0$1 digitaldaemon.com...
 "Ben Hinkle" <ben.hinkle gmail.com> wrote in message 
 news:db6q3a$jfk$1 digitaldaemon.com...
 Maybe I don't understand. What I meant was:
  module a;
  import b;
  static this() { foo(); }
  void foo(){bar();}

  module b;
  import a;
  statis this(){bar();}
  void bar(){printf("who wins?\n");}

 In order for dmd to know that a's ctor depends on b but b's ctor doesn't 
 it has to track the run-time behavior. Imagine now that the logic before 
 calling bar is arbitrarily complex. Imagine if foo was
  void foo(){ if (hell is frozen) bar(); }

I didn't think about having code like you posted. Now this fits right in with determining whether to issue an error for "function does not return a value" or not. I'm sorry for cluttering up the board with so many half-thought-out suggestions..

Don't be sorry! I remember fairly recent long threads about exactly what you are talking about: circular module dependencies and the semantics of module ctors. I think it will come up again, too. Over time I wouldn't be surprised if there evolves a way to tell the compiler that a's ctor depends on b but b's doesn't depend on a. What that syntax would look like is anyone guess.
Jul 14 2005
parent reply "Walter" <newshound digitalmars.com> writes:
"Ben Hinkle" <ben.hinkle gmail.com> wrote in message
news:db70l8$o5d$1 digitaldaemon.com...
 Don't be sorry! I remember fairly recent long threads about exactly what

 are talking about: circular module dependencies and the semantics of

 ctors. I think it will come up again, too. Over time I wouldn't be

 if there evolves a way to tell the compiler that a's ctor depends on b but
 b's doesn't depend on a. What that syntax would look like is anyone guess.

Sometimes I think a simple rule that is easilly explained and understood is better than a complex rule with many border cases, even if the latter does handle a few more cases.
Jul 14 2005
parent "Uwe Salomon" <post uwesalomon.de> writes:
 Sometimes I think a simple rule that is easilly explained and understood  
 is
 better than a complex rule with many border cases, even if the latter  
 does handle a few more cases.

Yes, that's it. We should really avoid cluttering the language with "small, simple" fixes to support every feature one could possibly imagine. Most of the time, module initializers are not needed anyways (as with constructors), and for the other cases one could either redesign the module dependencies, or simply initialize the second module from the first: module initialize.me; private import initialize.myself; package MyStruc* myStruc; module initialize.myself; private import initialize.me; package MyStruc* myOtherStruc; static this() { myOtherStruc = new MyStruc; initialize.me.myStruc = new MyStruc; } Or something similar... Ciao uwe
Jul 14 2005