Range checks

classic Classic list List threaded Threaded
13 messages Options
Reply | Threaded
Open this post in threaded view
|

Range checks

C Western-2
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
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Range checks

Adriaan van Os-2
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
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Range checks

dmitry boyarintsev
On Sat, Jan 27, 2018 at 10:06 AM, Adriaan van Os <[hidden email]> wrote:

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

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Range checks

C Western-2
In reply to this post by Adriaan van Os-2
On 27/01/18 15:06, Adriaan van Os wrote:
> 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
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Range checks

Free Pascal - General mailing list
In reply to this post by C Western-2
On 27.01.2018 15:10, 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, 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

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Range checks

dmitry boyarintsev
In reply to this post by C Western-2
On Sat, Jan 27, 2018 at 9:10 AM, C Western <[hidden email]> wrote:

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

?


_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Range checks

Jonas Maebe-3
In reply to this post by C Western-2

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.


Jonas
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Range checks

Jonas Maebe-3

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.


Jonas


Jonas
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Range checks

C Western-2
On 27/01/18 16:28, Jonas Maebe wrote:

>
> 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


_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Range checks

dmitry boyarintsev
On Sat, Jan 27, 2018 at 12:30 PM, C Western <[hidden email]> wrote:
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

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Range checks

dmitry boyarintsev
On Sat, Jan 27, 2018 at 12:51 PM, Dmitry Boyarintsev <[hidden email]> 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
(min version is macOS 10.5)

thanks,
Dmitry

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Range checks

Santiago A.
In reply to this post by C Western-2
El 27/01/18 a las 15:10, C Western escribió:
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.

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Range checks

Adriaan van Os-2
In reply to this post by Jonas Maebe-3
Jonas Maebe wrote:

> 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

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal