February 06, 2015
On 2/5/2015 7:39 PM, Zach the Mystic wrote:
> On Friday, 6 February 2015 at 03:14:59 UTC, Walter Bright wrote:
>> I don't see how any proposal can work unless it specifies a safe interface to
>> an unsafe section of code. (I read a Rust tutorial that rather bluntly pointed
>> this out as well.)
>
> Link?

"A non-unsafe function using unsafe internally should be implemented to be safe to call; that is, there is no circumstance or set of arguments that can make the function violate any invariants. If there are such circumstances, it should be marked unsafe."

"However, this is not the case, unsafe is just an implementation detail; if a safe function uses unsafe internally, it just means the author has been forced to step around the type system, but still exposes a safe interface."

http://huonw.github.io/blog/2014/07/what-does-rusts-unsafe-mean/
February 06, 2015
I figured that someone would have already objected to part of this, but the definition is stronger than I believe is intended for D:

On 2/5/2015 5:23 PM, H. S. Teoh via Digitalmars-d wrote:
>
> 2) I think we also all basically agree that the *intent* of @trusted is
> to be an encapsulation mechanism, or to present a safe API, or however
> you want to describe it. I.e., if a function is marked @safe, then it
> must be impossible to cause it to do something unsafe by passing it the
> wrong arguments. Whatever it does under the hood should not have any
> observable unsafe effect on the outside world. I'm pretty sure everyone
> also agrees with this; I don't think anyone is advocating that we should
> changee this intent. So can we please also take this as a given, and
> stop repeating it at each other as if we don't all already know it?

Specifically, the 'impossible to cause it to do something unsafe by passing it the wrong arguments' part.

For example, this is valid @safe code:

@safe void setToZero(ubyte[] foo)
{
    foo[] = 0;
}

How about now:

void bar()
{
    ubyte[] a;
    a.ptr = 0; // arbitrary value as long as NOT from an allocator
    a.len = 1;

    setToZero(a);
}

@safe code is memory safe when passed good inputs.  @safe code does not have to detect or prevent garbage input from causing harm.   The promises of @trusted aren't stronger than that either.
February 06, 2015
On 2/5/2015 8:20 PM, Dicebot wrote:
> I am not even sure how you can show the example though, to be honest - implied
> issues are about maintaining code and not just writing it.

Let's start with std.array.uninitializedArray():

   auto uninitializedArray(T, I...)(I sizes) nothrow @trusted

Note how it says it is 'trusted'. Yet it can be used to create an uninitialized array of pointers! There is no freakin' way this function should pass muster. Worse, multiple modules use it:

array.d:            return uninitializedArray!(Unqual!E[])(n);
array.d:auto uninitializedArray(T, I...)(I sizes) nothrow @trusted
array.d:    double[] arr = uninitializedArray!(double[])(100);
array.d:    double[][] matrix = uninitializedArray!(double[][])(42, 31);
array.d:    auto s1 = uninitializedArray!(int[])();
array.d:            return uninitializedArray!(T[])(n);
array.d:        auto result = uninitializedArray!(RetTypeElement[])(length);
array.d:            auto result = uninitializedArray!(RetTypeElement[])(length);
array.d:        auto result = uninitializedArray!(RetTypeElement[])(length);
file.d:    import std.array : uninitializedArray;
file.d:        auto buf = uninitializedArray!(ubyte[])(size);
file.d:        void[] result = uninitializedArray!(ubyte[])(initialAlloc);
numeric.d:    import std.array : uninitializedArray;
numeric.d:        auto memSpace = uninitializedArray!(lookup_t[])(2 * size);
numeric.d:        ret = uninitializedArray!(Complex!F[])(range.length);
numeric.d:        ret = uninitializedArray!(Complex!F[])(range.length);
parallelism.d:                auto buf = uninitializedArray!(MapType!(Args[0], functions)[])(len);
parallelism.d:                    temp = uninitializedArray!Temp(workUnitSize);
parallelism.d:                    temp = uninitializedArray!Temp(workUnitSize);
stdio.d:    import std.array : appender, uninitializedArray;
stdio.d:                buf = uninitializedArray!(char[])(i);
stdio.d:                buf = uninitializedArray!(char[])(i);
stdio.d:    import std.array : appender, uninitializedArray;
utf.d:        import std.array : uninitializedArray;
utf.d:        auto copy = uninitializedArray!(Unqual!OutChar[])(str.length + 1);

So I already know there are serious problems. Looking closer, I see there's another function:

auto minimallyInitializedArray(T, I...)(I sizes) nothrow @trusted

which initializes everything to 0, pointers and all. I agree this one can be trusted. The difference between their implementations is a parameter they pass to arrayAllocImpl(), which says whether to 0 it or not.

But really, it's only pointer types (and types composed of pointers) that make it system code. Since uninitializedArray() is a template, we can have two implementations for it that are statically selected based on the element type.
One is @system, when E is a type that contains a pointer. The other is @trusted, when E is not a pointer.

That solves that problem without great effort.

Upstream of uninitializedArray(), the only problem then is when it is called to generate an uninitialized array of pointers. In order to make the call safe, the caller must then ensure that the array gets initialized with safe values. And that code should be properly marked as @trusted, and not before.

I.e. start at the bottom, and work up until you find the right place to make it trusted. Rinse, repeat. I am not seeing where the problem with doing this is.
February 06, 2015
On 2/5/2015 8:30 PM, Brad Roberts via Digitalmars-d wrote:
> @safe code is memory safe when passed good inputs.  @safe code does not have to
> detect or prevent garbage input from causing harm.   The promises of @trusted
> aren't stronger than that either.

This is correct. Thanks for catching it.
February 06, 2015
On Thursday, 5 February 2015 at 23:39:39 UTC, Walter Bright wrote:
>   static void trustedMemcopy(T[] dest, T[] src) @trusted
>   {
>     assert(src.length == dest.length);
>     memcpy(dest.ptr, src.ptr, src.length * T.sizeof);
>   }
>
> I don't have to review callers of trustedMemory() because it encapsulates an unsafe operation (memcpy) with a safe interface.

It might have done so if it ensured that T was a proper value type, but unfortunately D's type system is not strong enough.

What happens if T is a unique_ptr style reference? Ouch, two unique references to the same object. Ouch, memory unsafe.

@safe is a leaky cauldron and will continue to be so until you provide a proof of language constructs and how they interact. The only sane way to do that is to do the proof over a simplified virtual machine and map all language constructs to it.
February 06, 2015
On 2/5/2015 9:00 PM, "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= <ola.fosheim.grostad+dlang@gmail.com>" wrote:
> On Thursday, 5 February 2015 at 23:39:39 UTC, Walter Bright wrote:
>>   static void trustedMemcopy(T[] dest, T[] src) @trusted
>>   {
>>     assert(src.length == dest.length);
>>     memcpy(dest.ptr, src.ptr, src.length * T.sizeof);
>>   }
>>
>> I don't have to review callers of trustedMemory() because it encapsulates an
>> unsafe operation (memcpy) with a safe interface.
>
> It might have done so if it ensured that T was a proper value type, but
> unfortunately D's type system is not strong enough.
>
> What happens if T is a unique_ptr style reference? Ouch, two unique references
> to the same object. Ouch, memory unsafe.

Good point. Then a constraint can be added to the function signature that T is copyable.

D's type system is strong enough for that.
February 06, 2015
On Friday, 6 February 2015 at 05:32:48 UTC, Walter Bright wrote:
> On 2/5/2015 9:00 PM, "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= <ola.fosheim.grostad+dlang@gmail.com>" wrote:
>> On Thursday, 5 February 2015 at 23:39:39 UTC, Walter Bright wrote:
>>>  static void trustedMemcopy(T[] dest, T[] src) @trusted
>>>  {
>>>    assert(src.length == dest.length);
>>>    memcpy(dest.ptr, src.ptr, src.length * T.sizeof);
>>>  }
>>>
>>> I don't have to review callers of trustedMemory() because it encapsulates an
>>> unsafe operation (memcpy) with a safe interface.
>>
>> It might have done so if it ensured that T was a proper value type, but
>> unfortunately D's type system is not strong enough.
>>
>> What happens if T is a unique_ptr style reference? Ouch, two unique references
>> to the same object. Ouch, memory unsafe.
>
> Good point. Then a constraint can be added to the function signature that T is copyable.
>
> D's type system is strong enough for that.

What's the "D way" of checking if a parameter is a reftype, valuetype, etc?
February 06, 2015
On 2/5/2015 9:55 PM, weaselcat wrote:
> What's the "D way" of checking if a parameter is a reftype, valuetype, etc?

http://dlang.org/phobos/std_traits.html
February 06, 2015
On Thursday, 5 February 2015 at 23:39:39 UTC, Walter Bright wrote:
>   static void trustedMemcopy(T[] dest, T[] src) @trusted
>   {
>     assert(src.length == dest.length);
>     memcpy(dest.ptr, src.ptr, src.length * T.sizeof);
>   }

Should be enforce: assert doesn't guard against malicious usage.
February 06, 2015
On Friday, 6 February 2015 at 05:32:48 UTC, Walter Bright wrote:
> Good point. Then a constraint can be added to the function signature that T is copyable.
>
> D's type system is strong enough for that.

Yes, if you remember to add it.  The ideal would be to have the default constrained to proper value-types. D inherits some of C's weaker typing properties by trying to stay Cish to the programmer.

The fact that so many messages has been posted to this thread before someone caught it is testament to the burden put upon reviewers with "@trusted". After all, in debate-like threads like this people will look with higher scrutiny at posted code than in an average review...