I never would have expected that switching the range checks on would mask these kind of memory access errors.
Well....
Note: in the below I say "on the heap" => for locals that is "on the stack" => but the same rules apply...
var a: array of bytes;
begin
setlength(a,2); // allocates 2 bytes of mem on the heap => the heap itself is bigger though, depends on OS
a[50 * 1000 * 1000] := 1;
Now you write to memory 50 MB after your allocated area => most likely outside the total heap area. You get an access violation.
With range-checking, fpc compiles the above as
var a: array of bytes;
begin
setlength(a,2); // allocates 2 bytes of mem on the heap => the heap itself is bigger though, depends on OS
if 50 * 1000 * 1000 > length(a) then CallRangeCheckError;
a[50 * 1000 * 1000] := 1;
CallRangeCheckError is a runtime error or exception. It either aborts your program, or raises on exception which will go to the next "except end" (if there is).
In either case: The assignment is not executed => no access violation.
The range checking has nothing to do with the debugger. It is compiled into your exe. If you ship this, it will do range checking at your clients PC too. (there are valid reasons to do that).
@Martin_fr:
So, in the future, in case of an error, I will have to check the software with all range checks off, additionally. Can you probably find a way to let the other error message through with the range checks switched on, too? Those provide more info in those cases.
As above: the debugger does not create the range check error. It merely catches it. (internally it just sets a breakpoint an "raise" and at "run_error".).
And normally that works. I still have no idea why it does not work in your case.
You could still produce the log as I described (by putting the error back in your code). It may or may not help in identifying why it's not caught by the debugger.
Btw, take the example above:
var
i: integer;
a: array [0..1] Integer;
b: boolean;
begin
b := false;
i := 2;
a[i] := 1; // this changes "b" // Depend on compiler setting / optimization ...
So without range check => at some point later when you happen to do "if b then ..." => your app will behave unexpected. Because b is not what you wanted.
If "b" wasn't boolean, but "b: TObject" => then you changed the internal pointer of b (the reference to the object date). => "b" will not be nil, but "b" will point to a random address.
If that address is readable you get some strange values.
If that address is not readable you get the access violation when you access "b" (which may be long after the mistake had happen).