Recent

Author Topic: [SOLVED] FPC trunk: Existing code is broken about explicit class casting  (Read 2152 times)

abouchez

  • Full Member
  • ***
  • Posts: 111
    • Synopse
Sometimes, we have a class defined as such in an unit:
Code: Pascal  [Select][+][-]
  1. TSomeClass = class
  2. protected
  3.   procedure SomeProtectedMethod;
  4. end;
  5.  
  6. TChildClass = class(TSomeClass);
and in another unit, we want to run the SomeProtectedMethod.

What we could do for ever is to create a local "hook" class:
Code: Pascal  [Select][+][-]
  1. TSomeClassHook  = class(TSomeClass);
  2. ...
  3.   if obj is TSomeClass then
  4.      TSomeClassHook(obj).SomeProtectedMethod;

It worked well, because of the forced typecast.
But if obj is of type TChildClass, now FPC trunk complains at runtime.

So I suspect, the trunk compiler add a runtime check to ensure that TSomeClassHook(obj) is actually a TSomeClassHook - which is not the case of course.
In practice, now above code seems to be compiled as:
Code: Pascal  [Select][+][-]
  1. TSomeClassHook  = class(TSomeClass);
  2. ...
  3.   if obj is TSomeClass then
  4.      (obj as TSomeClassHook).SomeProtectedMethod;
which is breaking existing code.

What should I do?
« Last Edit: April 04, 2022, 08:19:11 am by abouchez »

abouchez

  • Full Member
  • ***
  • Posts: 111
    • Synopse
Re: FPC trunk: Existing code is broken about explicit class casting
« Reply #1 on: April 01, 2022, 10:54:55 am »

abouchez

  • Full Member
  • ***
  • Posts: 111
    • Synopse
Re: FPC trunk: Existing code is broken about explicit class casting
« Reply #2 on: April 01, 2022, 10:56:42 am »
I guess the following won't help:
Code: Pascal  [Select][+][-]
  1. TSomeClassHook  = class(TSomeClass);
  2. ...
  3.   if obj is TSomeClass then
  4.      TSomeClassHook(pointer(obj)).SomeProtectedMethod;

PascalDragon

  • Hero Member
  • *****
  • Posts: 5486
  • Compiler Developer
Re: FPC trunk: Existing code is broken about explicit class casting
« Reply #3 on: April 01, 2022, 03:48:38 pm »
Please provide a full, self contained example that demonstrates the issue. The following code works correctly in both FPC 3.2.2 and main and will only trigger the mentioned error when object checks (parameter -CR or directive $ObjectChecks) are enabled (as that is its purpose):

Code: Pascal  [Select][+][-]
  1. program thook;
  2.  
  3. {$mode objfpc}
  4.  
  5. uses
  6.   sysutils, uhook;
  7.  
  8. type
  9.   TSomeClassHook = class(TSomeClass);
  10.  
  11. var
  12.   obj: TObject;
  13. begin
  14.   obj := TChildClass.Create;
  15.   try
  16.     if obj is TSomeClass then
  17.       TSomeClassHook(obj).SomeProtectedMethod;
  18.   finally
  19.     obj.Free;
  20.   end;
  21. end.

Code: Pascal  [Select][+][-]
  1. unit uhook;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. type
  8.   TSomeClass = class
  9.   protected
  10.     procedure SomeProtectedMethod;
  11.   end;
  12.  
  13.   TChildClass = class(TSomeClass);
  14.  
  15. implementation
  16.  
  17. procedure TSomeClass.SomeProtectedMethod;
  18. begin
  19.   Writeln('I''m protected');
  20. end;
  21.  
  22. end.

abouchez

  • Full Member
  • ***
  • Posts: 111
    • Synopse
Re: FPC trunk: Existing code is broken about explicit class casting
« Reply #4 on: April 01, 2022, 06:40:08 pm »
It was in Delphi mode, in only a single computer... perhaps the user enabled object checks in the project options.
I didn't know about {$OBJECTCHECKS ON}.

I will force {$OBJECTCHECKS OFF} in all units of the framework, and wait for report.

dcoun

  • Newbie
  • Posts: 3
Re: FPC trunk: Existing code is broken about explicit class casting
« Reply #5 on: April 02, 2022, 08:42:49 am »
Reading the documentation (https://www.freepascal.org/docs-html/prog/progsu57.html):
Quote
1.2.57 $OBJECTCHECKS : Check Object

This boolean switch determines whether code to test the SELF pointer is inserted in methods. By default it is OFF. For example:
{$OBJECTCHECKS ON}

If the SELF pointer is NIL a run-time error 210 (range check) will be generated.

This switch is also activated by the -CR command line option.

To be honest, this feature is not expected when reading the documentation.

dcoun

  • Newbie
  • Posts: 3
Re: FPC trunk: Existing code is broken about explicit class casting
« Reply #6 on: April 02, 2022, 12:42:31 pm »
In https://www.freepascal.org/docs-html/user/usersu15.htm the -CR option is described:
Quote
-CR
    Generate checks when calling methods to verify if the virtual method table for that object is valid.
-CR is the same thing with {$OBJECTCHECKS ON} or subpart of {$OBJECTCHECKS ON} ?

PascalDragon

  • Hero Member
  • *****
  • Posts: 5486
  • Compiler Developer
Re: FPC trunk: Existing code is broken about explicit class casting
« Reply #7 on: April 02, 2022, 09:18:12 pm »
Reading the documentation (https://www.freepascal.org/docs-html/prog/progsu57.html):
Quote
1.2.57 $OBJECTCHECKS : Check Object

This boolean switch determines whether code to test the SELF pointer is inserted in methods. By default it is OFF. For example:
{$OBJECTCHECKS ON}

If the SELF pointer is NIL a run-time error 210 (range check) will be generated.

This switch is also activated by the -CR command line option.

To be honest, this feature is not expected when reading the documentation.

Please file a bug report against the documentation.

In https://www.freepascal.org/docs-html/user/usersu15.htm the -CR option is described:
Quote
-CR
    Generate checks when calling methods to verify if the virtual method table for that object is valid.
-CR is the same thing with {$OBJECTCHECKS ON} or subpart of {$OBJECTCHECKS ON} ?

-CR enables $OBJECTCHECKS as well as $RANGECHECKS.

dcoun

  • Newbie
  • Posts: 3
Re: FPC trunk: Existing code is broken about explicit class casting
« Reply #8 on: April 02, 2022, 09:42:46 pm »

abouchez

  • Full Member
  • ***
  • Posts: 111
    • Synopse
I confirm the -CR switch was enabled on this computer.
As a workaround, I am adding {$OBJECTCHECKS OFF} to every unit of mORMot.

The documentation is indeed a bit unprecise about this function.

af0815

  • Hero Member
  • *****
  • Posts: 1291
BTW: -Cr (Range) and -CR (Verify method calls) are at debugtime normal enabled.
 

Edit: Is $OBJECTCHECKS unitlocal, global or like $R can be procedure or expressionlevel ?!
« Last Edit: April 04, 2022, 08:48:09 am by af0815 »
regards
Andreas

PascalDragon

  • Hero Member
  • *****
  • Posts: 5486
  • Compiler Developer
Thanks a lot. The issue is created: https://gitlab.com/freepascal.org/fpc/source/-/issues/39644

Thank you.

Edit: Is $OBJECTCHECKS unitlocal, global or like $R can be procedure or expressionlevel ?!

Directives are either global (for the unit) or local (for the expression). Compiler parameters can be global for the whole compilation, but not if a unit isn't recompiled.

And $OBJECTCHECKS is local.

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1314
    • Lebeau Software
A user of Indy just ran into this very same issue in the wild a few days ago:

https://forum.lazarus.freepascal.org/index.php/topic,63996.msg485641.html#msg485641

Which I then reported here (as I didn't find this discussion yet):

https://forum.lazarus.freepascal.org/index.php/topic,64034.0.html

Using {$OBJECTCHECKS OFF} worked to solve the issue.

Although, I decided not to apply it to entire units as a whole (as the project that was using Indy needed the checks to be on), so instead I wrapped affected call sites with {$PUSH}/{$POP}, effectively like this:

Code: [Select]
{$PUSH}
{$OBJECTCHECKS OFF}
TIdSomeClassAccess(SomeObj).DoSomethingWithProtectedMember;
{$POP}
« Last Edit: July 22, 2023, 11:33:06 pm by Remy Lebeau »
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

 

TinyPortal © 2005-2018