Thread overview
Could static ctors / module init be a little more intelligent?
Jul 14, 2005
Ben Hinkle
Jul 14, 2005
Ben Hinkle
Jul 15, 2005
Ben Hinkle
Jul 15, 2005
Walter
Jul 15, 2005
Uwe Salomon
July 14, 2005
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?


July 14, 2005
"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.
[snip]
> 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.


July 14, 2005
"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.


July 14, 2005
"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.


July 15, 2005
"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..


July 15, 2005
"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.


July 15, 2005
"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
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.

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.


July 15, 2005
> 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