November 20, 2019
On Wednesday, 20 November 2019 at 13:51:34 UTC, rikki cattermole wrote:
> On 21/11/2019 1:16 AM, Timon Gehr wrote:
>> Right now, the only use I can see for @live is as an incomplete and unsound linting tool in @system code. It doesn't make @safe code any more expressive. To me, added expressiveness in @safe code is the whole point of a borrowing scheme.
>
> You touch upon a very good point.
>
> I'm starting to think of @live as a superset of @safe. With @trusted being an escape route. If this is the case then perhaps making all pointers non-null (with asserts) would make sense.


Indeed it looks that @live sits on top of @safe. I would love to see this default to @safe when -preview=borrow (don't think it is implemented ATM) is enabled.

I guess the reason for @live is backwards compatibility, but even with this I think using @safe(own), name up to discussion - meaning having @safe accept a parameter, would serve better to limit the attribute soup.
November 20, 2019
On Wednesday, 20 November 2019 at 06:57:55 UTC, mipri wrote:
>
>   void main() {
>       auto p = cast(Person*)malloc(Person.sizeof + 10);
>       birthday(p);
>       p.name.ptr[0] = 'B';
>       writeln(p);
>   }
>

If you would make main @live, would the compiler automatically put in a call to free for variable 'p' at the end of main?


November 20, 2019
On Wednesday, 20 November 2019 at 18:32:42 UTC, IGotD- wrote:
> On Wednesday, 20 November 2019 at 06:57:55 UTC, mipri wrote:
>>
>>   void main() {
>>       auto p = cast(Person*)malloc(Person.sizeof + 10);
>>       birthday(p);
>>       p.name.ptr[0] = 'B';
>>       writeln(p);
>>   }
>>
>
> If you would make main @live, would the compiler automatically put in a call to free for variable 'p' at the end of main?

How I read it is that @live doesn't know anything about malloc or
free (except what it knows by their signatures: that malloc returns
a pointer and that free takes ownership of a pointer). A glance
through the commits seems to support this; I don't see references
to either function outside of unit tests, and the docs mention that
there's no protection against using the wrong allocator for a
pointer, like free_allocator2(malloc_allocator1());

This compiles without error, for example:

  import std;
  import core.stdc.stdlib: malloc;

  struct Person {
      uint age;
      uint karma;
      char[0] name;
  }

  @live void birthday(scope Person* p) {
      p.age++;
  }

  void consume(Person* p) { }

  @live void main() {
      auto p = cast(Person*)malloc(Person.sizeof + 10);
      birthday(p);
      writeln(p.age);
      consume(p);
  }

Where valgrind points out the use of undefined memory and the
leaked memory. @live just cares that consume() took ownership of
the pointer, even if consume() is not a responsible owner.

Of course you get an error if you try to add @live to consume().

@live isn't a superset of @safe either, since it permits the cast.

November 20, 2019
On Wednesday, 20 November 2019 at 08:19:46 UTC, Walter Bright wrote:
>> Are you really certain there's no way to do this without adding yet
>> more attributes?
>
> We'll never be able to compile C-like code if we force an O/B system on all the code. There has to be a way to distinguish, like what `pure` does. D would be unusable if everything had to be `pure`. My understanding of O/B is you're going to have to redesign code and data structures to use it effectively. I.e. it'll break everything. Rust has a powerful enough marketing machine to convince people that redesigning your programs is a Good Thing (tm) and perhaps it is, but we don't have the muscle to do that.

An attribute you put on every function just isn't the greatest. If you do fix "@live:" and you can put it at the start of the file, then how do you disable it for a section of code you don't want it on? This is the problem with @nogc as well and possibly some of the other attributes. This leads to every function being individually marked as such as needed. This doesn't really add on to the problem, but it just introduces a new feature with the same problem as those before it. I don't see why this can't just be attached to @safe, if this is a feature to prevent memory corruption. That's exactly what @safe is for. If your porting C code you aren't going to be using @safe anyways. Just as you wouldn't be using @live either as you'd effectively have to completely rewrite your code.

November 20, 2019
On Wednesday, 20 November 2019 at 19:14:52 UTC, Jab wrote:
> new feature with the same problem as those before it. I don't see why this can't just be attached to @safe, if this is a feature to prevent memory corruption. That's exactly what @safe is for. If your porting C code you aren't going to be using @safe anyways. Just as you wouldn't be using @live either as you'd effectively have to completely rewrite your code.

Yes, something like "@live" does not really work unless it is a default you occasionally try to escape from. So it seems like it would be better to have "@nolive" instead of "@live", but maybe I don't understand what it is meant to enable yet.

I think @nogc is different, though, you should never be able to escape from that.  @nogc is there so that you can ship executables with a bare bones runtime.  It is basically available so that both GC and non-GC programs can share libraries, IMO.


November 20, 2019
On Wednesday, 20 November 2019 at 19:14:52 UTC, Jab wrote:
> I don't see why this can't just be attached to @safe, if this is a feature to prevent memory corruption. That's exactly what @safe is for.

Well, @live enables a static check, with no runtime cost, but
adding @safe to code also makes it retain bounds checks by default.
@live you might want to just place everywhere you can, at a cost
only to programmer time, but if you add @safe everywhere you might
then want to remove bounds checks also from @safe code, which would
removes the potentially useful distinction.

Anyway, the comment from the pull request is "This is far enough
along that it can be experimented with."

For example, if you think @live should be folded into @safe, you
can write some code while always trying to put @safe alongside
@live, and maybe you'll end up with some results like:

a. I was always able to easily do this, but now I have all these
"@safe @live" in my code and frankly any function with only one of
these attributes is only going to lack the other attribute due to
an oversight, so why not merge these attributes?

b. I found I couldn't add @live to lots of @safe functions for
whatever reason. If these attributes were merged I would have to
downgrade these functions to @trusted, and in practice I'd lose a
ton of safety provisions, just because my code is incompatible with
borrow checking.

c. I found that trying to add @safe to @live functions resulted in
my breaking portions of the @live functions code out into new
@trusted @live functions. If @live was merged with @safe I'd just
lose borrow checking of these functions.

An example of the third case follows, where factory() and then
newPerson() are results of wanting to add @safe.

import std;
import core.stdc.stdlib: malloc;

struct Person {
    uint age;
    uint karma;
    char[0] _name; // a C-style "flexible array member"

    static Person* factory(size_t size) @live @safe
    in { assert(size >= 4); }
    body {
        Person* newPerson() @live @trusted {
            auto partial = cast(Person*)malloc(Person.sizeof + size);
            partial._name.ptr[0] = '\0';
            return partial;
        }
        auto p = newPerson();
        p.age = uint.init;
        p.karma = uint.init;
        return p;
    }

    // Conditional jump or move depends on uninitialised value(s)
    void identify() @live @trusted {
        _name.ptr[0..4] = "Bob\0";
    }

    string name() @live @trusted {
        return _name.ptr.fromStringz.idup;
    }
}

void birthday(scope Person* p) @live @safe {
    p.age++;
}

void consume(Person* p) @safe { }

void main() @safe @live {
    auto p = Person.factory(4);
    birthday(p);
    p.identify();
    writeln(p.name, " ", p.age);
    consume(p);
}


November 20, 2019
On 11/20/2019 5:46 AM, jmh530 wrote:
>  From here [1], Walter says that "OB will also be turned off in @system code." But I don't see anything about @safe or @sysstem in the changelog/ob.md file.

That was the original plan, making @live an extension of @safe. But in implementing it, I realized it could be used as an independent attribute, and implemented it that way. So yes, you can use it in @system code, and I think it can add value to it, especially code that is a snarl of pointer allocations and frees.
November 20, 2019
On 11/20/2019 5:51 AM, rikki cattermole wrote:
> If this is the case then perhaps making all pointers non-null (with asserts) would make sense.

I originally was going to add null to the data flow analysis. But I realized it would be rather useless:

  T* foo();

  T* p = foo(); // is p null or not?

Very quickly, the flow analysis would drop into "dunno if it is null or not" so it just won't be worth much.

In order to make non-null checking actually work, the language semantics would likely need to change to make:

   T*    a non-null pointer
   T?*   an optionally null pointer

or something like that. Two different pointer types would need to exist.

Something like this is orthogonal to what @live is trying to do, so I put it on the shelf for the time being.
November 20, 2019
Your experiments with @live shows that we need more experience with @live before we can make a proper determination as to how it fits in with other attributes. In the meantime, I like it being an independent attribute which makes it easy to experiment with in various combinations with other attributes.
November 20, 2019
On 11/20/2019 10:52 AM, mipri wrote:
> How I read it is that @live doesn't know anything about malloc or
> free (except what it knows by their signatures: that malloc returns
> a pointer and that free takes ownership of a pointer).

That is correct. It only checks that the pointer value gets "consumed" exactly once. Passing it to another function that takes ownership of the pointer value counts as "consuming" it. What that function does with the pointer value is not the concern of the @live function.


> Where valgrind points out the use of undefined memory and the
> leaked memory. @live just cares that consume() took ownership of
> the pointer, even if consume() is not a responsible owner.
> 
> Of course you get an error if you try to add @live to consume().
> 
> @live isn't a superset of @safe either, since it permits the cast.

Exactly right.