Jump to page: 1 2
Thread overview
std.format and uninitialized elements in CTFE
Dec 05, 2019
berni44
Dec 06, 2019
berni44
Dec 06, 2019
Johan Engelen
Dec 06, 2019
berni44
Dec 08, 2019
berni44
Dec 09, 2019
berni44
Dec 09, 2019
berni44
Dec 05, 2019
kinke
Dec 05, 2019
kinke
Dec 05, 2019
mipri
Dec 05, 2019
Ali Çehreli
December 05, 2019
std.format contains code, that accesses the members of a struct via tupleof (which gives access to private members, that might be intentionally not yet initialized), to print them. If members are not initialized it produces a "used before initialized" error or something similar. (See issue 19769 [1]).

What to do about this? Should std.format check if the members of a struct are not initialized and do what in that case? And how to check them?

Here an example:

```
import std.format;

struct Foo
{
    int a = void;
}

static x = format("%s", Foo());
```

Replace int by int[3] for a more complex version.

[1] https://issues.dlang.org/show_bug.cgi?id=19769
December 05, 2019
On 12/5/19 1:37 PM, berni44 wrote:
> std.format contains code, that accesses the members of a struct via tupleof (which gives access to private members, that might be intentionally not yet initialized), to print them. If members are not initialized it produces a "used before initialized" error or something similar. (See issue 19769 [1]).
> 
> What to do about this? Should std.format check if the members of a struct are not initialized and do what in that case? And how to check them?
> 
> Here an example:
> 
> ```
> import std.format;
> 
> struct Foo
> {
>      int a = void;
> }
> 
> static x = format("%s", Foo());
> ```
> 
> Replace int by int[3] for a more complex version.
> 
> [1] https://issues.dlang.org/show_bug.cgi?id=19769

What is the point of formatting items that aren't initialized? You would be printing garbage.

What if you just initialize the items you wish to print, or if you have items that aren't initialized (but you don't want to print), create a toString overload? That's how I would handle it.

-Steve
December 05, 2019
On Thursday, 5 December 2019 at 18:37:03 UTC, berni44 wrote:
> Should std.format check if the members of a struct are not initialized and do what in that case?

It shouldn't, and there's no such thing as uninitialized struct/class *members*, there are only uninitialized whole struct instances. There are surely bugzillas wrt.

struct S { int bla = void; }

having no effect (correct way: `S s = void`). IMO, void member initializers should be invalid to make that clear.
December 05, 2019
On 12/5/19 2:56 PM, kinke wrote:
> On Thursday, 5 December 2019 at 18:37:03 UTC, berni44 wrote:
>> Should std.format check if the members of a struct are not initialized and do what in that case?
> 
> It shouldn't, and there's no such thing as uninitialized struct/class *members*, there are only uninitialized whole struct instances. There are surely bugzillas wrt.
> 
> struct S { int bla = void; }
> 
> having no effect (correct way: `S s = void`). IMO, void member initializers should be invalid to make that clear.

Well, as long as all members are void initialized, then it should have an effect right? I know that the compiler blits the whole struct at once, but if it's all void initialized, it doesn't have to.

-Steve
December 05, 2019
On 12/5/19 10:37 AM, berni44 wrote:
> std.format contains code, that accesses the members of a struct via tupleof (which gives access to private members, that might be intentionally not yet initialized), to print them. If members are not initialized it produces a "used before initialized" error or something similar. (See issue 19769 [1]).
> 
> What to do about this? Should std.format check if the members of a struct are not initialized and do what in that case? And how to check them?
> 
> Here an example:
> 
> ```
> import std.format;
> 
> struct Foo
> {
>      int a = void;
> }
> 
> static x = format("%s", Foo());
> ```
> 
> Replace int by int[3] for a more complex version.
> 
> [1] https://issues.dlang.org/show_bug.cgi?id=19769

Similar:

import std.string;

struct S {
  int i = void;
}

void main() {
  auto s = S(42);
  auto f = format!"%s"(s);
}

Error: cannot read uninitialized variable `i` in CTFE

Note that the user is not doing anything at compile time. Only format is attempting to catch errors in the format string.

Ali

December 05, 2019
On Thursday, 5 December 2019 at 19:56:35 UTC, kinke wrote:
> there's no such thing as uninitialized struct/class *members*, there are only uninitialized whole struct instances.

Is that true in CTFE?

Consider:

  import std.format;

  struct Foo
  {
      int a = void;
      int b;
  }

  static x = format("%s", Foo().a);
  static y = format("%s", Foo().b);

The second format succeeds on its own.
The first fails with

  Error: cannot read uninitialized variable a in CTFE
December 05, 2019
On Thursday, 5 December 2019 at 20:47:02 UTC, Steven Schveighoffer wrote:
> Well, as long as all members are void initialized, then it should have an effect right? I know that the compiler blits the whole struct at once, but if it's all void initialized, it doesn't have to.

LDC and DMD still pre-initialize it (optimizer disabled) in that case (and druntime etc. via generic `= T.init` code). I doubt there's a good use case for a struct defining all of its fields as uninitialized, without being able to be sensibly default-constructed (no default ctor). These cases are probably better handled outside the struct, i.e., where the dangerous uninitialized allocation resides, and can be handled in some little factory function returning a void-initialized stack instance.

E.g., this attempt at manually eliminating the init blit/memset before the ctor call:

struct S {
    int[32] data = void;
    bool isDirty = void;
    this() @disable;
    this(const int[] data) {
        this.data[] = data[];
        isDirty = false;
    }
}

can be expressed (and actually works) like this, maintaining sensible default-constructability at the price of a somewhat more verbose `S.get()` for manually optimized construction:

struct S {
    int[32] data;
    bool isDirty;
    static S get(const int[] data) {
        S r = void;
        r.data[] = data[];
        r.isDirty = false;
        return r;
    }
}
December 06, 2019
On Thursday, 5 December 2019 at 19:45:27 UTC, Steven Schveighoffer wrote:
> What is the point of formatting items that aren't initialized? You would be printing garbage.
>
> What if you just initialize the items you wish to print,

You are on the wrong track... I'm programming inside of phobos. Such questions do not arise there. If the user provides a struct with (partially) uninitialized items std.format has to cope with this somehow. We cannot really answer the question, why the user is doing this, nor can we make him initialize the items before printing.

Ali's example shows, that this is a serious issue. IMHO my example should print `Foo(void)` or `Foo([void, void, void])` with `int[3]`. With that, Ali's example will work.

For me, the question remains, how to detect (at compile time) if a variable is void. The best I could come up with yet is:

int a = void;

static if (!__traits(compiles, a?a:a))

But I'm not sure if ?: can be applied to all thinkable types.
December 06, 2019
Uninitialized member variables, or partially initialized structs, are needed for e.g. small-string optimized string types.
There are a number of bug reports about it, see: https://issues.dlang.org/show_bug.cgi?id=11331

I remember that at the end of my DConf talk in 2017, we got Walter's approval to treat `=void` initialized struct members as if they are initialized / skip all initialization of it.
- Discussion of =void: https://youtu.be/YL6Tp8Zb5aI?t=2959
- Confirmation of that we should do something about it: https://youtu.be/YL6Tp8Zb5aI?t=3084
- Approval :-)  https://youtu.be/YL6Tp8Zb5aI?t=3266

This is a fun project to work on for LDC: https://github.com/ldc-developers/ldc/issues/3249

-Johan

December 06, 2019
On Friday, 6 December 2019 at 06:49:41 UTC, berni44 wrote:
> int a = void;
>
> static if (!__traits(compiles, a?a:a))
>
> But I'm not sure if ?: can be applied to all thinkable types.

Even that doesn't work (don't know, why I got this morning the impression, that it does).


Is the following a bug in __traits?

```
struct Foo
{
    int a = void;
}

int foo(Foo f)
{
    static if (__traits(compiles, f.a == f.a))
    {
        if (f.a == f.a) return 1; // error
        return 2;
    }
    return 3;
}

static x = foo(Foo());
```
« First   ‹ Prev
1 2