Jump to page: 1 2
Thread overview
Why allow initializers of non-static members that allocate?
Jun 10, 2022
Bastiaan Veelo
Jun 10, 2022
Mike Parker
Jun 10, 2022
Mike Parker
Jun 10, 2022
Bastiaan Veelo
Jun 11, 2022
matheus
Jun 11, 2022
Mike Parker
Jun 11, 2022
matheus
Jun 11, 2022
Salih Dincer
Jun 11, 2022
Bastiaan Veelo
Jun 11, 2022
Mike Parker
Jun 11, 2022
Bastiaan Veelo
Jun 10, 2022
Bastiaan Veelo
Jun 10, 2022
Mike Parker
Jun 10, 2022
Bastiaan Veelo
Jun 10, 2022
Salih Dincer
Jun 10, 2022
Arafel
June 10, 2022

I have been foolish enough to make a mistake like this:

struct S
{
    int[] arr = new int[](5);
}

This is terrible because

S s1;
S s2;
s2.arr[0] = 42;
writeln(s1.arr[0]); // 42 Gotcha!

Of course there are less obvious variants of the same mistake:

import std;

struct S
{
    A a = A(5);
}

struct A
{
    int[] arr;
    this (int l)
    {
        arr.length = l;
    }
}

void main()
{
    S s1;
    S s2;
    s2.a.arr[0] = 42;
    writeln(s1.a.arr[0]); // 42 :-(
}

Is there a use case where this makes sense? I would have much appreciated the compiler slapping me on the fingers, but it doesn't. I understand that it is safe and that the compiler can allow this, but why would anyone want that? D-scanner does not check for this either.

I think a helpful error message would be: "Error: The initializer A(5) allocates memory that is shared among all instances of S. If you want that, make S.a static."

-- Bastiaan.

June 10, 2022

On Friday, 10 June 2022 at 07:35:17 UTC, Bastiaan Veelo wrote:

>

Is there a use case where this makes sense? I would have much appreciated the compiler slapping me on the fingers, but it doesn't. I understand that it is safe and that the compiler can allow this, but why would anyone want that? D-scanner does not check for this either.

Any initialization of a member field is overriding the field's .init value for the type. If a dynamic allocation set a different value per instance, then you'd have inconsistent behavior with, e.g., int a = 5.

>

I think a helpful error message would be: "Error: The initializer A(5) allocates memory that is shared among all instances of S. If you want that, make S.a static."

I understand that it's not something that people expect, but making it an error can't be the answer. And making it a static field is not the same thing.

I think this is a case where having a warning that's on by default, and which can be explicitly disabled, is useful. "Blah blah .init blah blah. See link-to-something-in-docs. Is this what you intended?"

June 10, 2022

On Friday, 10 June 2022 at 07:46:36 UTC, Mike Parker wrote:

>

I think this is a case where having a warning that's on by default, and which can be explicitly disabled, is useful. "Blah blah .init blah blah. See link-to-something-in-docs. Is this what you intended?"

And it is documented:

>

Struct fields are by default initialized to whatever the Initializer for the field is, and if none is supplied, to the default initializer for the field's type.
The default initializers are evaluated at compile time.

https://dlang.org/spec/struct.html#default_struct_init

June 10, 2022

On Friday, 10 June 2022 at 07:46:36 UTC, Mike Parker wrote:

>

On Friday, 10 June 2022 at 07:35:17 UTC, Bastiaan Veelo wrote:

>

Is there a use case where this makes sense? I would have much appreciated the compiler slapping me on the fingers, but it doesn't. I understand that it is safe and that the compiler can allow this, but why would anyone want that? D-scanner does not check for this either.

Any initialization of a member field is overriding the field's .init value for the type. If a dynamic allocation set a different value per instance, then you'd have inconsistent behavior with, e.g., int a = 5.

Yes, I understand that the compiler can't do what I was expecting it to do, it was a mistake.

> >

I think a helpful error message would be: "Error: The initializer A(5) allocates memory that is shared among all instances of S. If you want that, make S.a static."

I understand that it's not something that people expect, but making it an error can't be the answer. And making it a static field is not the same thing.

It's not the same thing, therefore I was hoping to see a use case for it -- but just because I'm curious; Preventing the mistake is my main thing.

>

I think this is a case where having a warning that's on by default, and which can be explicitly disabled, is useful. "Blah blah .init blah blah. See link-to-something-in-docs. Is this what you intended?"

That would be fine too. By the way, if there is something-in-docs, I don't think it is prominent enough...

-- Bastiaan.

June 10, 2022

On Friday, 10 June 2022 at 07:49:43 UTC, Mike Parker wrote:

>

And it is documented:

>

Struct fields are by default initialized to whatever the Initializer for the field is, and if none is supplied, to the default initializer for the field's type.
The default initializers are evaluated at compile time.

https://dlang.org/spec/struct.html#default_struct_init

Yes, that section I find open for interpretation, because I don't think pointer values can be determined at compiler time. I assume S.init is constructed at program initialization, which is then blitted into any new instance of S.

-- Bastiaan.

June 10, 2022

On Friday, 10 June 2022 at 07:35:17 UTC, Bastiaan Veelo wrote:

>

I have been foolish enough to make a mistake like this:

struct S
{
    int[] arr = new int[](5);
}

Well, if the b's may not be equal, there's a simple solution. But why are a's like that, they're not static!

void main()
{
  struct S(size_t size)
  {
    int[] arr = new int[size];
  }

  S!5 a1, a2;
  assert(a1.arr.ptr == a2.arr.ptr);

  S!5 b1;
  S!6 b2;
  assert(b1.arr.ptr != b2.arr.ptr);
}

SDB@79

June 10, 2022
On 10/6/22 14:58, Salih Dincer wrote:
> On Friday, 10 June 2022 at 07:35:17 UTC, Bastiaan Veelo wrote:
>> I have been foolish enough to make a mistake like this:
>> ```d
>> struct S
>> {
>>     int[] arr = new int[](5);
>> }
>> ```
> 
> Well, if the b's may not be equal, there's a simple solution. But why are a's like that, they're not static!
> 
> ```d
> void main()
> {
>    struct S(size_t size)
>    {
>      int[] arr = new int[size];
>    }
> 
>    S!5 a1, a2;
>    assert(a1.arr.ptr == a2.arr.ptr);
> 
>    S!5 b1;
>    S!6 b2;
>    assert(b1.arr.ptr != b2.arr.ptr);
> }
> ```
> SDB@79

Because the `arr` are created for each instantiation of the template.

All S!5 share one default value, so you could also:

```d
assert (a1.arr.ptr == b1.arr.ptr);
```

However, S!6 becomes a completely different struct, and thus gets a different default `arr`.

Note that this would also happen with static members:

```d
struct S(int T) {
    static int foo;
}

static assert(&S!1.foo !is &S!2.foo);

void main() { }
```
June 10, 2022

On 6/10/22 3:46 AM, Mike Parker wrote:

>

On Friday, 10 June 2022 at 07:35:17 UTC, Bastiaan Veelo wrote:

>

Is there a use case where this makes sense? I would have much appreciated the compiler slapping me on the fingers, but it doesn't. I understand that it is safe and that the compiler can allow this, but why would anyone want that? D-scanner does not check for this either.

Any initialization of a member field is overriding the field's .init value for the type. If a dynamic allocation set a different value per instance, then you'd have inconsistent behavior with, e.g., int a = 5.

Yes, it can't be done the way that is expected (which is common to many languages): allocate one for every instance.

> >

I think a helpful error message would be: "Error: The initializer A(5) allocates memory that is shared among all instances of S. If you want that, make S.a static."

I understand that it's not something that people expect, but making it an error can't be the answer. And making it a static field is not the same thing.

I think this is a case where having a warning that's on by default, and which can be explicitly disabled, is useful. "Blah blah .init blah blah. See link-to-something-in-docs. Is this what you intended?"

Here the language is being extremely unsafe.

Not only is the field shared between instances, it's shared across instances in different threads.

Discovered circa 2009: https://issues.dlang.org/show_bug.cgi?id=2947

It should be illegal to declare a field this way that has mutable references without being shared. End of story.

-Steve

June 10, 2022

On Friday, 10 June 2022 at 14:56:24 UTC, Steven Schveighoffer wrote:

>

Discovered circa 2009: https://issues.dlang.org/show_bug.cgi?id=2947

It should be illegal to declare a field this way that has mutable references without being shared. End of story.

-Steve

The docs do say that:

>

The default initializers may not contain references to mutable data.

June 10, 2022

On 6/10/22 1:20 PM, Mike Parker wrote:

>

On Friday, 10 June 2022 at 14:56:24 UTC, Steven Schveighoffer wrote:

>

Discovered circa 2009: https://issues.dlang.org/show_bug.cgi?id=2947

It should be illegal to declare a field this way that has mutable references without being shared. End of story.

The docs do say that:

>

The default initializers may not contain references to mutable data.

I guess it was added but no implementation done for it? There's not even a warning!

https://github.com/dlang/dlang.org/pull/2331/files#diff-dee70e840ada3a820b3a373d649812b1157fd584ed0837e4a937ea3418f093e3R157

Anyways, a documented rule that's so easily enforced but is not actually enforced is pretty worthless.

-Steve

« First   ‹ Prev
1 2