On Borrow Checking
Timon Gehr
timon.gehr at gmx.ch
Sun May 11 12:19:16 UTC 2025
On 5/10/25 23:20, Walter Bright wrote:
> 1. `scope` is not part of the type.
I stated as much, but it still influences type checking, so it is
formally part of the type system.
> It is a storage class, not a type
> modifier. The only type modifiers are immutable, const, shared and
> inout. Those are pervasively handled throughout the compiler's dealings
> with types. Making a new pointer type would be a huge effort *in
> addition* to all the other work needed to make a borrow checker.
> ...
Sure, so don't do it if you think it is not worth it. It's necessary to
have annotations that are part of types to make meaningful progress on
this though. Note that I am pretty sure that your issues with type
modifiers in DMD are largely due to the way they are implemented. I
don't think it would be very hard to add such features to my own frontend.
> 2. We have a lot of experience with transitive attributes. There's a lot
> of resistance to using them because of this. Just look at all the
> complaints about `nogc` being transitive.
Well, sometimes you want it to be transitive, sometimes you just want to
avoid implicit allocations. It's different use cases and they should be
distinct features. OpenD adopted my proposal for `pragma(explicit_gc)`
and it helped me chase down some unwanted implicit GC allocations in my
programs without flagging all the instances where I was deliberately
using GC.
> Personally, I find `const`
> being transitive as a serious impediment to using `const` in the dmd
> source code. One of the big problems is it doesn't work with the Visitor
> pattern. (There are a couple cases where `const` is just cast away,
> shame on me.)
> ...
Well yes, as I have been saying for years, don't use `const` if you want
to mutate. As you would not use features that restrict aliasing if you
want to freely alias. There is no free lunch, if you want
machine-checkable guarantees, you have to follow a certain discipline.
`@live` however is discipline for its own sake, without the slightest
hint of practical machine-checkable guarantees.
> 3. I'm well aware that ideally @live would be sounder if it is
> transitive.
No. Again, ideally there would be no `@live` function attribute in the
first place. It's the wrong way to decide where to do type checks.
Anyway, there is sound and unsound. And there are directions to follow
that can in principle lead to a sound design with enough effort, and
there are directions to follow that will never lead to a sound design.
`@live` is in the second category, `@safe` is in the first category.
> I disagree with the notion that it is useless if it is not.
I am not going to use it. It is useless to me. Not because it is not
transitive, but because it does not even pretend to want to achieve
anything.
> I am 98% sure that if @live was made transitive, it would be instantly
> useless because nobody is going to be willing to make an existing
> program or library pass the borrow checker.
It appears that you did not read what I wrote in my previous post, you
are still attacking the same straw man that I already disowned.
You are correct here. `@live` is useless whether it is transitive or not.
> (It took enormous effort for
> Rust to convince people they had to throw their existing code away and
> recode it from scratch to use the borrow checker.) Much of the existing
> code would have to be thrown out. Making a perfect borrow checker is the
> enemy of making a practical, useful one.
> ...
Nonsense. Being practical and useful is of course part of perfection.
Soundness is just also needed, if the utility sought is memory safety
guarantees.
And if the goal is not memory safety guarantees, I just don't see why
people would bother with this. What customer or boss will be like: "You
really need to use `@live` when you code up this program for me." I
really don't see it.
> 4. Making dip1000 transitive made it impractical to use in existing code.
> ...
No, the problem with DIP1000 you are referring to that it adds
additional restrictions for existing syntax, so if you want to adopt it
you have to first pay the up-front cost of updating your existing code.
This simply does not happen if the new feature uses syntax that is not
used in the wild.
Of course, even then you will get what you pay for in terms of
annotation and type checking overhead, but with `@live` you get way less
than what you pay.
Anyway, hardness of adoption is simply the nature of DIP1000, as its
goal was to fix holes in @safe without making certain use cases
impossible in @safe code outright. I suspect with editions, people will
be able to adopt DIP1000 (or its more expressive future successor)
incrementally, which will make it significantly more practical.
> 5. dip1000 makes it an error to assign a scope pointer to multiple
> indirections (because scope isn't a type modifier).
> ...
Sure. Code often has multiple indirections in practice though.
> 6. When an @live function calls another function, a `scope` parameter is
> a borrow, and a non-scope parameter is an ownership transfer.
> ...
Well, operationally it acts somewhat like these. Semantically this is
not what happens.
As I have stated in the past, it's a bit like cargo cults in the
pacific. Operationally, you have people that act kind of like staff
maintaining a landing strip. In practice, no planes actually are landing
or taking off from there.
`@live` signs contracts for borrowing and/or sale with much fanfare and
will sometimes stop you from doing what you want to do, but no lending
takes place and no ownership is actually transferred.
> 7. Compiling an @live function is slow because of the DFA. Making it
> transitive would make it apply to the whole program, which would then
> compile as slow as Rust.
>
Not true, you only have to enhance type checking where the features are
actually used. This is not a `@live`-specific thing at all.
More information about the Digitalmars-d
mailing list