www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Is this really intended??

reply claptrap <clap trap.com> writes:
struct Foo
{
     int i;
     void opAssign(int rhs) { this.i = rhs; }
     void reset() { i = 0; }
}

class Bar
{
     Foo foo;

     this()
     {
         foo.i = 0;    // *1
         foo.reset();  // *2
         foo = 42;
     }
}

With *1 & *2 commented out...

onlineapp.d(19): Error: cannot implicitly convert expression x of 
type int to Foo
onlineapp.d(19):        this.foo = x is the first assignment of 
this.foo therefore it represents its initialization
onlineapp.d(19):        opAssign methods are not used for 
initialization, but for subsequent assignments

With *2 commented out it compiles with no errors

With *1 commented out...

onlineapp.d(19): Error: cannot implicitly convert expression x of 
type int to Foo
onlineapp.d(19):        this.foo = x is the first assignment of 
this.foo therefore it represents its initialization
onlineapp.d(19):        opAssign methods are not used for 
initialization, but for subsequent assignments

Arn't structs supposed to be default initialised?

But in a constructor a struct member is not? If you write to a 
field in a struct member suddenly it is considered initialised?

And yet it's OK to call a method on the struct that is not 
initialised, but if you do it's still uninitialised?

Why should

foo.i = 0;
foo.reset();

Result in the compiler changing whether it's OK to call opAssign?

That's some weird ****.
Oct 10 2020
next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Sunday, 11 October 2020 at 00:16:53 UTC, claptrap wrote:
 Arn't structs supposed to be default initialised?

 But in a constructor a struct member is not? If you write to a 
 field in a struct member suddenly it is considered initialised?

 And yet it's OK to call a method on the struct that is not 
 initialised, but if you do it's still uninitialised?

 Why should

 foo.i = 0;
 foo.reset();

 Result in the compiler changing whether it's OK to call 
 opAssign?
The first assignment to a member inside a constructor is considered initialization so that you can use constructors to initialize immutable members: struct Example { immutable int i; this(int i) { this.i = i; // ok, initialization this.i = 42; // error, assignment to immutable variable } } One consequence of this is that the first assignment does not call opAssign, since opAssign isn't used for initialization. I agree that this is kind of a hack. A more principled way to handle this would be to introduce a separate syntax for initialization, like `let this.i = i` or `this.i := i`.
Oct 10 2020
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Sunday, 11 October 2020 at 00:34:57 UTC, Paul Backus wrote:
 The first assignment to a member inside a constructor is 
 considered initialization so that you can use constructors to 
 initialize immutable members:
This isn't what's weird here though, the bizarre thing is it lets you call the method on the "uninitialized" member, then proceed to initialize it afterward. The rest of it is justified, but that particular aspect is bizarre and I can't justify that...
Oct 10 2020
parent reply claptrap <clap trap.com> writes:
On Sunday, 11 October 2020 at 00:39:54 UTC, Adam D. Ruppe wrote:
 On Sunday, 11 October 2020 at 00:34:57 UTC, Paul Backus wrote:
 The first assignment to a member inside a constructor is 
 considered initialization so that you can use constructors to 
 initialize immutable members:
This isn't what's weird here though, the bizarre thing is it lets you call the method on the "uninitialized" member, then proceed to initialize it afterward. The rest of it is justified, but that particular aspect is bizarre and I can't justify that...
I though structs were default initialised? I mean I remember reading here that one of the design axioms of structs is that that have a simple bitblit for initialisation? Is that not done for members if their enclosing aggregate has a constructor? If you have... struct Foo { int i; } struct Bar { Foo foo; this() {} } Is foo not initialised before entry to Bars constructor? It cant be after, so it must be before, or is it not called at all if foo is explicitly initialised in Foo.this?
Oct 11 2020
parent claptrap <clap trap.com> writes:
On Sunday, 11 October 2020 at 09:55:31 UTC, claptrap wrote:
 if foo is explicitly initialised in Foo.this?
I mean "Bar.this"
Oct 11 2020
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 10/10/20 8:16 PM, claptrap wrote:
 struct Foo
 {
      int i;
      void opAssign(int rhs) { this.i = rhs; }
      void reset() { i = 0; }
 }
 
 class Bar
 {
      Foo foo;
 
      this()
      {
          foo.i = 0;    // *1
          foo.reset();  // *2
          foo = 42;
      }
 }
 
 With *1 & *2 commented out...
 
 onlineapp.d(19): Error: cannot implicitly convert expression x of type 
 int to Foo
 onlineapp.d(19):        this.foo = x is the first assignment of
this.foo 
 therefore it represents its initialization
 onlineapp.d(19):        opAssign methods are not used for 
 initialization, but for subsequent assignments
Hm.. yeah, this seems not right. The compiler should just go ahead and initialize it if you try to call any methods on it (including opAssign) before initialization. note that if you define a constructor for Foo that takes an int, it would work (and use that when you assigned it to 42). But it shouldn't need this, IMO. What's really bizarre is if you comment *1 out, it still complains. If you called reset on it, is it not already initialized???!
 
 With *2 commented out it compiles with no errors
 
 With *1 commented out...
 
 onlineapp.d(19): Error: cannot implicitly convert expression x of type 
 int to Foo
 onlineapp.d(19):        this.foo = x is the first assignment of
this.foo 
 therefore it represents its initialization
 onlineapp.d(19):        opAssign methods are not used for 
 initialization, but for subsequent assignments
 
 Arn't structs supposed to be default initialised?
 
 But in a constructor a struct member is not? If you write to a field in 
 a struct member suddenly it is considered initialised?
 
 And yet it's OK to call a method on the struct that is not initialised, 
 but if you do it's still uninitialised?
 
 Why should
 
 foo.i = 0;
 foo.reset();
 
 Result in the compiler changing whether it's OK to call opAssign?
 
 That's some weird ****.
This makes no sense. I think this is worth a bug report. The compiler should be a bit smarter (dumber?) on flow checking here. -Steve
Oct 11 2020
parent reply claptrap <clap trap.com> writes:
On Sunday, 11 October 2020 at 18:57:55 UTC, Steven Schveighoffer 
wrote:
 On 10/10/20 8:16 PM, claptrap wrote:
 struct Foo
 {
      int i;
      void opAssign(int rhs) { this.i = rhs; }
      void reset() { i = 0; }
 }
 
 class Bar
 {
      Foo foo;
 
      this()
      {
          foo.i = 0;    // *1
          foo.reset();  // *2
          foo = 42;
      }
 }
 
 With *1 & *2 commented out...
 
 onlineapp.d(19): Error: cannot implicitly convert expression x 
 of type int to Foo
 onlineapp.d(19):        this.foo = x is the first assignment 
 of this.foo therefore it represents its initialization
 onlineapp.d(19):        opAssign methods are not used for 
 initialization, but for subsequent assignments
Hm.. yeah, this seems not right. The compiler should just go ahead and initialize it if you try to call any methods on it (including opAssign) before initialization.
I think the struct should be initialised full stop. I mean whether a member is initialised before the constructor is run shouldn't be dependent on what you do in the constructor. I mean you declare a struct, whether local or as part of an aggregate, it should be default initialised. Its a simple orthogonal rule. If it's to do with being able to modify immutable members in the constructor that should be a separate rule. Either "You can modify immutable members in a constructor", or "you can construct immutable members in a constructor, but only once and it must be the first action on the member" If there's a question of wasted effort initialising a member twice, the compiler should be able to elide the default initialiser, at least in simple cases the mechanism is already there. They should be separate orthogonal rules.
Oct 11 2020
next sibling parent Paul Backus <snarwin gmail.com> writes:
On Sunday, 11 October 2020 at 21:50:28 UTC, claptrap wrote:
 I think the struct should be initialised full stop. I mean 
 whether a member is initialised before the constructor is run 
 shouldn't be dependent on what you do in the constructor. I 
 mean you declare a struct, whether local or as part of an 
 aggregate, it should be default initialised. Its a simple 
 orthogonal rule.
This is how the language already works, at least according to the spec: When an instance of a struct is created, the following steps happen: 1. The raw data is statically initialized using the values provided in the struct definition. This operation is equivalent to doing a memory copy of a static version of the object onto the newly allocated one. 2. If there is a constructor defined for the struct, the constructor matching the argument list is called. 3. If struct invariant checking is turned on, the struct invariant is called at the end of the constructor. Source: https://dlang.org/spec/struct.html#struct-instantiation
Oct 11 2020
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 10/11/20 5:50 PM, claptrap wrote:
 On Sunday, 11 October 2020 at 18:57:55 UTC, Steven Schveighoffer wrote:
 On 10/10/20 8:16 PM, claptrap wrote:
 struct Foo
 {
      int i;
      void opAssign(int rhs) { this.i = rhs; }
      void reset() { i = 0; }
 }

 class Bar
 {
      Foo foo;

      this()
      {
          foo.i = 0;    // *1
          foo.reset();  // *2
          foo = 42;
      }
 }

 With *1 & *2 commented out...

 onlineapp.d(19): Error: cannot implicitly convert expression x of 
 type int to Foo
 onlineapp.d(19):        this.foo = x is the first assignment of 
 this.foo therefore it represents its initialization
 onlineapp.d(19):        opAssign methods are not used for 
 initialization, but for subsequent assignments
Hm.. yeah, this seems not right. The compiler should just go ahead and initialize it if you try to call any methods on it (including opAssign) before initialization.
I think the struct should be initialised full stop. I mean whether a member is initialised before the constructor is run shouldn't be dependent on what you do in the constructor. I mean you declare a struct, whether local or as part of an aggregate, it should be default initialised. Its a simple orthogonal rule.
I didn't say it right - I mean that if you call a method on a struct, it should be treated as "initialized". It already initializes all the memory anyway.
 If it's to do with being able to modify immutable members in the 
 constructor that should be a separate rule. Either "You can modify 
 immutable members in a constructor", or "you can construct immutable 
 members in a constructor, but only once and it must be the first action 
 on the member"
It has to do with this: struct S { this(int) { writeln("ctor"); } void opAssign(int) { writeln("assignment"); } } S s = 5; // ctor s = 5; // assignment It is treating the first assignment of a member in a struct ctor as the initializer for the member. But if you have no ctor that matches, I see no reason why it shouldn't treat this as initializing (via default init), AND THEN calling opAssign on that. It also makes NO sense to treat any usage of the struct after calling a member function on it as initializing. That seems to me a bug in the compiler implementation. That first function could have done anything to the struct.
 If there's a question of wasted effort initialising a member twice, the 
 compiler should be able to elide the default initialiser, at least in 
 simple cases the mechanism is already there.
 
 They should be separate orthogonal rules.
The default initializer is happening anyway -- the opAssign or constructor is expecting it. -Steve
Oct 11 2020
parent claptrap <clap trap.com> writes:
On Sunday, 11 October 2020 at 22:11:48 UTC, Steven Schveighoffer 
wrote:
 On 10/11/20 5:50 PM, claptrap wrote:
 On Sunday, 11 October 2020 at 18:57:55 UTC, Steven 
 Schveighoffer wrote:
 On 10/10/20 8:16 PM, claptrap wrote:
If there's a question of wasted effort initialising a member twice, the compiler should be able to elide the default initialiser, at least in simple cases the mechanism is already there. They should be separate orthogonal rules.
The default initializer is happening anyway -- the opAssign or constructor is expecting it.
I didn't realise the default initialiser and constructor were separate required events. I thought default initialisation was enough, the constructors is there if you want something more than the default init. I guess that the error message isn't very clear either since it talks about initialisation when really it's construction that is needed. I mean if the language is going to differentiate between initialisation and construction, the error message should be clear which one is missing. In fact if the struct has no constructors it can only be default initialised anyway, so it's seems pointless for the compiler to require a constructor to be called when it's essentially a NOP. I mean if the struct has no constructor, then it should be considered fully constructed after the default init.
Oct 13 2020