January 24, 2023
On 24/01/2023 8:04 AM, Paul Backus wrote:
> Then again, it's not obvious that implementing `isolated` would be *less* work than reimplementing `scope` from scratch, so maybe it's just as good a proposal. 😄

Lol yeah.

Realistically somebody should give it a go, see how things turn out, only way to see what the true cost of any approach would be.
January 24, 2023

On Monday, 23 January 2023 at 17:49:20 UTC, Richard (Rikki) Andrew Cattermole wrote:

>

Perhaps something like this (note ref not actually required for pointer types):

struct Thing(T) {
	ref T get() scope { ... }
}

{
	Thing thing;
	scope ref got = thing.get; // owner = thing
	func(got); // ok parameter is scope

	thing = Thing.init;// Error: thing must out live got variable, but thing is being assigned to.
	return got; // Error: scope variable thing must out live got variable, but got is being returned.
}

void func(scope ref T value) {}

For the rest, I'm glad that we are converging on a possible position :)

I'm afraid it's more complicated than you think.

thing might have its destructor called before the end of got lifetime. The language could pretty trivially prevent doing that directly, but what if you have a scope pointer to thing and call the destructor via it? or a scope SumType!(Thing*, int*)[5] variable, that may contain both references to both things and ints?

These are probably solvable, but the solution is going to be at least as complex as @live, if not more so.

January 24, 2023

On Monday, 23 January 2023 at 17:44:03 UTC, Paul Backus wrote:

> >

We could require that any allocator that's supposed to be safe for reference counting must have a member function @trusted @disable void refCountCertificate();, with @trusted attribute being checked for. That way it's not possible to corrupt memory in @safe code without writing at least one @trusted function.

This would technically work, but I do not think I could look someone in the eye who was new to D and explain it without dying of embarrassment.

Now when I think of it, it probably should be called an allocator certificate and used for all allocators. @trusted required only if any of the other allocation primitives are @system. We have no reason to support allocators which might corrupt memory if they free their own memory after all, so no reason to name the certificate after reference counting.

Anyway, what's the problem? Perhaps we can improve this solution somehow. I agree with you that we do not want to wait for any major language-level changes, considering how long it took for DIP1000 to become stable.

January 25, 2023
On 25/01/2023 3:39 AM, Dukc wrote:
> I'm afraid it's more complicated than you think.

I expect it will be complicated. DFA for this sort of thing always is.

> `thing` might have its destructor called before the end of `got` lifetime. The language could pretty trivially prevent doing that directly, but what if you have a `scope` pointer to `thing` and call the destructor via it? or a `scope SumType!(Thing*, int*)[5]` variable, that may contain both references to both `thing`s and ints?

Yes, you need to track the 'real' owner for memory and ensure the right order of variable destruction.

> These are probably solvable, but the solution is going to be at least as complex as `@live`, if not more so.

Considering @live isn't complete, I'd argue implementing @live is more complicated than @live ;)

The singular difference is @live is opt-in, function by function. This on the other hand isn't, which means its guarantees are actually real for memory safety.
January 24, 2023

On Tuesday, 24 January 2023 at 14:51:06 UTC, Dukc wrote:

>

I agree with you that we do not want to wait for any major language-level changes, considering how long it took for DIP1000 to become stable.

Also I suspect the benefit-to-complexity ration of isolated or a complete borrow checker would be poor. It fits Rust, since it's a dedicated systems programming language. D is an application programming and scripting language as much as it's a systems language, so for us it isn't as good fit.

After all, GC solves the majority of memory safety issues already, and DIP1000 can solve a big part, if not the majority of what isn't solved by the GC.

January 24, 2023

On Tuesday, 24 January 2023 at 15:03:46 UTC, Dukc wrote:

>

On Tuesday, 24 January 2023 at 14:51:06 UTC, Dukc wrote:

>

I agree with you that we do not want to wait for any major language-level changes, considering how long it took for DIP1000 to become stable.

Also I suspect the benefit-to-complexity ration of isolated or a complete borrow checker would be poor. It fits Rust, since it's a dedicated systems programming language. D is an application programming and scripting language as much as it's a systems language, so for us it isn't as good fit.

It's worth noting that D already has a bunch of special-case language rules that would be unified by adding isolated.

For example: return values of so-called "Pure factory functions" are allowed to implicitly convert to immutable, because "all mutable memory returned by the call cannot be referenced by any other part of the program"--or, in other words, the return value is isolated.

There's a similar rule for new expressions, although it doesn't seem to be written down anywhere. You can see it at work in code like this:

// converts from mutable to immutable
immutable(int[][]) a = new int[][](3, 3);

Replacing all of these special cases with a single consistent set of rules would help simplify the language.

January 24, 2023

On Tuesday, 24 January 2023 at 16:11:00 UTC, Paul Backus wrote:

>

It's worth noting that D already has a bunch of special-case language rules that would be unified by adding isolated.

[snip]

Replacing all of these special cases with a single consistent set of rules would help simplify the language.

That's precisely what made the bottom type such a great DIP from the perspective of use. However, as with the bottom type, it isn't any easier to write the DIP nor implement nor debug it. Meaning, in the long run we might well want isolated, but allocator-aware reference counter should not be waiting for one - which I think is what you said.

January 24, 2023

On Tuesday, 24 January 2023 at 16:21:37 UTC, Dukc wrote:

>

Meaning, in the long run we might well want isolated, but allocator-aware reference counter should not be waiting for one - which I think is what you said.

Oh sorry, I remembered Paul's earlier post wrong. He wrote:

>

I agree that it's bad UX, but what's the alternative?
Implement a borrow checker in D? It'll take 5-10 years and
won't even work properly when it's done. At that point, you
may as well just tell people to switch to Rust.

..but that was referring to the borrow function, not allocators.

January 24, 2023

On Sunday, 22 January 2023 at 21:11:31 UTC, RTM wrote:

>

On Sunday, 22 January 2023 at 15:28:53 UTC, Atila Neves wrote:

>

I'm pretty much convinced we need isolated.
Seems redundant to dip1021 (@live).

From the @live docs:

>

Multiple borrower pointers can simultaneously exist if all of them are pointers to read only (const or immutable) data

AFAIU isolated needs to guarantee no other references point to the data, so that we can call free on it without dangling references. That applies even if the data is immutable, so we can put immutable data on the heap.

January 24, 2023

On Tuesday, 24 January 2023 at 21:01:04 UTC, Nick Treleaven wrote:

>

[snip]

AFAIU isolated needs to guarantee no other references point to the data, so that we can call free on it without dangling references. That applies even if the data is immutable, so we can put immutable data on the heap.

I would think that a hypothetical isolated would apply to mutable data or pointers to mutable data. If the data is const or immutable, then D's transitivity implies that all pointers to it are read only.