June 17, 2021

On Thursday, 17 June 2021 at 16:50:28 UTC, Paolo Invernizzi wrote:

>

On Thursday, 17 June 2021 at 16:21:53 UTC, Paul Backus wrote:

>

On Thursday, 17 June 2021 at 14:30:58 UTC, Steven Schveighoffer wrote:

>

[...]

Consider the following example:

size_t favoriteNumber() @safe { return 42; }

int favoriteElement(ref int[50] array) @trusted
{
    // This is memory safe because we know favoriteNumber returns 42
    return array.ptr[favoriteNumber()];
}

[...]

> >

There is no language change you can make (short of removing @trusted entirely) that will prevent this situation from arising.

Apart from reviewers asking the author of favoriteElement to assert that the index is appropriate ...

Yes, that's exactly my point. This can't be solved by changing the language, but it can be solved by a good code review process. So we should avoid wasting our time on language-based solutions (like Steven's proposal for @system blocks in @trusted functions), and instead focus on how to improve our code review process so that this kind of brittle @trusted code doesn't slip through.

For example, a review process that enforced the following rules would have flagged favoriteElement as problematic:

  1. Every use of @trusted must be accompanied by a comment containing a proof of memory safety.
  2. A memory-safety proof for @trusted code may not rely on any knowledge about other functions beyond what is implied by their signatures.

Specifically, favoriteElement violates rule (2). To bring it into compliance, we'd have to either add an assert to verify our assumption about favoriteNumber, or find a way to encode that assumption into favoriteNumber's signature (for example, with an out contract).

June 17, 2021

On Thursday, 17 June 2021 at 15:08:53 UTC, Dukc wrote:

>

No language can do this. C++ API does not provide any safety guarantees, so calling a C++ function means that it needs to be manually verified, or it's authors trusted, BY DEFINITION.

Sure, but that is obviously not enough. Because what is being said implies that @trusted code have to assume that anything it receives that isn't pointers can be garbage and that such garbage should never lead to memory unsafety even if you know that the @trusted function never receives garbage.

>

If that's the case, the only conclusion I can draw is that D philosophy is fundamentally wrong from your point of view. D is all about letting the programmer pick the paradigm according to the situation, instead of being designed for just one of them. This philosophy is rooted so deep that if it proves to be just plain wrong, were best off to just ditch D and switch to other languages.

I sure hope that won't happen.

My conclusion so far is that it is unrealistic to think that anyone would write code that satisfies that requirements put upon @trusted functions for a program the size of a desktop application.

It is even unrealistic to think that the average D programmer will understand what the requirements for @trusted are!

June 17, 2021

On Thursday, 17 June 2021 at 14:30:58 UTC, Steven Schveighoffer wrote:

>

The goal is to guarantee that as long as your @trusted functions and blocks have a @safe interface, then @safe code does not need to be checked. When I say "not require review" I mean "I have checked all the @trusted code, and it has a sound @safe interface, so all @safe code that may call it have no need for review." We will never have a marking that is language-guaranteed to not require review.

But doesn't this mean that having even a single @safe method on an ADT class would be a liability? So you are essentially forced to define them all as @trusted?

E.g.

class A {

    this() @trusted {
        ptr = &buffer[0];
        offset = 0;
    }

    int get() @trusted { return ptr[offset]; }
    void set(int i) @trusted { this.offset = i&1; }

    /*BUG: offset was pasted in here by mistake*/
    int size()@safe{ offset=2;  return 2;}

private:
    int[2] buffer;
    int* ptr;
    int offset;
}


Since this @safe size() function could in theory mess up offset by a bug, it should not be allowed?

However if we make size() @trusted then this is perfectly ok by the requirements?

As a result, you have to make ALL methods @trusted.

June 17, 2021
On Thu, Jun 17, 2021 at 05:14:12PM +0000, Paul Backus via Digitalmars-d wrote:
> On Thursday, 17 June 2021 at 16:50:28 UTC, Paolo Invernizzi wrote:
> > On Thursday, 17 June 2021 at 16:21:53 UTC, Paul Backus wrote:
[...]
> > > ```d
> > > size_t favoriteNumber() @safe { return 42; }
> > > 
> > > int favoriteElement(ref int[50] array) @trusted
> > > {
> > >     // This is memory safe because we know favoriteNumber returns 42
> > >     return array.ptr[favoriteNumber()];
> > > }
> > > ```
[...]
> 1. Every use of `@trusted` must be accompanied by a comment containing
> a proof of memory safety.
> 2. A memory-safety proof for `@trusted` code may not rely on any
> knowledge about other functions beyond what is implied by their
> signatures.
> 
> Specifically, `favoriteElement` violates rule (2). To bring it into compliance, we'd have to either add an `assert` to verify our assumption about `favoriteNumber`, or find a way to encode that assumption into `favoriteNumber`'s signature (for example, with an `out` contract).

Using an out contract is a rather weak guarantee, because the author of favoriteNumber can easily change the contract and silently break favoriteElement's assumptions.  Using an assert in favoriteElement is better, because if favoriteNumber ever changes in an incompatible way, the assert would trigger.


T

-- 
The best way to destroy a cause is to defend it poorly.
June 17, 2021

On Thursday, 17 June 2021 at 17:24:27 UTC, Ola Fosheim Grøstad wrote:

>

On Thursday, 17 June 2021 at 15:08:53 UTC, Dukc wrote:

>

No language can do this. C++ API does not provide any safety guarantees, so calling a C++ function means that it needs to be manually verified, or it's authors trusted, BY DEFINITION.

Sure, but that is obviously not enough. Because what is being said implies that @trusted code have to assume that anything it receives that isn't pointers can be garbage and that such garbage should never lead to memory unsafety even if you know that the @trusted function never receives garbage.

Ah, I guess the problem is that someone phrased that slightly wrong. Let me try a better formulation.

Given a module module_a, if a client module module_b that imports only module_b, and contains only @safe code cannot cause memory corruption (except due to compiler/OS/hardware bugs), then and only then API of module_a is sound with regards to memory safety.

This means that a @trusted or @safe function is allowed to assume certain invariants about some types, as long as those invariants cannot be violated from @safe client code alone. This also means that @safe code that is in module_a may be able to violate memory safety. DIP1035 aims to address that.

June 17, 2021

On Thursday, 17 June 2021 at 17:49:39 UTC, Dukc wrote:

>

Given a module module_a, if a client module module_b that imports only module_b

Meant: that imports only module_a

June 17, 2021

On Thursday, 17 June 2021 at 17:49:39 UTC, Dukc wrote:

>

This means that a @trusted or @safe function is allowed to assume certain invariants about some types, as long as those invariants cannot be violated from @safe client code alone. This also means that @safe code that is in module_a may be able to violate memory safety. DIP1035 aims to address that.

So if I control module_0 and module_a depends on it, then I can assume the invariants for types in module_0 as long as module_b cannot break those invariants from @safe code?

June 17, 2021
On 6/17/2021 3:33 AM, Alexandru Ermicioi wrote:
> Not always possible. Sometimes you have objects, that 90% are safe, and only 10% not. Having dedicated functions or interfaces for those 10% is just plain and unneeded clutter. How would I even name those methods/interfaces?

"Not possible" and "unneeded clutter" are unrelated. Anyhow, consider it a challenge to one's organizational skills. I didn't say it was always the easy path. But it's worth making the effort.


> Truth to be told, I gave in to this temptation, though they were one liners. But still I fear that this temptation is quite great, as not every software engineer is keen at keeping highest degree of safety and code quality. That is my concern why current use of @trusted, and trusted lambda might not be sufficient, to make it quite convenient for ordinary engineer to use them properly.

The trusted lambda (){}() is indeed bad, but blessing it with new syntax is much worse.
June 17, 2021
On 6/17/2021 4:19 AM, ag0aep6g wrote:
> It still has an interface, of course. The surrounding context acts as one large `ref` parameter. Strictly speaking, the programmer must ensure that the @trusted nested function doesn't create unsafe values in the outer function.

You're right. I made a mistake in not thinking about that when designing @trusted. @trusted lambdas should be `static` so that their interface is forced to be spelled out.


> However, even the standard library has more than enough instances of strictly-speaking-incorrect @trusted.

That's right. They should all be re-engineered.
June 17, 2021
On 6/17/2021 8:49 AM, Andrei Alexandrescu wrote:
> I think the whole discussion should be redirected toward simplifying `pure` instead.
> 
> * There are many legitimate reasons to want impure code act as pure.
> * There is no easy recourse as there is for @trusted. All approaches are crazily convoluted.

There are ways to do it that are in use in Phobos. It involves doing an unsafe cast. Doing things like that is why D supports @system code. I'm uncomfortable making it too easy to defeat the semantics of pure.