std.format and uninitialized elements in CTFE
Steven Schveighoffer
schveiguy at gmail.com
Sun Dec 8 20:18:23 UTC 2019
On 12/8/19 2:41 PM, berni44 wrote:
> As I wrote above, I was looking into issue 19769. This is the test given
> there:
>
> ```
> import std.uni;
> import std.format;
> static immutable x = format("%s", cast(const)"test".asCapitalized);
> ```
>
> This doesn't compile, because asCapitalized returns a struct
> (ToCapitalizerImpl), which contains uninitialized data (dchar[3] buf =
> void;) and format tries to print that. It first tries to print it as a
> range, but isInputRange misses somehow, that this struct is a range
> (probably because of the cast(const), but I don't know exactly) and
> therefore formatValueImpl tries to print the members of that struct
> directly, but as buf is not initialized, that fails. And the result is a
> strange error message (about uninitialized variables, where everything
> looks initialized).
It definitely is the cast(const). Removing it works. And it makes sense
-- a const type cannot be a range, as popFront can never be const.
So one thing you CAN do, in cases like this is just initialize the damn
void stuff in CTFE. i.e. here:
https://github.com/dlang/phobos/blob/a24888e533adfe8d141eb598be22a50df5e26a66/std/uni.d#L9390
change to:
auto result = ToCapitalizerImpl(str);
if(__ctfe) result.buf[] = dchar.init;
return result;
>
> This bug could be fixed by making isInputRange recognize the struct as a
> range. But while I looked, what happens, I found out, that printing
> structs with uninitialized data fails (but with an errormessage, that
> says, that this data is uninitialized). I thought, it would be nice, if
> format would just print <void> instead of throwing an error and hence my
> question if doing so is possible.
OK, I see what you mean. Confusing error messages can be indeed a huge
problem.
But let's consider the fix I outline above. In this case, instead of
"Test", like he expects, he's going to get x equal to (yes, I did it at
runtime to see what it would be):
const(ToCapitalizerImpl)(const(Result)(const(ByCodeUnitImpl)("test"),
4294967295), const(ToCaserImpl)(const(Result)(const(ByCodeUnitImpl)(""),
4294967295), 0, "\0\0\0"), false, "\0\0\0", 0)
Which is going to result in a *different* confusion. Even if it said
"<void>" like you want, it's still a pile of gibberish that's far from
what you expect.
So which is better? A compiler error saying essentially in a long and
confusing way, "dude, you really don't want to do that", or a resulting
binary that has, um... that other thing instead of "Test"?
-Steve
More information about the Digitalmars-d
mailing list