On 14/04/2019 19:53, Ryan Joseph wrote:

On Apr 14, 2019, at 1:38 PM, Sven Barth via fpc-pascal 
<fpc-pascal@lists.freepascal.org> wrote:

As already said by Martin: the compiler *can not* determine all cases whether 
the parameter is Nil or not, so it *must* be done at runtime to ensure this. 
Otherwise the feature is just as useful as this:
I’ve read over what Martin said and honestly I’m confused now. :) I’m not sure 
if I don’t understand you guys or you don’t understand me.

Lets use the example of the optional (i.e. "could be nil") return value because 
it’s most easy to understand. Why can’t the compiler know that the result of GetThing is 
an optional and therefore you *must* always check any code that dereferences it? Isn’t 
this a compile time issue?

In the example below I would always check for not nil if the documentation said 
it may be nil so why can’t the compiler just make you do that anyways and 
provide the information right there in the declaration? I don’t understand 
where the runtime element of this is.

function GetThing: TThing; optional;

var
   thing: TThing;
begin
   thing := GetThing;
   if assigned(thing) then
     writeln(thing.name);

Well the answer has several parts.

One thing is what checks can be done, to help the programmer not to accidentality  de-ref nil.
The other thing is how.

1) The "optional" is actually the default. If anything towards a deref-warn feature was done, then *every* parameter,  *every* return-value (and even every other var) of a nil-able type should be seen as optional. If a type is not optional, then it should be declared as "not nil-able" => function foo(notnil a: TObject): notnil TOBject;

That is in the same way as range checks. There is on "type x=array[1..50] with range checks of integer;"

Using your "Optional" is just specifying what already applies. In other words: redundant.

Now specifying it for a result, is different than specifying it for a parameter.
- For a parameter, the problem was:
   that if I want the compiler to check that I test for "assigned",
   => then I must remember that I want the compiler to do that,
   => because if I do not remember, then I would not add the "optional"
  But:
  If I already remember that I want to have a check for "assigned", then I can do it right away.   I do not need the "optional", so that I get a warning for forgetting something that I actually just though of.

As something applied to the result, the reminder is for some other person. The person that will write the code.
Now that idea is perfectly fine.
- But the "optional" still is not. The user, the compiler, everyone already knows that the result can be nil. Every TObject can be nil. This is the default. - So to improve this we would need a way to say: It will never be nil. (I.e., it is different from the default) If we had that, then there would be nothing wrong with the compiler giving a hint, in case the user does not check.

So that is about the how.
- Reverse the spec, so it acknowledges the current default.
- Then issue warnings for every parameter or return value, that has not been marked as non-nil.

2) If the compiler does check, we have to consider that all current code defaults to nil-able.
To avoid floods of warnings the check would need to be off by default.
It could be enabled by
- directive
- command line to the compiler
- fpc.cfg

3) If the compiler does check, it can issue (at compile time) hints, notes, or in very very few cases warnings. You can use -we to tread them as error, but they can never be error by default.

Code can be very complex. The compiler can never understand all possible ways of implementation. Therefore the compiler will always have false positives. If those were errors by default, then perfectly valid code could not be compiled.
So hints, notes, and occasional warnings.

4) runtime checks:
Are entirely independent of the above.

However they can be helpful. They would work exactly like range-check errors work today. If you declare a function can never return nil, then the compiler can (as a separate feature, with its own option) in addition to the hints/notes at compile time, add checks at runtime.

You do not write try except for range checks (at least that is not what range checks are meant for), and you would not do that for nil checks.

-------------------
In conclusion:

The nil-deref-protection would be very much like range checks. (except that the nature of tracing the nil value is more complex, meaning you get notes instead of errors)

range checks can occur at compile time SomeString[-1] should give a compile time error (in that case actually an error)
range checks also occur at runtime.

nil-deref-protection  does the same.
It applies to *ALL* nil-able types by default.

In order to be useful it is necessary that the programmer has a way to tell the complier that variable/param/returnval do not fall into the default, and that they are never nil.

Invalid contracts are an error that should trigger during development, but not 
necessarily when released. So coupling them with the default exception 
mechanism is a valid solution.
Could you give a custom message for the exception or would it just say “Function X 
failed conditions”? Usually we give more detailed messages so we know went wrong. 
Since this is the compiler maybe it could say which condition failed, like 
“condition x < 0 failed”.

See the oxygen page. Yes there are custom messages.
But they should be for the developper. Not for the end user.
Though of course nothing stops you from (ab-)using them for the enduser.
_______________________________________________
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal

Reply via email to