Cause of Range Check Error (Delphi)

Jessica Brown picture Jessica Brown · Jul 25, 2012 · Viewed 27.5k times · Source

Here's a condensed version of some code that causes both a Range check error and an overflow error, should I turn on those compiler check directives. I understand why this would cause an overflow, on the multiplication of C1, it seems likely it might exceed the data-type's max valude. But why would this also trigger a Range-check error? Delphi's documentation and other posts on stack overflow make it sound like range-check errors are usually for array accesses that are out of bounds. But I'm not accessing an array on the line it's saying is causing the range-check error. Perhaps its on the assignment to param1? But why would that be a range-check and not an overflow error, if so?

const
  C1 = 44001;
  C2 = 17999;

function fxnName(..other params...; param1: Word): String;
var
  someByte: byte;
begin
  // some code
  // by now we're in a loop. the following line is where it breaks to in the debugger: 
  param1 := (someByte + param1) * C1 + C2;
  // more code
end;

If it's relevant, when it breaks on that line in the debugger, all the values look as expected, except param1, which shows "Undeclared identifier: 'param1'" when I ask Delphi to evaluate it.

Answer

LU RD picture LU RD · Jul 25, 2012

The documents about range-checking states:

The $R directive enables or disables the generation of range-checking code. In the {$R+} state, all array and string-indexing expressions are verified as being within the defined bounds, and all assignments to scalar and subrange variables are checked to be within range. If a range check fails, an ERangeError exception is raised (or the program is terminated if exception handling is not enabled).

So the reason here is the assignment to a scalar value, which is handed a value that has passed the upper range.

See also docwiki Simple Types about range-checking errors on simple types and subrange types.

Example:

{$R+} // Range check on
var
  w1,w2 : word;
begin
  w1 := High(word);
  w1 := w1 + 10; // causes range-check error on assignment to w1 (upper range passed)
  w2 := 0;
  w2 := w2 - 10; // causes range-check error on assignment to w2 (lower range passed)
end;

A summary test of all combinations of $R and $Q for all platform-independent integer types:

            R+Q+  R+Q-  R-Q+
 ShortInt    R     R     x
 SmallInt    R     R     x
 Integer     O     x     O
 LongInt     O     x     O
 Int64       O     x     O
 Byte        R     R     x
 Word        R     R     x
 LongWord    O     x     O
 Cardinal    O     x     O
 UInt64      O     x     O

R=Range error; O=Overflow error; x=Nothing

And the test was(pseudo-code) with XE2 in 32-bit mode:

number := High(TNumber);
number := number + 1;