On Friday, 19 February 2016 at 10:03:44 UTC, Walter Bright wrote:
On 2/18/2016 11:46 PM, Jonathan M Davis wrote:
We're in exactly the same boat as C++ in that we rely on the
programmer to be smart about casting away const in order for const to provide
any guarantees.

No, we're not. @trusted is specifically designed to be greppable (so are casts, by the way). This is quite unlike C++. I defy you (or anyone) to look at a piece of non-trivial C++ code and verify there are no deviated preversions going on.

D is simply not in the same boat at all.

D is definitely in a better boat in that it's easier to find and catch problems with const, but it really doesn't do much more to actually guarantee that a const object isn't being violated. Programmers are free to do horrible things and mark it as @trusted, possibly thinking that what they're doing is fine. And in D, programmers are frequently tempted to cast away const thinking that it's a legitimate backdoor like it is in C++ just so long as the object isn't actually immutable, especially because there is no mutable keyword in D.

We can debate about the holes in @safe that get bug reports filed on them, but these are bugs and we intent to fix all of them. There is no plan nor proposal to fix C++ unsafety.

Sure, but the main problem here is with code that is marked as @trusted. If you want to be 100% sure that an object isn't mutated via a const reference, you're still ultimately forced to dig through the code to verify it. That attack space is much reduced in comparison to C++, and it's much more greppable, so finding and fixing the problem is much easier, but the compiler still ultimately fails to actually guarantee that an object is not mutated via a const reference. To do that, it would have to be outright illegal to cast away const.

So, are we in a much better position than C++ with regards to const protecting against mutation? Yes. But we're not actually preventing it. And because of how restrictive D's const is, folks either end up casting away const to mutate, or they abandon const altogether. And if they abandon const altogether, then they actually end up with more error-prone code, because they don't have it preventing any mutation at all. Even protection with holes is better than no protection. But we don't even need to add holes on the same level as C++ in order to make D's const considerably more usable. I completely agree that we shouldn't make casting away const and mutating defined behavior (much as we can't actually prevent it), but having an equivalent to C++'s mutable would help considerably.

> I said that a type with an @mutable member would be forced to
be marked with @mutable as well

You're proposing '@mutable const' ?

No, whether a type has @mutable members is part of its definition, not a type qualifier. I'm proposing that we have something like

@mutable struct S
{
    int i;
    @mutable int* refCount;
    ...
}

or

@mutable class C
{
public:
    @property int i() shared const { Guard guard(m); return i; }
    @property void i(int val) shared { Guard guard(m); i = val; }
private:
    shared int i;
    shared @mutable Mutex m;
}

Then if you had an instance of C which was const, you could still mutate the Mutex member inside of a const function, and if it had a const instance of S, it could still mutate its ref-count appropriately in its postblit constructor and assignment operator and whatnot. And code which used S or C, would just use it like it would now. e.g.

    const S s;
    auto r1 = s.foo();

    const c = new C;
    auto r2 = c.i;

So, functionally, it should act the same as mutable in C++. However, it would act like abstract in that it would go with the members that it affects and with the type that contains such members. Any type that then contained such a type would also have to be marked with @mutable. e.g.

@mutable struct Foo
{
    // A member whose type declaration is marked with @mutable,
    // but the member variable itself is not @mutable
    C c;
}

Then if someone needed a mutable member, they could use @mutable just like they'd use the mutable keyword in C++, except that they'd also have to put it on the struct/class itself so that it works with opaque types and always makes it clear to the compiler that a type contains @mutable members and that it cannot do things like construct an immutable instance of it. If we had that, then all of the common uses cases for mutable in C++ that we currently cannot have in D (e.g. ref counting, mutexes, caching, etc.) could be used. And the code base wouldn't have to be littered with extra modifiers - just on the type declarations themselves for the types that have @mutable members and on the @mutable members themselves. Yes, it would be creating a backdoor in const, but it would be much safer and much more restricted than casting away const and mutating (assuming that that were legit), and it would take away most of the incentive to cast away const. It also wouldn't muck up const for code that doesn't use @mutable, and just like @trusted or cast, it would be highly greppable.

Certainly, if we're going to add a backdoor, I think that something well-controlled like this is the way to go, and if we don't add any backdoors, I expect that a lot of D code simply won't use const, and it'll lose out completely on the guarantees that const provides, leading to more bugs. Adding @mutable will increase type safety for code that can't currently use const while not reducing type safety for code that already uses const.

- Jonathan M Davis

Reply via email to