May 19, 2013
On 5/19/13 1:12 PM, Idan Arye wrote:
> On Sunday, 19 May 2013 at 13:13:07 UTC, Andrei Alexandrescu wrote:
>> On 5/19/13 9:11 AM, deadalnix wrote:
>>> On Sunday, 19 May 2013 at 13:08:53 UTC, Andrei Alexandrescu wrote:
>>>> Sounds like a race problem unrelated to null. With non-null objects
>>>> the race would have manifested itself in a different way, perhaps even
>>>> more pernicious.
>>>>
>>>
>>> It is both a race condition and a null problem.
>>
>> No, it's just a race condition.
>>
>>> And having non nullable
>>> type would have been a compile time error instead of days of debugging.
>>
>> No, the race condition would have stayed.
>>
>>
>> Andrei
>
> I believe this claim requires an explanation:
>
> It's a good practice to initialize references(and all other types of
> variables) as soon as possible - and if possible, right away in the
> declaration. If that reference started as null, it's safe to assume it
> was not possible to initialized it at declaration, so it was
> intentionally initialized with null(if there was no initialization Java
> would scream at you).
>
> Now, let's assume that reference was non-nullable. It is safe to assume
> that this change would not remove the obstacle that prevented that
> reference from being initialized right away in the declaration - so you
> still need to initialize it to something else - let's call that
> something `Nil`. Nil is an object that tells you that the reference has
> not yet been initialized.
>
> So, in the original bug the reference "could be seen
> as null where it was assumed everywhere to be set.". But now we don't
> have null - so that piece of code that thought the reference is null
> would now think that it is... what? The initialization value? No!
> Because we didn't switch from initializing the reference with null to
> initializing it with the later initialization value - we couldn't do it.
> Instead, we had to use Nil. So now, the reference 'could be seen as Nil
> where it was assumed everywhere to be set'...
>
> Now, if you are lucky and your Nil is the better-kind-of-null that is
> used in dynamic object oriented languages, you'll get a nil exception -
> which is just as good as null exception. But if you are not so lucky,
> and you had to declare Nil as a blank object of the type of that
> reference, you are going to have logical bug, which is far worse than
> exceptions...

So the race would have manifested it just the same, except under the name of Nil instead of null.

Andrei
May 19, 2013
On Saturday, 18 May 2013 at 20:39:29 UTC, Walter Bright wrote:
> On 5/18/2013 1:22 PM, deadalnix wrote:
>> Many are, but I think that isn't the point we are discussing here.
>>
>> Removing all holes in @disable this will require the same sacrifices at the ends
>> than default constructor would. For isntance, what should happen in this case :
>>
>> S[] ss;
>> ss.length = 42;
>>
>> if S has @disable this ?
>
> Already reported:
>
> http://d.puremagic.com/issues/show_bug.cgi?id=10115

New case, will report it:

struct S
{
   int a;
   @disable this();
   this(int) { a = 1; }
   ~this() { assert(a !is 0); }
   alias a this;
   int opCall() { return a; }
}

void main()
{
   switch (S.init())
   {
      case 0:
         assert(0); //oops
      default:
   }
}

By the way, here is another bug.

I think there is disagreement about @disable reliability and usefulness and similar issues (@safe reliability too) due to different attitude to  the problem:
- As a language designer I care about whether some feature is claimed to solve some problem - and that all, I put it on a slide as lang advantage;
- As a programmer who writes medium importance code I care whether the feature stops me from making bugs unintentionally. If it does, than I consider that the feature works.
- As a programmer who writes critical code I care whether feature prevents from problem, even made deliberately, and if it doesn't, than the feature isn't reliable. It doesn't mean that it is totally useless, but it does mean that its reliability commitments are cheap.

Since in system language there is plenty of ways to deliberately pass invalid data to the place where some validity assumptions were made, @disable is a broken feature.
May 19, 2013
On 5/19/13 3:10 PM, deadalnix wrote:
> On Sunday, 19 May 2013 at 18:46:31 UTC, Walter Bright wrote:
>>> You also never provided any convincing solution to the safety hole.
>>> We can't
>>> even add check only on some edges cases as D also have values types.
>>> The only
>>> solution we are left with that is really safe is to null check every
>>> dereference
>>> or give up on @safe.
>>
>> Please don't make us guess what exactly you mean by this.
>
> This isn't new and I discussed that again and again.
>
> When you dereference null, you hit the first plage, which is protected
> on most systems. But if you access an element with sufficient offset you
> bypass all protections provided by the type system and you are back in
> unsafe world.

Oh, the good old "object of sufficient size". We know how to fix that.

> And no, putting nullcheck on access of field of sufficient offset (as
> propose dby Andrei) isn't enough because we have value types. Consider :
>
> S[BIG_NUMBER]* a;
> auto s = &(*a[SLIGHTLY_BELLOW_CHECK_OFFSET]);
> s.fieldAccess; // May not have enough offset to trigget null check, but
> still can be usnafe
>
> See bug reports :
> http://d.puremagic.com/issues/show_bug.cgi?id=3677
> http://d.puremagic.com/issues/show_bug.cgi?id=5176

All of the above are variations on the "sufficiently large object" theme.

Andrei
May 19, 2013
On Sunday, 19 May 2013 at 18:30:09 UTC, deadalnix wrote:
> void buzz(Foo f) {
>     f.foo(); // Rely in faith. It is invalid and way easier to write than the valid code, which is THE recipe for it to spread.
> }

Shouldn't this throw a NullPointerSomething?
May 19, 2013
On Sunday, 19 May 2013 at 19:10:28 UTC, Andrei Alexandrescu wrote:
> No, your argument is ridiculous. You make a yarn with precious little detail that describes for everything everyone knows a textbook race condition, essentially ask that you are taking by your word that non-null would miraculously solve it, and, to add insult to injury, and when we don't buy it, you put the burden of proof on us. This is quite a trick, my hat is off to you.
>

I described a very usual null bug : something is set to null, then to a specific value. It is assumed not to be null. In a specific case it is null and everything explode.

The concurrent context here made it especially hard to debug, but isn't the cause of the bug.

Additionally, if you don't have enough information to understand what I'm saying, you are perfectly allowed to ask for additional details This isn't a shame.

>> You also never provided any convincing solution to the safety hole.
>
> What's the safety hole? Objects of large static size?
>

Limiting object size isn't going to cut it. Or must be super restrictive (the protection is 4kb on some systems).

>> We
>> can't even add check only on some edges cases as D also have values
>> types. The only solution we are left with that is really safe is to null
>> check every dereference or give up on @safe.
>
> How about using NonNull. We won't change the language at this point to make non-nullable references by default. Even you acknowledged that that's not practical. So now you contradict your own affirmation. What exactly do you sustain, and what are you asking for?
>

1/ NonNull do not work.
2/ It isn't because it is too late to solve a problem that it magically isn't a problem anymore.

>> Most new languages removed nullable by default, or limited its uses
>> (scala for instance, allow for null for limited scope).
>
> So what do you realistically think we should do, seeing that we're aiming at stability?
>

Acknowledge it was a mistake and move on. Use the analysis that need to be done to track down @disable this issue to warn about uninitialized null stuffs.

>> I once again want to get attention on the fact that GC change everything
>> in regard to reference, and that the C++ situation is a bad example.
>
> I don't understand this.
>

I C or C++ you are doomed to manage reference as you need to for memory management purpose. In garbage collected languages, you ends up with way more unmanaged references, because the GC take care of them. Doing so you multiply the surface area where null bug can strike.
May 19, 2013
On Sunday, 19 May 2013 at 19:15:47 UTC, Andrei Alexandrescu wrote:
> Oh, the good old "object of sufficient size". We know how to fix that.
>
>> And no, putting nullcheck on access of field of sufficient offset (as
>> propose dby Andrei) isn't enough because we have value types. Consider :
>>
>> S[BIG_NUMBER]* a;
>> auto s = &(*a[SLIGHTLY_BELLOW_CHECK_OFFSET]);
>> s.fieldAccess; // May not have enough offset to trigget null check, but
>> still can be usnafe
>>
>> See bug reports :
>> http://d.puremagic.com/issues/show_bug.cgi?id=3677
>> http://d.puremagic.com/issues/show_bug.cgi?id=5176
>
> All of the above are variations on the "sufficiently large object" theme.
>
> Andrei

The code above never access a field with a sufficient offset to trigger "sufficiently large runtime check". Obviously, in the presented code the bug is trivial, but if the dereferences occurs across several functions, this is doomed to fail.

The solutions are : prevent any conglomerate of value type to be bigger than 4kb (the protection on OSX is 4kb) or put a null check on every dereference in @safe code.
May 19, 2013
On 5/19/2013 12:13 PM, Maxim Fomin wrote:
> Since in system language there is plenty of ways to deliberately pass invalid
> data to the place where some validity assumptions were made, @disable is a
> broken feature.

Please report all such holes to bugzilla.
May 19, 2013
On 5/19/2013 12:31 PM, Minas Mina wrote:
> On Sunday, 19 May 2013 at 18:30:09 UTC, deadalnix wrote:
>> void buzz(Foo f) {
>>     f.foo(); // Rely in faith. It is invalid and way easier to write than the
>> valid code, which is THE recipe for it to spread.
>> }
>
> Shouldn't this throw a NullPointerSomething?

It throws a seg fault at runtime. It *is* checked for by the hardware.
May 19, 2013
On 05/19/2013 09:10 PM, Andrei Alexandrescu wrote:
> On 5/19/13 1:35 PM, deadalnix wrote:
>> On Sunday, 19 May 2013 at 13:13:07 UTC, Andrei Alexandrescu wrote:
>>> On 5/19/13 9:11 AM, deadalnix wrote:
>>>> It is both a race condition and a null problem.
>>>
>>> No, it's just a race condition.
>>>
>>>> And having non nullable
>>>> type would have been a compile time error instead of days of debugging.
>>>
>>> No, the race condition would have stayed.
>>>
>>
>> That is ridiculous.  non nullable would have made the bug non existent,
>> and even without race condition the problem would exists. a reference is
>> null, it container shared, then set to something else. You can put
>> barriers all over the place to make that sequentially consistent that it
>> wouldn't change anything and the bug would still arise.
>
> No, your argument is ridiculous. You make a yarn with precious little
> detail that describes for everything everyone knows a textbook race
> condition, essentially ask that you are taking by your word that
> non-null would miraculously solve it, and, to add insult to injury, and
> when we don't buy it, you put the burden of proof on us. This is quite a
> trick, my hat is off to you.
> ...

It is easy to buy that the buggy code would have been rejected by a stronger type system whereas the fixed code would have passed type checking. Especially given that it manifested itself as a NPE, which would not even exist.

May 19, 2013
On 5/19/13 3:41 PM, deadalnix wrote:
> On Sunday, 19 May 2013 at 19:15:47 UTC, Andrei Alexandrescu wrote:
>> Oh, the good old "object of sufficient size". We know how to fix that.
>>
>>> And no, putting nullcheck on access of field of sufficient offset (as
>>> propose dby Andrei) isn't enough because we have value types. Consider :
>>>
>>> S[BIG_NUMBER]* a;
>>> auto s = &(*a[SLIGHTLY_BELLOW_CHECK_OFFSET]);
>>> s.fieldAccess; // May not have enough offset to trigget null check, but
>>> still can be usnafe
>>>
>>> See bug reports :
>>> http://d.puremagic.com/issues/show_bug.cgi?id=3677
>>> http://d.puremagic.com/issues/show_bug.cgi?id=5176
>>
>> All of the above are variations on the "sufficiently large object" theme.
>>
>> Andrei
>
> The code above never access a field with a sufficient offset to trigger
> "sufficiently large runtime check".

It does, when the pointer to the large static array is dereferenced.

Andrei