Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2018-01

Range checks

13 messages · 6 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

C Western

The following innocuous looking code generates a range check error:

{$R+}
function Count: qword;
begin
   Result := 0;
end;
var
   i: Integer;
begin
   for i := 0 to Count-1 do
     WriteLn(i);
end.

I can (more or less) see why, but it means that I can't (for example) compile the Cocoa widget set in 64 bit with bounds checking on, as then qword seems to be used as a count for, for example, NSarray.

Am I missing something?

Colin

Adriaan van Os
to C Western
C Western wrote
The following innocuous looking code generates a range check error:

The code shouldn't generate a range check error, as the target type is signed.

Note that Oberon was designed by Niklaus Wirth specifically to prevent this kind of problems. See the notes on type inclusion in section A.4.4 of An introduction to Oberon-2 <http://www.cs.bath.ac.uk/~claire/Oberon.pdf>

Apple has never heard of type inclusion or even range checking and is arbitrarily mixing signed and unsigned types.

Regards,

Adriaan van Os

Dmitry Boyarintsev
to Adriaan van Os
Adriaan van Os wrote 5 lines
The code shouldn't generate a range check error, as the target type is signed.

Count returns Unsigned Qword.
unsigned $FFFFFFFFFFFFFFFF is beyond 32-bit boundary.

The similar issue, but more explicit.

{$R+}
function Count: qword;
begin
  Result := 0;
end;
var
  i: Integer;
  K : Qword;
begin
  K:=count-1;
  writeln(K);
  I:=k;  // <-- rangecheck error
  writeln(i);
end.

thanks,
Dmitry

C Western
to Adriaan van Os: The code shouldn't generate a range check error, as the target type is signed. Note that…
Adriaan van Os wrote 6 lines
C Western wrote: > The following innocuous looking code generates a range check error: The code shouldn't generate a range check error, as the target type is signed.

Note that shifting the loop variable to Int64 still triggers the range check error. Using qword for the loop variable triggers an overflow error.

Apple has never heard of type inclusion or even range checking and is arbitrarily mixing signed and unsigned types.

I don't think this is an Apple issue - an unsigned type is not unreasonable for a count.

Colin

Sven Barth
to C Western: The following innocuous looking code generates a range check error: {$R+} function Count:…
C Western wrote 19 lines
The following innocuous looking code generates a range check error: {$R+} function Count: qword; begin   Result := 0; end; var   i: Integer; begin   for i := 0 to Count-1 do     WriteLn(i); end. I can (more or less) see why, but it means that I can't (for example) compile the Cocoa widget set in 64 bit with bounds checking on, as then qword seems to be used as a count for, for example, NSarray. Am I missing something?

Integer is 32-bit, QWord is 64-bit. *Of course* there is a range check error then. You need to use e.g. SizeInt as counter variable type in those cases (SizeInt is 32-bit on 32-bit systems and 64-bit on 64-bit systems).

Regards,
Sven

Dmitry Boyarintsev
to C Western: The following innocuous looking code generates a range check error: {$R+} function Count:…
C Western wrote 6 lines
I can (more or less) see why, but it means that I can't (for example) compile the Cocoa widget set in 64 bit with bounds checking on, as then qword seems to be used as a count for, for example, NSarray. how about taking objc approach:
 RangeCheckCount = objccategory(NSArray)
    function intcount: Integer; message 'intcount';
  end;

{ RangeCheckCount }

function RangeCheckCount.intcount: Integer;
begin
  Result:=Integer(count);
end;
var
  arr : NSArray;
  for i:=0 to arr.intcount-1 do

?

Jonas Maebe
to C Western: The following innocuous looking code generates a range check error: {$R+} function Count:…
C Western wrote 15 lines
The following innocuous looking code generates a range check error: {$R+} function Count: qword; begin Result := 0; end; var i: Integer; begin for i := 0 to Count-1 do WriteLn(i); end. I can (more or less) see why

I changed the type used to evaluate for-loop bounds in ISO Pascal mode because of https://bugs.freepascal.org/view.php?id=24318 . Maybe the same should be done for other non-TP/Delphi modes too.

Jonas

Jonas Maebe
Jonas Maebe wrote 19 lines
C Western wrote: > The following innocuous looking code generates a range check error: > > {$R+} > function Count: qword; > begin > Result := 0; > end; > var > i: Integer; > begin > for i := 0 to Count-1 do > WriteLn(i); > end. > > I can (more or less) see why I changed the type used to evaluate for-loop bounds in ISO Pascal mode because of https://bugs.freepascal.org/view.php?id=24318 . Maybe the same should be done for other non-TP/Delphi modes too.

Actually, it won't help because "qword - 1" will still be evaluated as qword. The issue is that there is no safe way to evaluate this with range checking that is correct in all cases without a 128 bit integer type:
1) if you evaluate it as qword, you get a range error if count is 0 (as above)
2) if you evaluate it as int64, then if count is high(int64)+1 you will get a range error even though the result could be represented in int64

In this particular case, because the counter is 32 bit (which seems quite unsafe for use with a container whose "count" property is 64 bit), using int64 would be "correct", but it's not a systemic solution (as shown above) and hence not something to implement in the language.

Jonas

Jonas

C Western
to Jonas Maebe
Jonas Maebe wrote 35 lines
Jonas Maebe wrote: > C Western wrote: >> The following innocuous looking code generates a range check error: >> >> {$R+} >> function Count: qword; >> begin >> Result := 0; >> end; >> var >> i: Integer; >> begin >> for i := 0 to Count-1 do >> WriteLn(i); >> end. >> >> I can (more or less) see why > I changed the type used to evaluate for-loop bounds in ISO Pascal mode > because of https://bugs.freepascal.org/view.php?id=24318 . Maybe the > same should be done for other non-TP/Delphi modes too. Actually, it won't help because "qword - 1" will still be evaluated as qword. The issue is that there is no safe way to evaluate this with range checking that is correct in all cases without a 128 bit integer type: 1) if you evaluate it as qword, you get a range error if count is 0 (as above) 2) if you evaluate it as int64, then if count is high(int64)+1 you will get a range error even though the result could be represented in int64 In this particular case, because the counter is 32 bit (which seems quite unsafe for use with a container whose "count" property is 64 bit), using int64 would be "correct", but it's not a systemic solution (as shown above) and hence not something to implement in the language.

The actual code that triggered my interest was:

procedure LCLViewExtension.lclSetEnabled(AEnabled: Boolean);
var
   ns : NSArray;
   i  : integer;
   obj : NSObject;
begin
   ns := subviews;
   for i := 0 to ns.count - 1 do
   begin
     obj := NSObject(ns.objectAtIndex( i ));
     ... process obj ...
   end;
end;

I suppose i should be declared as the same type as ns.count which is NSUInteger = qword, but this will still trigger the error. You can avoid the problem by coding the loop as:

   for i := 1 to ns.count do
   begin
     obj := NSObject(ns.objectAtIndex( i-1 ));

so I suppose the real flaw is that the first item is at index 0, rather than 1, but I don't think that is a battle I am going to win.

Given how common the above bit of code is likely to be, I can't see any way of fixing this without some work in the compiler.

Colin

Dmitry Boyarintsev
to C Western
C Western wrote 20 lines
The actual code that triggered my interest was: procedure LCLViewExtension.lclSetEnabled(AEnabled: Boolean); var ns : NSArray; i : integer; obj : NSObject; begin ns := subviews; for i := 0 to ns.count - 1 do begin obj := NSObject(ns.objectAtIndex( i )); ... process obj ... end; end; Given how common the above bit of code is likely to be, I can't see any way of fixing this without some work in the compiler.

imho, bad approach to adjust compiler for not safe code. However, getting a compiler warning would be nice.

Some time ago, the compiler has been extended with for-in syntax:

var
  SubView : NSView;
begin
  for SubView in ns.subviews do
  begin
    .. process obj ...
  end

thanks,
Dmitry

Dmitry Boyarintsev
to Dmitry Boyarintsev

On Sat, Jan 27, 2018 at 12:51 PM, Dmitry Boyarintsev <

Show quoted text 13 lines
wrote: imho, bad approach to adjust compiler for not safe code. However, getting a compiler warning would be nice. Some time ago, the compiler has been extended with for-in syntax: var SubView : NSView; begin for SubView in ns.subviews do

That requires objectivec2 though
https://www.freepascal.org/docs-html/ref/refse76.html#x138-16000011.10 (min version is macOS 10.5)

thanks,
Dmitry

Santiago A.
to C Western: The following innocuous looking code generates a range check error: {$R+} function Count:…
C Western wrote 19 lines
The following innocuous looking code generates a range check error: {$R+} function Count: qword; begin   Result := 0; end; var   i: Integer; begin   for i := 0 to Count-1 do     WriteLn(i); end. I can (more or less) see why, but it means that I can't (for example) compile the Cocoa widget set in 64 bit with bounds checking on, as then qword seems to be used as a count for, for example, NSarray. Am I missing something?

The problem is that count is qword, so the operation is qword until it needs to convert it

for i:=0 to integer(qword( qword(count) -  qword(1)) do

try this:

for i := 0 to *integer*(Count)-1 do
-------- Saludos Santiago A.
Adriaan van Os
to Jonas Maebe: Actually, it won't help because "qword - 1" will still be evaluated as qword. The issue…
Jonas Maebe wrote 7 lines
Actually, it won't help because "qword - 1" will still be evaluated as qword. The issue is that there is no safe way to evaluate this with range checking that is correct in all cases without a 128 bit integer type: 1) if you evaluate it as qword, you get a range error if count is 0 (as above) 2) if you evaluate it as int64, then if count is high(int64)+1 you will get a range error even though the result could be represented in int64

Without type inclusion, that is in a language that allows mixing typed an untyped numbers, there is no anambiguous solution, as Jonas points out. So, I suggest to choose the next best solution, which is to let result type of the expression decide whether to apply 1) or 2). In this case, it would be
2) as the result type is signed.

Regards,

Adriaan van Os