June 13, 2019
On Thursday, 13 June 2019 at 13:32:05 UTC, Joseph Rushton Wakeling wrote:
> On Thursday, 13 June 2019 at 08:24:38 UTC, Nicholas Wilson wrote:
>> No: the spec disagrees with the implementation, therefore at least one of them is wrong. It does not immediately follow that it is the spec that is in error.
>
> It's reasonable to say in this case that the spec is in error, because Walter has always been 100% clear that @safe refers only to memory safety, and the implementation reflects that intent.

Not really:

https://issues.dlang.org/show_bug.cgi?id=19326

> tupleof is known to break through private access protections. It should probably not be allowed in @safe code.
June 13, 2019
On Thursday, 13 June 2019 at 13:32:05 UTC, Joseph Rushton Wakeling wrote:
> On Thursday, 13 June 2019 at 08:24:38 UTC, Nicholas Wilson wrote:
>> No: the spec disagrees with the implementation, therefore at least one of them is wrong. It does not immediately follow that it is the spec that is in error.
>
> It's reasonable to say in this case that the spec is in error, because Walter has always been 100% clear that @safe refers only to memory safety, and the implementation reflects that intent.

Alas, memory safety is not an easy beast to pin down. If it were, we wouldn't be having this conversation.

>> If you have a race condition on a pointer write, and that write corrupts memory, then that is a memory safety bug that is also a thread safety bug.
>>
>> In the world of multithreading, memory safety and thread safety are _not_ separable.
>>
>> I know because we'e had a bug exposed by LDC breaking such optimisation that relied on that.
>
> There's certainly a reasonable case for trying to support constraints that rule out undefined behaviour, but let's make that case in the understanding that we're asking for something new, not something that was always intended to be part of the language spec.

Sure, but whatever happens, I don't think there's any future in saying "@safe code guarantees that no memory corruption will occur except if a) there is a bug in your @trused code (your fault) b), the compiler (our fault) or c) you write through a stale cached pointer,through a race condition or somehow else (tough luck)".

For sure the status quo is vastly superior to nothing, but memory safety doesn't stop with the current implementation.

June 13, 2019
On Thursday, June 13, 2019 7:32:05 AM MDT Joseph Rushton Wakeling via Digitalmars-d wrote:
> On Thursday, 13 June 2019 at 08:24:38 UTC, Nicholas Wilson wrote:
> > No: the spec disagrees with the implementation, therefore at least one of them is wrong. It does not immediately follow that it is the spec that is in error.
>
> It's reasonable to say in this case that the spec is in error, because Walter has always been 100% clear that @safe refers only to memory safety, and the implementation reflects that intent.

The problem is that if undefined behavior is allowed in @safe code, then how can @safe code guarantee memory safety when undefined behavior lets the compiler do whatever it wants with that code? So, while the spec may be wrong in the sense that preventing undefined behavior is not @safe's purpose, I don't see how we can allow @safe code to have undefined behavior and still have @safe do what it's actually supposed to be doing.

As I understand it, a real problem related to this that exists right now is that llvm considers dereferencing null to be undefined behavior. As such, if ldc is given a piece of code where the optimizer thinks that a pointer is null, it will treat it as if that code path was never hit, because it's allowed to do whatever it wants with that piece of code, since the behavior is undefined. So, instead of segfaulting by dereferencing null like most of us would assume that it would, it could do who knows what. As I understand it, there's nothing there stopping it from generating code that results in invalid memory being accessed, because it decided that a different branch was taken that would not have been possible if llvm didn't assume that the branch with the null dereference didn't happen based on its behavior being undefined. Because dereferencing is considered @safe, that means that you have a piece of code that is considered @safe but which could actually end up doing something like corrupting memory.

That particular issue highlights how we need to make sure that what happens with dereferencing null is actually defined instead of just assuming that the CPU will catch it and segfault, but it's just one case of undefined behavior. If _any_ undefined behavior is allowed in @safe code, then such problems could crop up in other ways - particularly with aggressive code optimizers.

- Jonathan M Davis



June 13, 2019
On Thursday, 13 June 2019 at 17:39:56 UTC, Jonathan M Davis wrote:
> As I understand it, a real problem related to this that exists right now is that llvm considers dereferencing null to be undefined behavior.

Not really. Clang might.

You should be able to control this by how you set up the optimizer:

http://blog.llvm.org/2011/05/what-every-c-programmer-should-know_14.html


June 13, 2019
On 6/13/2019 1:24 AM, Nicholas Wilson wrote:
> No: the spec disagrees with the implementation, therefore at least one of them is wrong. It does not immediately follow that it is the spec that is in error.

@safe has always been about memory safety. The spec is wrong.
June 13, 2019
On 6/13/2019 10:39 AM, Jonathan M Davis wrote:
> The problem is that if undefined behavior is allowed in @safe code, then how
> can @safe code guarantee memory safety when undefined behavior lets the
> compiler do whatever it wants with that code?
Timon brought that up in another reply, and I responded to it.
June 13, 2019
On Tuesday, 11 June 2019 at 00:30:27 UTC, Walter Bright wrote:
> If you want @safe to mean "no undefined behavior", that is a valid opinion, but that is not what @safe in D is currently defined as. It is currently defined as "memory safe". If you can find a case where an int with garbage in it can cause memory corruption in @safe code, that would indeed be a bug in D.

The following code causes memory corruption, because a bool has garbage in it.

import std.stdio;

// Returns newly allocated data with increased value if b is true.
// Returns same data otherwise.
const(int)* test(immutable int *data, bool b) @trusted
{
    int *data2;
    if(!b)
    {
        writeln("b seems to be false");
        // The cast can be trusted, because data2 will only be modified if b is true.
        data2 = cast(int*)data;
    }
    else
    {
        writeln("b seems to be true");
        data2 = new int(*data);
    }

    if(b)
    {
        writeln("b seems to be true");
        *data2 += 1;
    }
    return data2;
}

void main() @safe
{
    bool b = void;
    immutable int *data = new immutable int(1);
    writeln("data before: ", *data);
    const int *data2 = test(data, b);
    writeln("data after: ", *data);
    writeln("data2 after: ", *data2);
}

After compiling it with dmd and running it I get the following output:
data before: 1
b seems to be false
b seems to be true
data after: 2
data2 after: 2

The immutable integer pointed to by data is modified. The function test is @trusted, but only modifies the immutable data, because b seems to be true and false at the same time.
Normally a bool is internally 0 or 1 and the compiler can assume that. If b has another value and !b is implemented as ~b, then b and !b can both be true.

This means, that uninitialized data can result in memory corruption.
June 13, 2019
On 6/13/2019 4:27 AM, Timon Gehr wrote:
> E.g., in principle, an optimizer can end up caching the result of the bounds check but not the result of the function call for something like a[foo()]+a[foo()]. (The particular example could be more involved, the point is that it is possible that the optimizer only partially applies the available information.)

I see the principle, but in practice when a compiler sees a common subexpression, it replaces it with a temporary initialized with the first instance.
June 13, 2019
On 6/13/2019 4:30 AM, Timon Gehr wrote:
> Yes. IMHO this shouldn't be a @safe pure operation.

D regards converting a pointer to an int as safe, but not the other way around.
June 13, 2019
On 13.06.19 21:44, Walter Bright wrote:
> On 6/13/2019 4:30 AM, Timon Gehr wrote:
>> Yes. IMHO this shouldn't be a @safe pure operation.
> 
> D regards converting a pointer to an int as safe, but not the other way around.

I know, and that is fine, but casting a pointer to int is not pure. It glances into the global state maintained by the memory allocator.