[fpc-pascal] Optional param modifier

Martin Frb lazarus at mfriebe.de
Sun Apr 14 21:38:46 CEST 2019


On 14/04/2019 19:53, Ryan Joseph wrote:
>
>> On Apr 14, 2019, at 1:38 PM, Sven Barth via fpc-pascal <fpc-pascal at 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.



More information about the fpc-pascal mailing list