Recent

Author Topic: Unified Pascal syntax  (Read 4564 times)

PascalDragon

  • Hero Member
  • *****
  • Posts: 2132
  • Compiler Developer
Re: Unified Pascal syntax
« Reply #30 on: December 17, 2019, 09:50:27 am »
Thus it's also not possible to have one inherit from the other, because they present completely different concepts.
Do you mean on implementation/memory side or on syntax side?
The former. For starters an object might be located on the stack or the heap, calling constructors can be considered optional and they or may not have a pointer to a VMT depending on the inheritance and the presence of virtual methods. In contrast a class instance is always located on the heap, one must call a constructor to instantiate it and they always have a pointer to a VMT. Not to mention that the VMT of the later is much more vast than that of objects.
TL;DR: They are simply not compatible and they'll stay that way.

kupferstecher

  • Sr. Member
  • ****
  • Posts: 406
Re: Unified Pascal syntax
« Reply #31 on: December 17, 2019, 06:07:34 pm »
For starters an object might be located on the stack or the heap, [...]
In contrast a class instance is always located on the heap
Thats clear, but both variables, of type Object and Class carry the memory address of the data block. So regarding the memory access there should be no difference whether its allocated on the stack or heap or whereever else in the RAM.

Quote
[Object...] calling constructors can be considered optional [...]
[Class...] one must call a constructor to instantiate it [...]
Even with Class/Object compatibility this still could stay that way. If the constructor were called from an object type, then the Constructor would be called as plain procedure without reserving memory, as the static memory of an object is reserved already. I think that would be straight forward to use.

Quote
[Object...] and they or may not have a pointer to a VMT depending on the inheritance and the presence of virtual methods. [...]
[Class...] and they always have a pointer to a VMT. Not to mention that the VMT of the later is much more vast than that of objects. [...]
Ok, thats a real incompatibility now. There arises the question if backward compatibility on binary side is always given, or if internal structures could be changed time by time.

PascalDragon

  • Hero Member
  • *****
  • Posts: 2132
  • Compiler Developer
Re: Unified Pascal syntax
« Reply #32 on: December 18, 2019, 09:53:11 am »
For starters an object might be located on the stack or the heap, [...]
In contrast a class instance is always located on the heap
Thats clear, but both variables, of type Object and Class carry the memory address of the data block. So regarding the memory access there should be no difference whether its allocated on the stack or heap or whereever else in the RAM.
Yes, there is a difference. A class can be allocated using a class type and the class that is really instantiated could have a larger size. Let's assume that allocating a class on the stack would work. Then the following code would fail:
Code: Pascal  [Select][+][-]
  1. type
  2.   TMyClass = class
  3.     fMyField: LongInt;
  4.   end;
  5.   TMyClasses = class of TMyClass;
  6.  
  7.   TMySubClass = class(TMyClass)
  8.     fMySubField: LongInt;
  9.   end;
  10.  
  11. procedure Whatever;
  12. var
  13.   o: TMyClass; stack; // this will allocate TMyClass.InstanceSize of memory
  14.   c: TMyClasses;
  15. begin
  16.   c := TMySubClass;
  17.   o := c.Create; // this requires TMySubClass.InstanceSize
  18.   Writeln(TMySubClass(o).fMySubField); // <- BOOM!
  19. end;
  20.  

That's the same reason why you need to allocate object instances on the heap if you want to work with virtual inheritance and such. For class types this is an integral part of the concept and thus combining them essentially reduces classes to objects.

kupferstecher

  • Sr. Member
  • ****
  • Posts: 406
Re: Unified Pascal syntax
« Reply #33 on: December 18, 2019, 11:05:27 pm »
Firstly let me rewrite your code to my syntax in mind and comment inside. The interface part stays the same.
Code: Pascal  [Select][+][-]
  1. type
  2.   TMyClass = class
  3.     fMyField: LongInt;
  4.   end;
  5.   TMyClasses = class of TMyClass;
  6.  
  7.   TMySubClass = class(TMyClass)
  8.     fMySubField: LongInt;
  9.   end;
  10.  
  11. procedure Whatever;
  12. var
  13.   o: object(TMyClass);  //An instance of TMyClass is allocated on the stack.
  14.                         // Short form for:
  15.                         //  Type TMyObject = object(TMyClass);
  16.                         //  o: TMyObject;
  17.   c: TMyClasses;
  18. begin
  19.   c := TMySubClass;
  20.   o := c.Create;  // This is an unfortunate line of code, as 'o' is already
  21.                   // allocated on the stack and Create results into a second memory
  22.                   // allocation, because it's called by the class type 'c' and not by an
  23.                   // object type, which would just call the procedure Create without
  24.                   // memory allocation. The address of the class type is written
  25.                   // into 'o' which leads into a memory leak (until the procedure is left).
  26.   Writeln(TMySubClass(o).fMySubField); // That would still work, as 'o' references the
  27.                                        // instantiated class and not the original object.
  28. end;
  29.  
As you can see, the object/class mixture in that special case wouldn't result in useful code, but it would still be legit code. In the example the limitations of the object type are already on syntax level as it's a result of the memory allocation (time and place) and of the behaviour of the constructor-call. Thus the compiler wouldn't have to do special checks for which features are allowed or not for object or class type.
But perhaps I'm missing something...


That's the same reason why you need to allocate object instances on the heap if you want to work with virtual inheritance and such.
Can you give an example for that?

The following (hypothetical) code should work with fully static memory:

Code: Pascal  [Select][+][-]
  1. Type TMyObj = object
  2.   Procedure DoSomething; virtual;
  3. end;
  4.  
  5. Type TMySubObj = object(TMyObj);
  6.   Procedure DoSomething; override;
  7. end;
  8.  
  9. var
  10.   obj: TMyObj;
  11.   subObj: TMySubObj;
  12.   vObj: class(TMyObj); // Serves as pointer, doesn't allocate memory
  13. begin
  14.   vObj:= obj;
  15.   vObj.DoSomething; // Calls TMyObj.DoSomething
  16.   vObj:= subObj;
  17.   vObj.DoSomething; // Calls TMySubObj.DoSomething
  18. end;

Quote
For class types this is an integral part of the concept and thus combining them essentially reduces classes to objects.
The class reference feature "class of T..." wouldn't be applicable to objects (that means no "object of T.."), but classes still can have it. I still think objects and classes could behave quite orthogonal without the reduce of any features.

PascalDragon

  • Hero Member
  • *****
  • Posts: 2132
  • Compiler Developer
Re: Unified Pascal syntax
« Reply #34 on: December 19, 2019, 09:46:18 am »
Firstly let me rewrite your code to my syntax in mind and comment inside. The interface part stays the same.
Code: Pascal  [Select][+][-]
  1. type
  2.   TMyClass = class
  3.     fMyField: LongInt;
  4.   end;
  5.   TMyClasses = class of TMyClass;
  6.  
  7.   TMySubClass = class(TMyClass)
  8.     fMySubField: LongInt;
  9.   end;
  10.  
  11. procedure Whatever;
  12. var
  13.   o: object(TMyClass);  //An instance of TMyClass is allocated on the stack.
  14.                         // Short form for:
  15.                         //  Type TMyObject = object(TMyClass);
  16.                         //  o: TMyObject;
  17.   c: TMyClasses;
  18. begin
  19.   c := TMySubClass;
  20.   o := c.Create;  // This is an unfortunate line of code, as 'o' is already
  21.                   // allocated on the stack and Create results into a second memory
  22.                   // allocation, because it's called by the class type 'c' and not by an
  23.                   // object type, which would just call the procedure Create without
  24.                   // memory allocation. The address of the class type is written
  25.                   // into 'o' which leads into a memory leak (until the procedure is left).
  26.   Writeln(TMySubClass(o).fMySubField); // That would still work, as 'o' references the
  27.                                        // instantiated class and not the original object.
  28. end;
  29.  
As you can see, the object/class mixture in that special case wouldn't result in useful code, but it would still be legit code. In the example the limitations of the object type are already on syntax level as it's a result of the memory allocation (time and place) and of the behaviour of the constructor-call. Thus the compiler wouldn't have to do special checks for which features are allowed or not for object or class type.
But perhaps I'm missing something...
First you declare it as "an object", thus it's on the stack and then suddenly there's a heap allocation thanks to the constructor. How is a user to know what is going on now, especially when they're somewhere down in the code? No, that's not a good design.

That's the same reason why you need to allocate object instances on the heap if you want to work with virtual inheritance and such.
Can you give an example for that?

The following (hypothetical) code should work with fully static memory:
I'm not talking about hypothetical code, I'm talking about existing code:

Code: Pascal  [Select][+][-]
  1. uses
  2.   SysUtils;
  3.  
  4. type
  5.   TMyObj = object
  6.     Procedure DoSomething; virtual;
  7.   end;
  8.  
  9.   TMySubObj = object(TMyObj)
  10.     Procedure DoSomething; virtual;
  11.   end;
  12.  
  13. procedure TMyObj.DoSomething;
  14. begin
  15.   Writeln('Obj');
  16. end;
  17.  
  18. procedure TMySubObj.DoSomething;
  19. begin
  20.   Writeln('SubObj');
  21. end;
  22.  
  23. var
  24.   obj: TMyObj;
  25.   subObj: TMySubObj;
  26. begin
  27.   obj.DoSomething;
  28.   Obj:= subObj;
  29.   Obj.DoSomething;
  30. end.
  31.  

The compiler even warns here:
Code: [Select]
PS E:\fpc\git> fpc -FEtestoutput .\fpctests\tobjtest.pp
Free Pascal Compiler version 3.0.4 [2019/10/27] for i386
Copyright (c) 1993-2017 by Florian Klaempfl and others
Target OS: Win32 for i386
Compiling .\fpctests\tobjtest.pp
tobjtest.pp(10,4) Warning: Virtual methods are used without a constructor in "TMyObj"
tobjtest.pp(18,4) Warning: Virtual methods are used without a constructor in "TMySubObj"
tobjtest.pp(39,3) Warning: Variable "obj" does not seem to be initialized
tobjtest.pp(40,9) Warning: Variable "subObj" does not seem to be initialized
Linking testoutput\tobjtest.exe
42 lines compiled, 0.2 sec, 64192 bytes code, 4100 bytes data
4 warning(s) issued

And here's the BOOM:

Code: [Select]
PS E:\fpc\git> .\testoutput\tobjtest.exe
An unhandled exception occurred at $00401544:
EAccessViolation: Access violation
  $00401544

The whole discussion is moot anyway however as we are not going to change that objects and classes are different.

kupferstecher

  • Sr. Member
  • ****
  • Posts: 406
Re: Unified Pascal syntax
« Reply #35 on: December 19, 2019, 11:11:05 am »
First you declare it as "an object", thus it's on the stack and then suddenly there's a heap allocation thanks to the constructor. How is a user to know what is going on now, especially when they're somewhere down in the code? No, that's not a good design.
It just makes no sense to mix the object and class type within the one line of calling the constructor. The compiler could even cover that by prompting an error, but that are details.

Why they should be compatible:
1. Then its very easy to change between both types
2. Imho there should be a possibility for static allocation of a class
3. The class type could function as easy to use pointers for objects (avoids the cryptic use of real pointers)


I'm not talking about hypothetical code, I'm talking about existing code:
[...]
And here's the BOOM:
I see. But there's an issue:
The allocation of the overridden method is done in the construtor and that means one could define an object and call the Creator of a subclassed object, then the object carries the methods of the subclass. So far so good, but the variables added by the subobject are statically allocated beforehand. The subclassed method now could access memory which actually is not available.
So I wonder what is it good for that the inheritance is not defined by the compiler on compile time and instead waits for the Constructor to be called.

The whole discussion is moot anyway however as we are not going to change that objects and classes are different.
I know. Still I hope that I can give you inspirations when you think about that topics and also this helps me to further understand the language and the internals of the compiler. And finally its just interesting to think about it.

Shpend

  • Jr. Member
  • **
  • Posts: 93
Re: Unified Pascal syntax
« Reply #36 on: December 19, 2019, 04:51:59 pm »
As much as I can understand that building new stuff for a compiler (especially if its not driven financially and also not by a central Head aka projectmanager or the like) that its alot of hardwork to be done, to implement such a feature, to be absolutely honest, I only had one particular featurea from C++ in mind which is very powerful (hence they embedded it very early in C++) that you can have everything what a class can but also the option to put it on a stack or heap, but you  alwaays have both, nevermind which exact memory layout it has.

But well, you guys have to set ur priorities and the points you have mentioned earlier, @PascalDragon, are also very much needed for the FPC, so go ahead, I just want to throw the caviet out, allways, when I open up a thread like this, my only intention is to, so to speak, brainstorm with you guys, what can be done in theory if and when you implement it, is another question you have to answer for yourself!

 

TinyPortal © 2005-2018