June 07, 2020
On Saturday, 6 June 2020 at 20:20:54 UTC, ag0aep6g wrote:
> On Saturday, 6 June 2020 at 18:30:15 UTC, Avrina wrote:
>> On Saturday, 6 June 2020 at 15:16:06 UTC, ag0aep6g wrote:
> [...]
>> So your saying void initializers don't have a defined function then? So if LDC wants it could initialise the memory.of all void initialized variables to zero and that would be considered within defined behaviour? If void initializers are undefined behaviour then the feature is useless.
>
> It's not void initializers themselves that have undefined behavior. Using an uninitialized value has undefined behavior. In code:
>
> ----
> int f() { int x = void; x = 42; return x + 1; } /* This is fine. */
> int g() { int x = void; return x + 1; } /* This is not. */
> ----
>
> If LDC defines behavior for `g`, it creates a dialect of D which has less undefined behavior than (current) standard D.

g is defined behavior, or it wouldn't work. The result may not be known, but it is defined. Otherwise, what, a compiler could just insert anything they want there. An assert(0) that terminates the application.

The issue isn't with void initializers. It is whether a bool should be treated as a byte or single bit. Void initialization just leads to the problem, there's many ways it can be expressed. Someone else has shown it with unions. You can also just use casts as well and you'll get the same problem.

    bool e() { int x = 2; return *cast(bool*)&x; }

The issue with void initializers is that they aren't actually @safe, just as I can't use cast's in @safe code, I shouldn't be allowed to use void initializers in @safe.

Aside from that, it is still a problem because DMD's backend isn't consistent. Is bool a byte or a 1-bit integer. The decision has to be made and the backend fixed. The only thing in the spec I can find is that bool is a byte.

>>> But the spec doesn't do that. Instead it says: "If a void initialized variable's value is used before it is set, the behavior is undefined." That is, the spec explicitly says that doing so is not allowed, and that compilers may assume that it doesn't happen. Because that's what "undefined behavior" means.
>>
>> The spec is poorly written. The language doesn't do what's written in it for a lot of cases on top of that.
>
> True. Which is why a lot of fixing needs to be done. Sometimes the spec needs to be changed, sometimes the implementation, sometimes both.

No one is going to waste their time with that.

> [...]
>>> But Walter agrees with you: Using a void value shouldn't actually have undefined behavior; it should just be an arbitrary value. Which is why he has an open pull request to change the spec:
>>> https://github.com/dlang/dlang.org/pull/2260
>>
>> Yea and that pull request is years old. If that change does go into effect then the spec will reflect the actual reality of what is happening.
>
> Sure. But as long as the PR is in limbo, Patrick Schluter has a point when he says that "undefined behaviour is undefined behaviour".

It's not though. There is expected behavior for void initializers. Otherwise what you are saying is that C++ has undefined behavior for using variables that aren't initialized (which are all of them unless they are initialized first). But it doesn't. The behavior is expected. The root of the problem isn't with void initialization because the behavior is defined. The problem is with the boolean type and the loose definition the spec provides it. Boolean is undefined behavior in D, as long as the implementation in DMD and LDC differ, and there's nothing in the spec to define it.

Like I said, the Spec doesn't follow what is actually implemented. If you are to say that, then I would say, it is defined behavior unless the DMD or LDC implementation changes in such as way that makes it undefined behavior. It is the spec that differs, it doesn't match the implementation. Hell even that pull request wouldn't accurately make it match what the implementation does.
June 07, 2020
On 6/6/20 8:33 PM, Paul Backus wrote:
> As long as the @trusted code is written correctly, it's safe regardless.

Consider an integer which represents the length of an array. Instead of the full bit pattern of the integer being valid, only the bit pattern that is less than or equal to the memory size is valid. And this is not something that can be checked even at runtime -- @trusted code must depend on the value being correct. This is enforced for D's builtin arrays, but not for a custom array type.

And currently there's no way to convey that danger to the compiler. Especially where unions and void initializations are involved, there are ways to use @safe code to subvert @trusted code, even for private variables. Even with careful encapsulation, D has ways to get at the data.

This is especially a problem with types which have a semantic invariant between calls to its members.

-Steve
June 07, 2020
On Sunday, 7 June 2020 at 13:10:22 UTC, Avrina wrote:
> On Saturday, 6 June 2020 at 20:20:54 UTC, ag0aep6g wrote:
[...]
>> ----
>> int f() { int x = void; x = 42; return x + 1; } /* This is fine. */
>> int g() { int x = void; return x + 1; } /* This is not. */
>> ----
>>
>> If LDC defines behavior for `g`, it creates a dialect of D which has less undefined behavior than (current) standard D.
>
> g is defined behavior, or it wouldn't work. The result may not be known, but it is defined. Otherwise, what, a compiler could just insert anything they want there. An assert(0) that terminates the application.

Per the current spec, g has undefined behavior. Yes, a compiler can just insert anything it wants there. Yes, it can replace the body of g with `assert(0);`. That's how undefined behavior works.

Please consider the possibility that you might not fully understand what "undefined behavior" means.

> The issue isn't with void initializers. It is whether a bool should be treated as a byte or single bit. Void initialization just leads to the problem, there's many ways it can be expressed. Someone else has shown it with unions. You can also just use casts as well and you'll get the same problem.
>
>     bool e() { int x = 2; return *cast(bool*)&x; }

It's true that you can get exotic bools in other ways that also pass as @safe. That's a valid counter to Patrick Schluter's remark about undefined behavior.

If we look at an example that has undefined behavior (per the spec), then Patrick is right that we can't expect any particular result. With undefined behavior, a bool can be true and false at the same time.

But if we look at an example that doesn't have undefined behavior, then a bool should really be either true or false and not both. If it's both, the compiler has a bug. This is what you're saying, and I agree.

On top of that, an example that passes as @safe cannot have undefined behavior (per the spec). If the compiler accepts code with undefined behavior as @safe, there is another bug (in the compiler or in the spec). This is what Walter's pull request is about.

> The issue with void initializers is that they aren't actually @safe, just as I can't use cast's in @safe code, I shouldn't be allowed to use void initializers in @safe.

I think that's a reasonable stance. And it's what the spec says at the moment. But it's not the only way to resolve the issue. And judging from his pull request, it's not how Walter wants to proceed.

On a side note: You can use casts in @safe code. Not all of them, of course, but your reinterpret cast above actually compiles as @safe when you compile with -preview=dip1000.

[...]
>> True. Which is why a lot of fixing needs to be done. Sometimes the spec needs to be changed, sometimes the implementation, sometimes both.
>
> No one is going to waste their time with that.

That's just not true. It doesn't happen as fast as everyone would like, but bugs are getting fixed.

[...]
>> Sure. But as long as the PR is in limbo, Patrick Schluter has a point when he says that "undefined behaviour is undefined behaviour".
>
> It's not though. There is expected behavior for void initializers. Otherwise what you are saying is that C++ has undefined behavior for using variables that aren't initialized (which are all of them unless they are initialized first). But it doesn't. The behavior is expected. The root of the problem isn't with void initialization because the behavior is defined. The problem is with the boolean type and the loose definition the spec provides it. Boolean is undefined behavior in D, as long as the implementation in DMD and LDC differ, and there's nothing in the spec to define it.

The spec clearly says that using an uninitialized value has undefined behavior. That doesn't go away just because you don't like it, or because C++ doesn't have that rule.

But yes, the issue with exotic bools goes beyond undefined behavior, as the examples with unions and casts show. As far as I can tell, the spec doesn't rule those out as undefined behavior.

June 07, 2020
On Sunday, 7 June 2020 at 14:14:51 UTC, Steven Schveighoffer wrote:
> Consider an integer which represents the length of an array. Instead of the full bit pattern of the integer being valid, only the bit pattern that is less than or equal to the memory size is valid. And this is not something that can be checked even at runtime -- @trusted code must depend on the value being correct. This is enforced for D's builtin arrays, but not for a custom array type.
>
> And currently there's no way to convey that danger to the compiler. Especially where unions and void initializations are involved, there are ways to use @safe code to subvert @trusted code, even for private variables. Even with careful encapsulation, D has ways to get at the data.
>
> This is especially a problem with types which have a semantic invariant between calls to its members.

For reference, there's a DIP being written to address this issue:

https://github.com/dlang/DIPs/pull/179

I know that you're already aware of it, Steven. But others might not be.
June 07, 2020
On Sunday, 7 June 2020 at 14:55:07 UTC, ag0aep6g wrote:
> On Sunday, 7 June 2020 at 13:10:22 UTC, Avrina wrote:
>> It's not though. There is expected behavior for void initializers. Otherwise what you are saying is that C++ has undefined behavior for using variables that aren't initialized (which are all of them unless they are initialized first). But it doesn't. The behavior is expected. The root of the problem isn't with void initialization because the behavior is defined. The problem is with the boolean type and the loose definition the spec provides it. Boolean is undefined behavior in D, as long as the implementation in DMD and LDC differ, and there's nothing in the spec to define it.
>
> The spec clearly says that using an uninitialized value has undefined behavior. That doesn't go away just because you don't like it, or because C++ doesn't have that rule.

I think we agree on about everything else.

The spec is wrong/incorrect/incomplete in a lot of places, as you agree. This is just another one. There's nothing an implementation can do that would reasonably make sense for it undefined behavior, unlike with something like little/big endian and unions or bit fields in C++. Reading a void initialized variable is no different than reading a randomized variable.

Right C++ is more linate with undefined behavior, and it doesn't even have the rule that D does. Reading uninitialized memory doesn't change or have any reason to change under any circumstances. It has no reason to be undefined behavior and is a mistake in the specs part.





June 08, 2020
On 07.06.20 21:01, Avrina wrote:
> On Sunday, 7 June 2020 at 14:55:07 UTC, ag0aep6g wrote:
>> On Sunday, 7 June 2020 at 13:10:22 UTC, Avrina wrote:
>>> It's not though. There is expected behavior for void initializers. Otherwise what you are saying is that C++ has undefined behavior for using variables that aren't initialized (which are all of them unless they are initialized first). But it doesn't. The behavior is expected. The root of the problem isn't with void initialization because the behavior is defined. The problem is with the boolean type and the loose definition the spec provides it. Boolean is undefined behavior in D, as long as the implementation in DMD and LDC differ, and there's nothing in the spec to define it.
>>
>> The spec clearly says that using an uninitialized value has undefined behavior. That doesn't go away just because you don't like it, or because C++ doesn't have that rule.
> 
> I think we agree on about everything else.
> 
> The spec is wrong/incorrect/incomplete in a lot of places, as you agree. This is just another one. There's nothing an implementation can do that would reasonably make sense for it undefined behavior, unlike with something like little/big endian and unions or bit fields in C++. Reading a void initialized variable is no different than reading a randomized variable.
> ...

It is not so simple, as the case with bool demonstrates.

> Right C++ is more linate with undefined behavior, and it doesn't even have the rule that D does.

Reading a trap representation is UB in C++, so reading an uninitialized bool in C++ will cause UB.

> Reading uninitialized memory doesn't change or have any reason to change under any circumstances. It has no reason to be undefined behavior and is a mistake in the specs part.
> 

It's not a mistake, there is also no reason why it can't be UB. The mistake is that it is UB and allowed in @safe code at the same time.
1 2 3 4 5 6
Next ›   Last »