[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