Frank Heckenbach wrote:
Waldek Hebisch wrote:
Russell Whitaker wrote:
The following compiles but no longer works:
program fill; var s: string(10); begin fillchar(s[1], s.capacity, ' '); writeln("ok"); end.
Do you get a message about out of range value? If yes then strictly speaking the compiler is right: s is uninitialized, so its length is set to 0, hence access to s[1] is illegal (out of range).
Yes.
In fact, I see another problem with the code above: s[1] is a single char, but fillchar acceses also s[2],... which strictly speaking is another out of bound access (but ATM compiler can not detect this problem).
Well, FillChar is low-level, working on memory directly.
Low-level does not exclude error checking. The following:
var c : char ... fillchar(c, 2, ' ');
is wrong. It make sense to flag such things, as long as there is a way to write the correct cases. More precisely, IMHO it is acceptable to recect some versions as long as there is an acceptable alternative.
I would say that fillchar is incompatible with range checking (at least now). ATM you can say:
setlength(s, 10); fillchar(s[1], s.capacity, ' ');
Yes, that's what I did in the few cases like that I had.
and I will work. However, I think that the correct way is: setlength(s, s.capacity); fillchar(s[1..length(s)], length(s), ' ');
Alternatively, you char turn range checking off.
In the future we may teach the compiler that fillchar is special, so it applies to it relaxed range checking rules, but that would require non-trivial changes in the compiler.
To some extend the compiler already knows this, that's why the first way works. I.e., it cares only about the address of the first argument (and thus only about its validity), not about the length of the memory block. Actually, that's nothing magic -- an untyped `var' parameter has the same effect. Since it's possible to write user-level routines using untyped parameters that work on memory blocks of any size, the compiler can't check them in general.
So I'd rather leave things as they are -- OTOH, making checking stricter (to the whole block) wouldn't be possible with user-level routines, OTOH, making it looser (WRT s[1]) would seem to cost quite some effort for little gain (as it's often easy to avoid the problem by reordering as here, or at worst, turn off range-checking locally).
In ideal world fillchar (and argument proccesing in the compiler) would ignore string length. Namely, when doing low-level string manipulation it is safer to fill data first and set length only _after_ the data is correct (gives at least partial protection against using partially filled string). OTOH in the ideal world fillchar would check that the block is big enough.
I am not sure if the effect would justify the effort, but in principle transfering:
filchar(x, l, c);
into something like
if sizeof(x) >= l then filchar_lowlevel(x, l, c) else RangeCheckError;
does not look very hard. Of course, for really low-level use we should add something like:
fillchar(Void(x), n, c);
to bypass the length check.