Thread overview
Bug: constructor calls
Jan 06, 2004
Hauke Duden
Re: constructor calls
Jan 10, 2004
Walter
Jan 10, 2004
Hauke Duden
Jan 17, 2004
Walter
Jan 18, 2004
Hauke Duden
Jan 18, 2004
Vathix
Jan 17, 2004
Andy Friesen
Jan 18, 2004
Hauke Duden
January 06, 2004
This works:

class Foo1
{
  this(bool b)
  {
    if(b)
      val=14;
  }
		
  this()
  {
    val=7;
  }
	
protected:
  int val;
}


This doesn't (Error: one path skips constructor:


class Foo2
{
  this(bool b)
  {
    if(b)
      val=14;
    else
      this();	//<< crucial line
  }
		
  this()
  {
    val=7;
  }
	
protected:
  int val;
}


Why is the constructor in Foo1 acceptable, while the one in Foo2 is not? I believe this is a bug.

Hauke
January 10, 2004
"Hauke Duden" <H.NS.Duden@gmx.net> wrote in message news:btf4jn$1irs$1@digitaldaemon.com...
> This works:
>
> class Foo1
> {
>    this(bool b)
>    {
>      if(b)
>        val=14;
>    }
>
>    this()
>    {
>      val=7;
>    }
>
> protected:
>    int val;
> }
>
>
> This doesn't (Error: one path skips constructor:
>
>
> class Foo2
> {
>    this(bool b)
>    {
>      if(b)
>        val=14;
>      else
>        this(); //<< crucial line
>    }
>
>    this()
>    {
>      val=7;
>    }
>
> protected:
>    int val;
> }
>
>
> Why is the constructor in Foo1 acceptable, while the one in Foo2 is not? I believe this is a bug.

Actually, it is designed to work that way. The semantic phase does a rather primitive data flow analysis to determine that if another constructor is called on one path through the constructor, it must be called on all paths through.


January 10, 2004
Walter wrote:
> "Hauke Duden" <H.NS.Duden@gmx.net> wrote in message
> news:btf4jn$1irs$1@digitaldaemon.com...
> 
>>This works:
>>
>>class Foo1
>>{
>>   this(bool b)
>>   {
>>     if(b)
>>       val=14;
>>   }
>>
>>   this()
>>   {
>>     val=7;
>>   }
>>
>>protected:
>>   int val;
>>}
>>
>>
>>This doesn't (Error: one path skips constructor:
>>
>>
>>class Foo2
>>{
>>   this(bool b)
>>   {
>>     if(b)
>>       val=14;
>>     else
>>       this(); //<< crucial line
>>   }
>>
>>   this()
>>   {
>>     val=7;
>>   }
>>
>>protected:
>>   int val;
>>}
>>
>>
>>Why is the constructor in Foo1 acceptable, while the one in Foo2 is not?
>>I believe this is a bug.
> 
> 
> Actually, it is designed to work that way. The semantic phase does a rather
> primitive data flow analysis to determine that if another constructor is
> called on one path through the constructor, it must be called on all paths
> through.

So how can I circumvent this? In my real world example I don't want to call the default constructor because that would cause a lot of unnecessary stuff to happen (like allocating memory that is thrown away right afterwards).

Do I have to write a empty bogus constructor overload with an invented list of parameters that I can call from the first code path? That would be SUCH a hack...

This is one of the things you always wanted to prevent in D. I know this code is right and I just want the compiler to shut up :(.

And it is also very similar to requiring returns in all code paths. Why do you allow missing returns, but you don't allow missing constructor calls? At least in the return case it is easy to "make the compiler shut up". In the constructor case I don't even know how, except for that ugly invented empty constructor hack.

Hauke
January 17, 2004
"Hauke Duden" <H.NS.Duden@gmx.net> wrote in message news:btota8$1ijf$1@digitaldaemon.com...
> So how can I circumvent this? In my real world example I don't want to call the default constructor because that would cause a lot of unnecessary stuff to happen (like allocating memory that is thrown away right afterwards).
>
> Do I have to write a empty bogus constructor overload with an invented list of parameters that I can call from the first code path? That would be SUCH a hack...
>
> This is one of the things you always wanted to prevent in D. I know this code is right and I just want the compiler to shut up :(.
>
> And it is also very similar to requiring returns in all code paths. Why do you allow missing returns, but you don't allow missing constructor calls? At least in the return case it is easy to "make the compiler shut up". In the constructor case I don't even know how, except for that ugly invented empty constructor hack.

It's an interesting tradeoff, adding in the dummy constructor in one case, and leaving in a potential large source of inadvertent bugs. I think the thing to do is wait and see how things evolve a bit.


January 17, 2004
Hauke Duden wrote:
> This works:
> 
> [...]
>
> Why is the constructor in Foo1 acceptable, while the one in Foo2 is not? I believe this is a bug.
> 
> Hauke

Foo2 shouldn't have to (shouldn't be *able* to!) rely on the internal representation of Foo1.  What you're doing is very dangerous from a design or maintenance perspective.

I'm almost ready to say that it should be an error if each of Foo2's constructors do not call exactly one Foo1 constructor.

What you should do is something more like:

class Foo1
{
   // uber-paranoid.  Use the accessor even internally.
   this(int v) { val = v; }
   this()      { this(0); }

   int val()   { return _val; }
   void val(int v) { _val = v; }

private:  // not protected
   int _val;
}

class Foo2
{
   this(bool b)
   {
      if (b)   super();
      else     super(17);
   }
}

Of course, this is all effectively religious dogma, but I'd say it's a very good thing if Foo2 is completely incapable of breaking because of changes to Foo1's implementation.

 -- andy
January 18, 2004
Andy Friesen wrote:
> Hauke Duden wrote:
> 
>> This works:
>>
>> [...]
>>
>> Why is the constructor in Foo1 acceptable, while the one in Foo2 is not? I believe this is a bug.
>>
>> Hauke
> 
> 
> Foo2 shouldn't have to (shouldn't be *able* to!) rely on the internal representation of Foo1.  What you're doing is very dangerous from a design or maintenance perspective.

While this may be true in many cases, it is not in this particular case.  The base class actually does not have a constructor, because it is a completely abstract class. It doesn't even have any member variables, it is only there to provide default implementations for some interface functions. This kind of implementation helper for an interface is often a very big help.

Besides: the problem was not that I called a base class constructor - I called another constructor of the same class in one code branch and didn't in another.

Hauke
January 18, 2004
Walter wrote:
> "Hauke Duden" <H.NS.Duden@gmx.net> wrote in message
> news:btota8$1ijf$1@digitaldaemon.com...
> 
>>So how can I circumvent this? In my real world example I don't want to
>>call the default constructor because that would cause a lot of
>>unnecessary stuff to happen (like allocating memory that is thrown away
>>right afterwards).
>>
>>Do I have to write a empty bogus constructor overload with an invented
>>list of parameters that I can call from the first code path? That would
>>be SUCH a hack...
>>
>>This is one of the things you always wanted to prevent in D. I know this
>>code is right and I just want the compiler to shut up :(.
>>
>>And it is also very similar to requiring returns in all code paths. Why
>>do you allow missing returns, but you don't allow missing constructor
>>calls? At least in the return case it is easy to "make the compiler shut
>>up". In the constructor case I don't even know how, except for that ugly
>>invented empty constructor hack.
> 
> 
> It's an interesting tradeoff, adding in the dummy constructor in one case,
> and leaving in a potential large source of inadvertent bugs. I think the
> thing to do is wait and see how things evolve a bit.

I hereby make a motion to introduce the keyword "shaddap", that makes the compiler stop complaining about these kinds of warnings-turned-errors ;).

Hauke
January 18, 2004
Hauke Duden wrote:
> 
> I hereby make a motion to introduce the keyword "shaddap", that makes the compiler stop complaining about these kinds of warnings-turned-errors ;).
> 
> Hauke

There's a switch for this, that big button on your computer case :>