March 05, 2009
Denis Koroskin wrote:
> On Wed, 04 Mar 2009 19:39:06 +0300, Don <nospam@nospam.com> wrote:
> 
>> Andrei Alexandrescu wrote:
>>> Don wrote:
>>>> Andrei Alexandrescu wrote:
>>>>> And there is no reference type with two subtypes. It's one type in the language and one in the library. Maybe-null (the library) is a supertype of non-null (the default).
>>>>
>>>> One problem I can see is with extern(C),(Windows) functions, since pointers are maybe-null in C. The name-mangling has to work out.
>>>> I can't see how this can be done without the compiler knowing SOMETHING about both nullable and non-nullable types.
>>>> At the bare minimum, you need to deal with maybe-null returns and reference parameters from C functions.
>>>  Walter is thinking of making only references non-null and leaving pointers as they are. (I know, cry of horror.) But say pointers are also non-null. Then:
>>>  extern(C) MaybeNull!(void*) malloc(size_t s);
>>>  will work, provided that MaybeNull has no size overhead and that word-sized structs are returned in the same register as word returns (I seem to remember Walter told me that's the case already).
>>
>> Here's a typical annoying Windows API function
>> --------
>> int GetTextCharsetInfo(
>>    HDC hdc,                // handle to DC
>>    LPFONTSIGNATURE lpSig,  // data buffer
>>    DWORD dwFlags           // reserved; must be zero
>> );
>>
>> lpSig
>> [out] Pointer to a FONTSIGNATURE data structure that receives font-signature information. The lpSig parameter can be NULL if you do not need the FONTSIGNATURE information.
>> ---------
>> How do you do this?
>>
>> Don.
> 
> extern(System) int GetTextCharsetInfo(
>     HDC hdc,
>     MaybeNull!(FONTSIGNATURE*) lpSig, // or whatever
>     DWORD dwFlags);
> 
> GetTextCharsetInfo(hdc, null, flags); // fine
> GetTextCharsetInfo(hdc, &sig, flags); // also ok
> 
But it needs to have the type name mangled as LPFONTSIGNATURE, not as MaybeNull!(FONTSIGNATURE*). Otherwise it can't link to Windows.
March 05, 2009
On Thu, 05 Mar 2009 09:09:42 +0100, Don <nospam@nospam.com> wrote:

>Denis Koroskin wrote:
>> On Wed, 04 Mar 2009 19:39:06 +0300, Don <nospam@nospam.com> wrote:
>> 
>>> Andrei Alexandrescu wrote:
>>>> Don wrote:
>>>>> Andrei Alexandrescu wrote:
>>>>>> And there is no reference type with two subtypes. It's one type in the language and one in the library. Maybe-null (the library) is a supertype of non-null (the default).
>>>>>
>>>>> One problem I can see is with extern(C),(Windows) functions, since
>>>>> pointers are maybe-null in C. The name-mangling has to work out.
>>>>> I can't see how this can be done without the compiler knowing
>>>>> SOMETHING about both nullable and non-nullable types.
>>>>> At the bare minimum, you need to deal with maybe-null returns and
>>>>> reference parameters from C functions.
>>>>  Walter is thinking of making only references non-null and leaving
>>>> pointers as they are. (I know, cry of horror.) But say pointers are
>>>> also non-null. Then:
>>>>  extern(C) MaybeNull!(void*) malloc(size_t s);
>>>>  will work, provided that MaybeNull has no size overhead and that
>>>> word-sized structs are returned in the same register as word returns
>>>> (I seem to remember Walter told me that's the case already).
>>>
>>> Here's a typical annoying Windows API function
>>> --------
>>> int GetTextCharsetInfo(
>>>    HDC hdc,                // handle to DC
>>>    LPFONTSIGNATURE lpSig,  // data buffer
>>>    DWORD dwFlags           // reserved; must be zero
>>> );
>>>
>>> lpSig
>>> [out] Pointer to a FONTSIGNATURE data structure that receives
>>> font-signature information. The lpSig parameter can be NULL if you do
>>> not need the FONTSIGNATURE information.
>>> ---------
>>> How do you do this?
>>>
>>> Don.
>> 
>> extern(System) int GetTextCharsetInfo(
>>     HDC hdc,
>>     MaybeNull!(FONTSIGNATURE*) lpSig, // or whatever
>>     DWORD dwFlags);
>> 
>> GetTextCharsetInfo(hdc, null, flags); // fine
>> GetTextCharsetInfo(hdc, &sig, flags); // also ok
>> 
>But it needs to have the type name mangled as LPFONTSIGNATURE, not as MaybeNull!(FONTSIGNATURE*). Otherwise it can't link to Windows.

Parameter names are not mangled for stacall, only their total size. IIRC, the above will get mangled into _GetTextCharsetInfo@12 just like it would without MaybeNull

March 05, 2009
Max Samukha wrote:
> On Thu, 05 Mar 2009 09:09:42 +0100, Don <nospam@nospam.com> wrote:
> 
>> Denis Koroskin wrote:
>>> On Wed, 04 Mar 2009 19:39:06 +0300, Don <nospam@nospam.com> wrote:
>>>
>>>> Andrei Alexandrescu wrote:
>>>>> Don wrote:
>>>>>> Andrei Alexandrescu wrote:
>>>>>>> And there is no reference type with two subtypes. It's one type in the language and one in the library. Maybe-null (the library) is a supertype of non-null (the default).
>>>>>> One problem I can see is with extern(C),(Windows) functions, since pointers are maybe-null in C. The name-mangling has to work out.
>>>>>> I can't see how this can be done without the compiler knowing SOMETHING about both nullable and non-nullable types.
>>>>>> At the bare minimum, you need to deal with maybe-null returns and reference parameters from C functions.
>>>>>  Walter is thinking of making only references non-null and leaving pointers as they are. (I know, cry of horror.) But say pointers are also non-null. Then:
>>>>>  extern(C) MaybeNull!(void*) malloc(size_t s);
>>>>>  will work, provided that MaybeNull has no size overhead and that word-sized structs are returned in the same register as word returns (I seem to remember Walter told me that's the case already).
>>>> Here's a typical annoying Windows API function
>>>> --------
>>>> int GetTextCharsetInfo(
>>>>    HDC hdc,                // handle to DC
>>>>    LPFONTSIGNATURE lpSig,  // data buffer
>>>>    DWORD dwFlags           // reserved; must be zero
>>>> );
>>>>
>>>> lpSig
>>>> [out] Pointer to a FONTSIGNATURE data structure that receives font-signature information. The lpSig parameter can be NULL if you do not need the FONTSIGNATURE information.
>>>> ---------
>>>> How do you do this?
>>>>
>>>> Don.
>>> extern(System) int GetTextCharsetInfo(
>>>     HDC hdc,
>>>     MaybeNull!(FONTSIGNATURE*) lpSig, // or whatever
>>>     DWORD dwFlags);
>>>
>>> GetTextCharsetInfo(hdc, null, flags); // fine
>>> GetTextCharsetInfo(hdc, &sig, flags); // also ok
>>>
>> But it needs to have the type name mangled as LPFONTSIGNATURE, not as MaybeNull!(FONTSIGNATURE*). Otherwise it can't link to Windows.
> 
> Parameter names are not mangled for stacall, only their total size.
> IIRC, the above will get mangled into _GetTextCharsetInfo@12 just like
> it would without MaybeNull
> 
Cool. So it'd only be an issue with extern(C++).
March 05, 2009
Nick Sabalausky wrote:
> "Walter Bright" <newshound1@digitalmars.com> wrote:
>> I started my career doing flight critical mechanical designs for Boeing airliners. I had it pounded into me that no matter how perfect you designed the parts, the next step is "assume it fails. Now what?" That is why Boeing airliners have incredible safety records.

Yup. That's what McDonnell didn't do with the DC-10. They were crashing mysteriously in mid-fligt, and nobody survived to tell.

The DC-10 had three entirely separate steering systems: a mechanical (as  in wires from cockpit to ailerons), a hydraulic one, and an electrical system.

After a superior pilot(1) actually brought his plane home after disaster struck, it was found out that the reason to all the crashes was a cargo door lock, which could be shut carelessly and then, if the ground guy was strong enough, lock the latch by force, leaving it only partly locked. Once in the air, the airpressure blew the door open, resulting in the passenger floor collapsing, and shredding the steering systems.

The "non-Boeing" designers had drawn all three steering systems next to each other, above the cargo door, below the passenger floor.

>> Assume the parts break. Assume the hydraulics are connected backwards. Assume all the fluid runs out of the hydraulics. Assume it is struck by lightning. Assume it is encased in ice and frozen solid. Assume the cables break. Assume a stray wrench jams the mechanism. Assume it rusts away. Assume nobody lubricates it for years. Assume it was assembled with a bad batch of bolts. Etc.

My father was an airline pilot, who had participated in crash investigations. Ever since I was a kid I got it hammered in my head that things break, period. And people make mistakes. Double period!

For example, it happens that car tires blow. In the old days, a front tire blowing usually meant you ended up in the ditch or a tree. Volkswagen designed the first car not to veer off the road when that happens, the Golf. The front suspension geometry was such that you didn't even have to have your hands on the steering wheel when the tire blows. No problem.

(But the funny thing is, the average driver shouldn't know about that, or he will compensate it with even sloppier driving.)

>> If software is in your flight critical systems, the way one proceeds is to *assume skynet takes it over* and will attempt to do everything possible to crash the airplane.
> 
> You're dodging the point. Just because these failsafes might exist does *NOT* excuse the processes from being lax about their reliability in the first place. What would Boeing have said if you designed a bolt with a fatal flaw and excused it with "It's ok, we have failsafes!".

Recently, in Sweden, it became known that supervisors in this ultra safe Nuclear Power Plant regularly drank beer on duty. "Why stay alert when nothing ever happens, and even if it does, this plant will shut itself down in an orderly manner." Homer Simpson, anyone?

(1) A superior pilot: he learns more than the teachers force him to. He tries to Understand the mechanics and machinery, as opposed to just using it by the manual. He constantly conjures up disaster scenarios and figures out how to deal with them (methods). He also "preloads" such methods in his brain during the various phases of flight.

At sudden danger, it is much more efficient to have the preloads at hand, rather than having to start inventing graceful exits when the cockpit is full of hands on the wheel and the knobs.

These practices have saved my car from being totalled more than once. While it may look difficult to apply this to software development, especially in one-man projects, the value of this can't be underestimated. When a habit and team practice, it helps productivity. Design by contract is but one example in this direction.

PS: it turned out that the DC-10 can be flown without flight controls. Since the three engines make a triangle (as looked at from the front), one can control the plane enough. The engine controls were not drawn next to the cargo door.
March 05, 2009
On Thu, 05 Mar 2009 10:06:14 +0100, Don <nospam@nospam.com> wrote:

>Max Samukha wrote:
>> On Thu, 05 Mar 2009 09:09:42 +0100, Don <nospam@nospam.com> wrote:
>> 
>>> Denis Koroskin wrote:
>>>> On Wed, 04 Mar 2009 19:39:06 +0300, Don <nospam@nospam.com> wrote:
>>>>
>>>>> Andrei Alexandrescu wrote:
>>>>>> Don wrote:
>>>>>>> Andrei Alexandrescu wrote:
>>>>>>>> And there is no reference type with two subtypes. It's one type in the language and one in the library. Maybe-null (the library) is a supertype of non-null (the default).
>>>>>>> One problem I can see is with extern(C),(Windows) functions, since
>>>>>>> pointers are maybe-null in C. The name-mangling has to work out.
>>>>>>> I can't see how this can be done without the compiler knowing
>>>>>>> SOMETHING about both nullable and non-nullable types.
>>>>>>> At the bare minimum, you need to deal with maybe-null returns and
>>>>>>> reference parameters from C functions.
>>>>>>  Walter is thinking of making only references non-null and leaving
>>>>>> pointers as they are. (I know, cry of horror.) But say pointers are
>>>>>> also non-null. Then:
>>>>>>  extern(C) MaybeNull!(void*) malloc(size_t s);
>>>>>>  will work, provided that MaybeNull has no size overhead and that
>>>>>> word-sized structs are returned in the same register as word returns
>>>>>> (I seem to remember Walter told me that's the case already).
>>>>> Here's a typical annoying Windows API function
>>>>> --------
>>>>> int GetTextCharsetInfo(
>>>>>    HDC hdc,                // handle to DC
>>>>>    LPFONTSIGNATURE lpSig,  // data buffer
>>>>>    DWORD dwFlags           // reserved; must be zero
>>>>> );
>>>>>
>>>>> lpSig
>>>>> [out] Pointer to a FONTSIGNATURE data structure that receives
>>>>> font-signature information. The lpSig parameter can be NULL if you do
>>>>> not need the FONTSIGNATURE information.
>>>>> ---------
>>>>> How do you do this?
>>>>>
>>>>> Don.
>>>> extern(System) int GetTextCharsetInfo(
>>>>     HDC hdc,
>>>>     MaybeNull!(FONTSIGNATURE*) lpSig, // or whatever
>>>>     DWORD dwFlags);
>>>>
>>>> GetTextCharsetInfo(hdc, null, flags); // fine
>>>> GetTextCharsetInfo(hdc, &sig, flags); // also ok
>>>>
>>> But it needs to have the type name mangled as LPFONTSIGNATURE, not as MaybeNull!(FONTSIGNATURE*). Otherwise it can't link to Windows.
>> 
>> Parameter names are not mangled for stacall, only their total size. IIRC, the above will get mangled into _GetTextCharsetInfo@12 just like it would without MaybeNull

Parameter names -> parameter types
stacall -> stdcall
Sorry

>> 
>Cool. So it'd only be an issue with extern(C++).
You're right, but the C++ interface is so limited that I don't think anybody uses it.
March 05, 2009
Jason House wrote:
> I'll gladly add assert(0) if my complex logic confuses the compiler.
> Actually, it pisses me off when the current dmd compiler complains
> about an assert(0) being unreachable.


I'd love it if an unreachable assert(0) were a special case.

At the start of programming, the code is in serious flux, and many times it would be really handy to have a few extra asserts around. Once the code crystallises you remove the most blatant asserts.

I do this in shell scripting, too. For example, in the original rdmd there still is a check that essentially says "if we get here, abort with a message 'logic failure, check code'."

Of course, there are people who simply have such a clear head that they essentially have the entire source file figured out before they start typing. I have some such friends. They would not need this feature. But there's some code even in Phobos that looks like it could use this.
March 05, 2009
Walter Bright escribió:
> Ary Borenszweig wrote:
>> Walter Bright escribió:
>>> Ary Borenszweig wrote:
>>>> Walter Bright escribió:
>>>>> Ary Borenszweig wrote:
>>>>>> It's not like that. They don't require you to initialize a variable in it's initializer, but just before you read it for the fist time. That's very different.
>>>>>
>>>>> The only way to do that 100% reliably is to instrument the running code.
>>>>
>>>> Java does it on compile time.
>>>
>>> Java is a severely constrained language. Even so, how does it do with this:
>>>
>>> Foo f;
>>> if (x < 1) f = new Foo(1);
>>> else if (x >= 1) f = new Foo(2);
>>> f.member();
>>
>> Whenever there are branches in code and a variable still doesn't have a value at that point:
>>  - if all branches assign a value to that variable, from now on the variable has a value
>>  - if not, at then end of the branches the variable still doesn't have a value
> 
> That rule gets the wrong answer in the above case. Consider that in order to get where you want to go with this, the flow analysis has to always work, not most of the time work. Otherwise you get bug reports with phrases like "seems to", "sometimes", "somehow", "I can't figure it out", "I can't reproduce the problem", etc.
> 
> Here's another lovely case:
> 
> Foo f;
> if (x < 1) f = new Foo();
> ... lots of code ...
> if (x < 1) f.member();
> 
> The code is quite correct and bug-free, but flow analysis will tell you that f in f.member() is "possibly uninitialized".

That's exactly the example of a code with a possible bug that'll get an null pointer access. Imagine in "... lots of code ..." you use f and the compiler complains. But you are sure "x < 1" will be true! Then you need to add an else and some assert(false) or throw an exception and that's it.

If the second condition is "if (x >= 1)" and in the next line you use f, then yes, you are definitely sure that "f" is initialized if you didn't touch "x" in "... lots of code ...". Well... not really, if some other thread changed "x" in the middle of the code, then you are not really sure about that. So again, it's an error.

If you are really, really sure that this won't happen, you do:

Foo f = null;

and that's it. You make the compiler help you only when you want it to, and that'll be most of the time.

The same goes with:

Foo f;
bar(&f);

If you are sure that bar will assign a value to f if it doesn't have one, go ahead and initialize "f" with null.
March 05, 2009
Walter Bright wrote:
> bearophile wrote:
>> Walter Bright:
>>> Joel C. Salomon:
>>>> To avoid this class of bug, you need a simple way to declare what
>>>> the acceptable values for a variable are.
>>> Languages have had this capability, but it never caught on. People
>>> found it just too tedious.
>>
>> What? We use ranges of integers in Delphi at work today :-) I have
>> even proposed something similar for D twice in the past. (But to be
>> precise, I often don't use ranged integral numbers for the purpose
>> discussed here).
> 
> I didn't mean nobody liked them or used them. I mean they have not caught on, despite having been around for 3 decades at least.

Integer ranges are closely related to contract programming.
March 05, 2009
Ary Borenszweig wrote:
> Walter Bright escribió:
>> Ary Borenszweig wrote:
>>> Walter Bright escribió:
>>>> Ary Borenszweig wrote:
>>>>> Walter Bright escribió:
>>>>>> Ary Borenszweig wrote:
>>>>>>> It's not like that. They don't require you to initialize a variable in it's initializer, but just before you read it for the fist time. That's very different.
>>>>>>
>>>>>> The only way to do that 100% reliably is to instrument the running code.
>>>>>
>>>>> Java does it on compile time.
>>>>
>>>> Java is a severely constrained language. Even so, how does it do with this:
>>>>
>>>> Foo f;
>>>> if (x < 1) f = new Foo(1);
>>>> else if (x >= 1) f = new Foo(2);
>>>> f.member();
>>>
>>> Whenever there are branches in code and a variable still doesn't have a value at that point:
>>>  - if all branches assign a value to that variable, from now on the variable has a value
>>>  - if not, at then end of the branches the variable still doesn't have a value
>>
>> That rule gets the wrong answer in the above case. Consider that in order to get where you want to go with this, the flow analysis has to always work, not most of the time work. Otherwise you get bug reports with phrases like "seems to", "sometimes", "somehow", "I can't figure it out", "I can't reproduce the problem", etc.
>>
>> Here's another lovely case:
>>
>> Foo f;
>> if (x < 1) f = new Foo();
>> ... lots of code ...
>> if (x < 1) f.member();
>>
>> The code is quite correct and bug-free, but flow analysis will tell you that f in f.member() is "possibly uninitialized".
> 
> That's exactly the example of a code with a possible bug that'll get an null pointer access. Imagine in "... lots of code ..." you use f and the compiler complains. But you are sure "x < 1" will be true! Then you need to add an else and some assert(false) or throw an exception and that's it.
> 
> If the second condition is "if (x >= 1)" and in the next line you use f, then yes, you are definitely sure that "f" is initialized if you didn't touch "x" in "... lots of code ...". Well... not really, if some other thread changed "x" in the middle of the code, then you are not really sure about that. So again, it's an error.
> 
> If you are really, really sure that this won't happen, you do:
> 
> Foo f = null;
> 
> and that's it. You make the compiler help you only when you want it to, and that'll be most of the time.
> 
> The same goes with:
> 
> Foo f;
> bar(&f);
> 
> If you are sure that bar will assign a value to f if it doesn't have one, go ahead and initialize "f" with null.

By the way, I'm defending this functionality because I received the "variable might not be initialized" errors many times now, and I know my coworkers also received them many times. In those cases, we were always making a mistake. I can't remember even one time when I had to put a dummy initializer in a variable just to make the compiler happy.
March 05, 2009
Georg Wrede wrote:
> Jason House wrote:
>> I'll gladly add assert(0) if my complex logic confuses the compiler.
>> Actually, it pisses me off when the current dmd compiler complains
>> about an assert(0) being unreachable.
> 
> I'd love it if an unreachable assert(0) were a special case.
> 
> At the start of programming, the code is in serious flux, and many times it would be really handy to have a few extra asserts around. Once the code crystallises you remove the most blatant asserts.

Don’t be so quick to remove them; when you refactor your code you leave yourself open to these same bugs biting you.

Remember “Can’t Happen or /* NOTREACHED */ or Real Programs Dump Core” by Ian Darwin & Geoff Collyer <http://www.literateprogramming.com/canthappen.pdf>.

—Joel Salomon