Feature request: hard type creation/declaration lazarus / 64-FPC development ---------------------------------------------------------------------- 440bx · 2025-07-07 09:16 Hello, One of the possible problems when programming is passing a handle to the wrong thing to a function/procedure/method and the compiler is unable to catch this error because, as of now, most handles are assignment compatible with one another. For instance, in Windows, an HWND can be assigned to an HMENU which can be assigned to an HPALETTE which can be assigned to an HBITMAP and so on.   This is because these types are not real types independent of each other but, synonyms of their underlying type (which is usually the same for most, if not all, handles.) The request is simple: provide a way to declare types that even though may share the same underlying type are NOT assignment compatible with one another.  IOW, provide a simple and effective way to ensure an HWND cannot be assigned to an HBITMAP or anything else that is not an HWND (however, still allow typecasting.) This would help prevent bugs and would set FPC apart.  It is a very, very simple feature but, it would really be a very nice feature to have. I believe I have requested this feature in the past, I also believe I am not the only one to request it (using other words but, same concept.)  I believe the feature is important enough that it should be requested again. Maybe if enough people support the addition of this feature, it might eventually make it into the compiler (of course, the sooner, the better.) Comments welcome. ETA: Added poll ---------------------------------------------------------------------- marcov · 2025-07-07 09:24 The syntax exist in pascal, but the Borland derived dialects lack in enforcing it. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Type    xx = type yyy;  IIRC the description is something like "create a separate entry in the type symbol table for xx", in other words, "don't alias". I'm no expert of the typesystem, but I assume it won't be a trivial {$modeswitch }, it will be harder to reconcile this with other typing related enhancements like the pointer type ( that is assignable from every pointer type), and ordinals, and overloads. Personally I think this is a better avenue for a language improvement trajectory than the C operators and similar features that are mostly (shorthand) syntax only. ---------------------------------------------------------------------- 440bx · 2025-07-07 09:45 Quote from: marcov on July 07, 2025, 11:24:56 amCode: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Type    xx = type yyy;  IIRC the description is something like "create a separate entry in the type symbol table for xx", in other words, "don't alias". I know what you mean.  The "type.... type" is _supposed_ to create a new type but, it doesn't. As far as implementation, what comes to mind is for the compiler to consider the type as if it were defined as a record (internally it could be a record.)  That would automatically make it assignment incompatible with other types defined that way.  There would be one additional thing needed, those "records" should allow a short hand form of typecasting that would not be available with normal records, this because it would be a "field-less" record.  That would allow assigning values of the underlying type, e.g, NEWTYPE = NEWTYPE(0);  which is something the compiler already does when assigning ordinal values to enumeration types. Long story long already, I believe it might not take a lot of work to implement the feature because it could be mostly implemented using characteristics that are already part of the language and, the additional stuff seems to be isolated to when the type is declared. Of course, implementation is something secondary.  First, there has to be agreement to adding the feature into the compiler. ---------------------------------------------------------------------- Thaddy · 2025-07-07 10:07 The type system is mainly to allow overloads. An example:Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$mode objfpc}{$H+} type  MyString = type AnsiString;    procedure printme(const s:MyString);overload;  begin    write('Type system uses MyString: ');    writeln(s);  end;   procedure printme(const s:AnsiString);overload;  begin    write('Type system uses AnsiString: ');    writeln(s);  end; var  a:AnsiString = 'AnsiString';  b:MyString = 'MyString';begin  printme(a);  printme(b);end.I believe this is documented somewhere. Basically it does create a new type, but the types remain otherwise assignment compatible. ---------------------------------------------------------------------- MarkMLl · 2025-07-07 10:14 Quote from: 440bx on July 07, 2025, 11:45:28 amQuote from: marcov on July 07, 2025, 11:24:56 amIIRC the description is something like "create a separate entry in the type symbol table for xx", in other words, "don't alias". I know what you mean.  The "type.... type" is _supposed_ to create a new type but, it doesn't. Xref to older thread https://forum.lazarus.freepascal.org/index.php/topic,70802.0.html The upshot of that was that it should be possible to declare a hard type using a few dozen lines of generics, but this is at present only implemented in main. I suggest that this be elevated to a request to get this into the compiler no later than 3.4 MarkMLl ---------------------------------------------------------------------- gidesa · 2025-07-07 10:28 Hello,  the topic was discussed in slightly different mode in https://forum.lazarus.freepascal.org/index.php/topic,65981. As of today, there is some usefulness of that syntax, although in very limited cases, described in FPC manual 3.8 paragraph. ---------------------------------------------------------------------- Thaddy · 2025-07-07 10:49 Another example where the types are distinct:Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$mode objfpc} {$H+}{$modeswitch typehelpers}type  Metres = type double;  Kilometres = type double;type    TMetreHelper = type Helper for Metres  function ToKilometres:Kilometres;  end;    TKiloMetreHelper = type helper for Kilometres  function ToMetres:Metres;  end;   function TMetreHelper.ToKilometres:Kilometres;  begin    result := self / 1000;  end;    function TKilometreHelper.ToMetres:Metres;  begin    result := self * 1000;  end; var  m1: Metres = 500.0;  km1: Kilometres = 5.0; begin  writeln( km1 + m1.ToKilometres:4:2);  writeln( km1.ToMetres + m1:4:2);  readln;end.The type helpers are strictly bound to the types: the type helper for metres is not accessible for kilometres and vice versa. You don't need a record for that. ---------------------------------------------------------------------- MarkMLl · 2025-07-07 10:55 Quote from: Thaddy on July 07, 2025, 12:49:45 pmAnother example where the types are distinct: ... The type helpers are strictly bound to the types. But when various people investigated this a few weeks ago it was found that underlying arithmetic wasn't, so even if helpers and overloads can be kept distinct there is a massive underlying trap for the unwary... and even for the wary. MarkMLl ---------------------------------------------------------------------- 440bx · 2025-07-07 11:02 Quote from: Thaddy on July 07, 2025, 12:07:13 pmBasically it does create a new type, but the types remain otherwise assignment compatible. if it were a new type it wouldn't be assignment compatible, e.g, all records are new types and they are definitely not assignment compatible. Currently there are convoluted ways to sort of implement new types but, they cause a lot of hassles.  They simply are not what a new type should be. As I stated in the OP, I am aware that this is not the first time this feature has been suggested'/requested/discussed but, this is really a basic feature that should be implemented sooner rather than later (actually, it should already be implemented.) ---------------------------------------------------------------------- Martin_fr · 2025-07-07 12:01 Quote from: 440bx on July 07, 2025, 01:02:21 pmQuote from: Thaddy on July 07, 2025, 12:07:13 pmBasically it does create a new type, but the types remain otherwise assignment compatible. if it were a new type it wouldn't be assignment compatible, e.g, all records are new types and they are definitely not assignment compatible. Exactly, so Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  HWND: record val: THandle; end;  HFont: record val: THandle; end; Will do. Of course, they are not assignment compatible, so any existing function declaration needs to be changed to use those types. You can define operators for assignments you want to allow. ---------------------------------------------------------------------- 440bx · 2025-07-07 12:49 That's probably the first thing anyone who has longed for hard types tries.  I tried to make it work to no avail, time wasted. Using records to create hard types, does create hard types and in addition to that, a lot of hard headaches.  For instance, just try to assign a value to the type, e.g: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  type  TNEW = record v : ptruint; end; var  SomeHandle : TNEW = TNEW(0); You can't typecast to the type.  To assign a value it is necessary to refer to the record field by name, that alone makes this "solution" completely inadequate as it depends on the name of a field. That's just one of the _many_ problems you encounter when trying to use a hard type defined that way.  Simply stated, it does not work and the amount of hassle it creates isn't worth it. ---------------------------------------------------------------------- Warfley · 2025-07-07 13:56 Quote from: 440bx on July 07, 2025, 11:45:28 amI know what you mean.  The "type.... type" is _supposed_ to create a new type but, it doesn't. Well it does so, just not for base types: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TMyRec = record i: Integer end;   TMyType = type TMyRec;  TOtherType = type TMyRec; var  v1: TMyType;  v2: TOtherType;begin  v2:=v1; // Errorend. All base types are compatible with each other, which kindof makes sense because of ranges: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TMyByte = 0..255;  TMyWord = 0..65535;Those are also two different types, but they are compatible with each other. Basically any ordinal type is compatible with any other ordinal type as long as the ranges are comptible. So when you write: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i TMyType = type Byte;It is exactly the same as if you write: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i TMyType = 0..255;And therefore is also compatible with other number types. Basically in Pascal any number type is defined as a range, and any copy of such a type is also just a new definition using the same range. And range types have to be compatible with each other ---------------------------------------------------------------------- Thaddy · 2025-07-07 14:12 That is basically what I wrote, better formulated. ---------------------------------------------------------------------- Martin_fr · 2025-07-07 14:45 Quote from: 440bx on July 07, 2025, 02:49:17 pmThat's probably the first thing anyone who has longed for hard types tries.  I tried to make it work to no avail, time wasted. Using records to create hard types, does create hard types and in addition to that, a lot of hard headaches.  For instance, just try to assign a value to the type, e.g: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  type  TNEW = record v : ptruint; end; var  SomeHandle : TNEW = TNEW(0); You can't typecast to the type.  To assign a value it is necessary to refer to the record field by name, that alone makes this "solution" completely inadequate as it depends on the name of a field. That's just one of the _many_ problems you encounter when trying to use a hard type defined that way.  Simply stated, it does not work and the amount of hassle it creates isn't worth it. You can, but its a bit more setup. All the commented lines fail (currently testing with 331 / 323). So: - TFoo and TNew are incompatible. - can be typecasted - assignments of numbers can be done -  "f:= TFoo(1);" also works / but I am not sure that it is a stable feature, it allows that even if TFoo has several fields...)   --- EDIT: that works, because it calls the overloaded operator first, then casts the TFoo result of the operator to TFoo (which it already is) Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program Project1;{$mode objfpc}  type  TNEW = record v : ptruint; end;  TFoo = record v : ptruint; end; operator := (a: integer): TNew;begin  result.v := a;end; operator := (a: integer): TFoo;begin  result.v := a;end; operator := (a: TNew): integer;begin  result := a.v;end; operator := (a: TFoo): integer;begin  result := a.v;end; procedure p(z: TNew); begin {} end; var  n: TNew;  f: TFoo;begin  n:= 1;  p(n);  f:= TFoo(n);  //p(v);  //f:= n;  //n:=f; end. ---------------------------------------------------------------------- Martin_fr · 2025-07-07 14:49 Mind you, there may be a draw back for the optimizer. Arguments to functions may not be passed in registers (not sure, maybe that is older fpc only). Then again, simply don't use the record types in release builds. If you checked a debug/test build, and it compiled, then your types are ok. That doesn't change if your release doesn't use the records. Mind: - I did (try to) offer a solution that exists. - I did not comment on the question of adding a dedicated language feature for this (that may or may not happen in FPC 4 or 5 or 6...). ---------------------------------------------------------------------- Martin_fr · 2025-07-07 14:53 Quote from: Martin_fr on July 07, 2025, 04:49:53 pmThen again, simply don't use the record types in release builds. If you checked a debug/test build, and it compiled, then your types are ok. That doesn't change if your release doesn't use the records. And if you do (should work too) Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TNEW = record v : ptruint; end;  TFoo = type TNew;  then its only one line to be changed. All others will change automatically. ---------------------------------------------------------------------- 440bx · 2025-07-07 15:00 It is completely irrelevant that base types are assignment compatible among each other. What's needed is a way to tell the compiler that a type definition is NOT compatible with other types that use the same base type, so that an HWND cannot be assigned to an HPALETTE and so on. It must not take tens of lines of various compiler construct to accomplish something so simple. The importance of this feature is that it prevents errors without the programmer having to bend over backwards and stand on one leg to create code that MAY prevent that error while opening the door to many more because of the complexity associated with implementing this fairly simple feature (at the compiler level.) Using records doesn't work.  It's like offering a tricycle as a replacement for seat belts.  Granted, it's very unlikely that someone on a tricycle is going to reach dangerous speeds but, way too much is lost in that "implementation". ---------------------------------------------------------------------- Martin_fr · 2025-07-07 15:09 Quote from: 440bx on July 07, 2025, 05:00:53 pmWhat's needed is a way to tell the compiler that a type definition is NOT compatible with other types that use the same base type, so that an HWND cannot be assigned to an HPALETTE and so on. The below is not meant as a bias no the request (I don't mind if or if not...) assuming a good definition can solve this. But technically, it might pose an issue. If you make   HWND incompatible, then it would also be incompatible with ptrUInt. It would have to be, otherwise if HWND and HPalette are both compatible to PtrUint, then they are compatible to each other. So then what happens to   if   MyHwnd <> 0  then ? That would stop working too. (and what if zero is a int variable, because it may be 0 or -1 ?  / and that var should compare to HWND and HPALETTE ?) You need a whole new set of rules when types are sometimes compatible. Well, yes the operator overloads do that, because fpc only inserts a single operator at max. So it can be done technically. Not sure what other quirks may need to be addressed. ---- I even have code of my own, where I would like the compiler to do such verifications. But since I haven't got a good answer to the potentially needed rule set... I don't currently ask for it. (Again, don't mind if/that you do / then I don't need to answer those things) ---------------------------------------------------------------------- 440bx · 2025-07-07 15:17 Quote from: Martin_fr on July 07, 2025, 05:09:53 pmIf you make   HWND incompatible, then it would also be incompatible with ptrUInt. It would have to be, otherwise if HWND and HPalette are both compatible to PtrUint, then they are compatible to each other. So then what happens to   if   MyHwnd <> 0  then ? That would stop working too. (and what if zero is a int variable, because it may be 0 or -1 ?  / and that var should compare to HWND and HPALETTE ?) You got it right.  an HWND should be incompatible with everything except another HWND.  In the example, "if MyHwnd <> 0 then..." the compiler should summarily reject that.  The correct statement is: "if MyHwnd <> HWND(0) then..." and that is presuming the underlying type of HWND is an integer type (as it would be in this case.)  The compiler already has a _limited_ implementation of that concept when using enumerated types.  if you've got some variable of type TMYENUM, to assign an integer to that variable requires typecasting the integer, i.e, TMYENUM(42) or whatever other value is the one of interest. ---------------------------------------------------------------------- Martin_fr · 2025-07-07 15:23 Actually, have you tried to make HWND an enum? with {$MINENUMSIZE xxx}  ? Again, not trying to stop your request. Just, trying to see if there is some immediately usable way. And yes, any immedicable useable way will obviously a work around. If its good depends on how bad you want/need it. ---------------------------------------------------------------------- 440bx · 2025-07-07 15:31 Quote from: Martin_fr on July 07, 2025, 05:23:01 pmActually, have you tried to make HWND an enum? with {$MINENUMSIZE xxx}  ? Again, not trying to stop your request. Just, trying to see if there is some immediately usable way. And yes, any immedicable useable way will obviously a work around. If its good depends on how bad you want/need it. I tried all kinds of things, enums among them.  IIRC, with enums the problem is the enumeration range which is obviously limited to whatever is declared, that causes an interminable stream of warnings about values being out of range. Currently, the problem has no clean solution.  There are ways to partially implement the feature but, every implementation using current facilities extorts a high price that makes it too cumbersome to use. Implementing this feature in the compiler is fairly simple.  It only requires fairly minor changes in the scanner and parser.  None in later phases. ---------------------------------------------------------------------- Martin_fr · 2025-07-07 15:33 Quote from: 440bx on July 07, 2025, 05:17:23 pmYou got it right.  an HWND should be incompatible with everything except another HWND.  In the example, "if MyHwnd <> 0 then..." the compiler should summarily reject that.  The correct statement is: "if MyHwnd <> HWND(0) then..." and that is presuming the underlying type of HWND is an integer type (as it would be in this case.)  Ok, in that case, for your case there could be simple rules. Then unfortunately for me, I would still not have a solution. My case requires that I can do math with the stored numbers. Basically I have numbers that act as position or index. Historically (over many hundred thousands of lines) they are some 0-based some 1-based. So, I don't want direct assignment (because then it points to the wrong item). But I do need   val := val + offs; or even   val_0 := val_1 - 1; // ok that should go through  a converter function Which ultimately means, you actually don't want me to join your feature request, because my extra needs make it more complicated, and less likely to go anywhere. ---------------------------------------------------------------------- Martin_fr · 2025-07-07 15:41 Well yes as I said workaround will be workarounds.  If you want them depends on how bad/soon you need it. If you can wait until such a feature is released (if it ever is...), well then forget workarounds. And yes, enums wont do. They are limited to 4 byte. So no PtrUint on 64bit. The range checks could have been solved (within signed int range): Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$MinEnumSize 4}type  TFoo = (e1=low(integer), e2=high(integer)); ---------------------------------------------------------------------- 440bx · 2025-07-07 15:51 Actually, I think this feature request would work wonders for you... consider this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  type  TRANGE0 = 0..high(ptruint);  TRANGE1 = 1..high(ptruint); var  r0      : TRANGE0 = 0;  r1      : TRANGE1 = 1; begin  r0 := r1;  r1 := r0;   r0 := 2 * r0 + 3;  r1 := 3 * r1 + 5; end. what's needed is for TRANGE0 and TRANGE1 _not_ be assignment compatible which this feature request would do. However, I believe that making them incompatible would also make them incompatible with any integer which would be a problem for you BUT, you can typecast the variable to its underlying type which would allow you to do math with it. The only way you'd be able to assign r0 to r1 would be to typecast either one to the type of the other which you obviously won't do since that's what you don't want. IOW, this feature would serve you very well. ---------------------------------------------------------------------- Warfley · 2025-07-07 17:21 Quote from: 440bx on July 07, 2025, 05:00:53 pmIt is completely irrelevant that base types are assignment compatible among each other. What's needed is a way to tell the compiler that a type definition is NOT compatible with other types that use the same base type, so that an HWND cannot be assigned to an HPALETTE and so on. There is no basetype in Pascal, the Pascal typesystem is not based around magic base types from which others are derived (like for example C is), the typesystem of Pascal is based around number ranges and sets. What you are asking is not just a simple change, you request a fundamental change to the typesystem of Pascal. Ordinal types in Pascal are not really types, they are defined as value ranges. Types in Pascal are from a theoretical level just a set of values. You can restrict the value set through subranges, so you can say Byte is a strict Subset of Word, this does not mean that Word is the base type of Byte or vice versa. They are just Sets. As it is worded in the ISO 7815 Standard for Pascal: QuoteA simple-type shall determine an ordered set of values . A value of an ordinal-type shall have an integer ordinal number ; the ordering relationship between any two such values of one type shall be the same as that between their ordinal numbers .Integer, Word, Byte, etc. are all just subsets of the whole number range (and Double, Float and Real subsets of the Real numbers). Pascal does not have a concept of derived types for ordinals. Don't get me wrong, I do like the idea of being able to create new distinct types, I'm just saying that this is something that the Pascal typesystem does not forsee at all and would be a very fundamental change to the typesystem, introducing a completely new kind of type (a derived type) to the language ---------------------------------------------------------------------- 440bx · 2025-07-07 18:15 @Warfley, Do you ever have something actually useful to the topic ? or you have some uncontrollable need to pretend you know way more than you actually do ? but, anyway, thank you so much for exposing the obvious and trivial... we all needed that. ---------------------------------------------------------------------- Martin_fr · 2025-07-07 18:24 Quote from: 440bx on July 07, 2025, 05:51:15 pmActually, I think this feature request would work wonders for you... QuoteHowever, I believe that making them incompatible would also make them incompatible with any integer which would be a problem for you Your entire example however has the compatible... QuoteBUT, you can typecast the variable to its underlying type which would allow you to do math with it. And that would be any better than other workarounds? Huge chunks of code would be obfuscated. To be honest my operator overloaded record approach will give me better readability. Of course, I could skip the records, and just create operators for those types that are now by themself incompatible. But the gain would really be little for me. Anyway, I am fine as it is now. My problem isn't the missing feature (as I would be happy with my record approach). My problem is that its a massive amount of code, and thousands of variable declarations need to be checked and changed. That just is a massive amount of work. (no matter how the type gets made distinct) ---------------------------------------------------------------------- Martin_fr · 2025-07-07 18:27 Quote from: 440bx on July 07, 2025, 08:15:54 pmexposing the obvious and trivial... we all needed that. I wasn't actually aware of it (as in having though about it). So I found it interesting info. Getting me to think a tiny moment about how it may differ for different language definitions. ---------------------------------------------------------------------- ASerge · 2025-07-07 18:30 I think that if this feature is implemented, then later the code will immediately be littered with a bunch of type conversions that will only worsen the readability and clarity of the code. ---------------------------------------------------------------------- Warfley · 2025-07-07 19:22 Quote from: 440bx on July 07, 2025, 08:15:54 pm@Warfley, Do you ever have something actually useful to the topic ? or you have some uncontrollable need to pretend you know way more than you actually do ? but, anyway, thank you so much for exposing the obvious and trivial... we all needed that. I answered something very specific you said: QuoteIt must not take tens of lines of various compiler construct to accomplish something so simple.And I said that it does require introduction of a new kind of type, because the current ordinal types are designed to be compatible. Note not just assignment compatible as you claim, they are fully compatible. This thing that you want does not yet exist as a concept in the language. So what would it take to include it in the compiler? First it requires the introduction of a new type kind, which in the fpc source is introduction of a new TDef including all the serialization and de-serialization into the PPUs. Then this must be included in the typechecker, introduction of type conversions etc. Not to mention that when you introduce a completely new concept to the compiler you must verify that it works with the other features of the compiler as well. Like ordinals can be used for set slices, array indices, case labels, etc. Those all need to be extended for the new kind of type thats introduced. When doing a simple text search for "orddef" which is the type identifier for ordinal types in the fpc sources, I get 2309 results. Which all need to be checked an many of those need to be amended for handling the new type. Thats going to be more than "tens of lines". It's a very deep change to the whole compiler infrastructure There would be a quick and dirty solution though, it could generate a dummy record type, and automatically generate the explicit cast operators for that type. But at that point you can also just define your own record types like Martin pointed out, which works perfectly well. ---------------------------------------------------------------------- Kays · 2025-07-07 21:00 Quote from: 440bx on July 07, 2025, 11:16:37 am[…] in Wi████s […]I voted No because this feels like a design flaw. Numeric handles are low‑level. Pascal is a high‑level programming language.Quote from: Martin_fr on July 07, 2025, 05:41:31 pm[…] [enumeration data types won’t] do. They are limited to 4 [bytes]. […]So extend them? ---------------------------------------------------------------------- 440bx · 2025-07-07 21:27 No wonder... smh. ---------------------------------------------------------------------- Martin_fr · 2025-07-07 21:35 Another work around (just in case you consider using one until...). But maybe you tried that one already too. Different pointers are assignment incompatible. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program Project1;type  TDummyFoo = record end;  TDummyBar = record end;  TFooHandle = ^TDummyFoo;  TBarHandle = ^TDummyBar;var  a: TFooHandle;  b: TBarHandle; begina:= b; // failsend.  Not tested anything else... ---------------------------------------------------------------------- Thaddy · 2025-07-08 04:42 Quote from: Kays on July 07, 2025, 11:00:53 pmQuote from: 440bx on July 07, 2025, 11:16:37 am[…] in Wi████s […]I voted No because this feels like a design flaw. Numeric handles are low‑level. Pascal is a high‑level programming language.Quote from: Martin_fr on July 07, 2025, 05:41:31 pm[…] [enumeration data types won’t] do. They are limited to 4 [bytes]. […]So extend them? Note they are not limited to 4 bytes but platform. On 64 bit platforms it is 8 bytes. ---------------------------------------------------------------------- H₂SO₄ · 2025-07-08 05:54 I think this would be a useful feature, but unfortunately I don't think it'll be implemented after seeing @Warfley's demonstration on how deeply ingrained the current behavior is within the compiler. Here's my suggestion for a workaround: define a generic record type with a dummy parameter that is different for each specialization. This ensures that all distinct handle types are truly incompatible while still having the relevant operators defined (explicit assignment, equal, not equal). Defining just one such advanced record and declaring handle types like  HWND = type THandle  would discard the operator overloads and make the derived types much less useful. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  generic THandle = record  private type    TSelf = specialize THandle;  strict private    Value: PtrInt;  public  class operator explicit(const Self: TSelf): PtrInt;    class operator explicit(Ordinal: PtrInt): TSelf;    class operator  =(const A, B: TSelf): Boolean;    class operator <>(const A, B: TSelf): Boolean;  end; class operator THandle.explicit(const Self: TSelf): PtrInt;begin  Result := Self.Value;end; class operator THandle.explicit(Ordinal: PtrInt): TSelf;begin  Result.Value := Ordinal;end; class operator THandle.=(const A, B: TSelf): Boolean;begin  Result := (A.Value = B.Value);end; class operator THandle.<>(const A, B: TSelf): Boolean;begin  Result := (A.Value <> B.Value);end; Only around 30 lines for the ability to write code like this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  HBITMAP = specialize THandle<'HBITMAP'>;  HWND    = specialize THandle<'HWND'>; procedure Test(var Window: HWND; var Bitmap: HBITMAP);begin   if Window = HWND(0) then  // OK    Window := HWND(-1);     // OK   if Bitmap = -1 then;      // Error: Operator is not overloaded: "" = "ShortInt"    Bitmap := Window;     // Error: Incompatible types: got "THandle" expected "THandle"end; Granted, the error messages aren't the most insightful, but at least it stops semantically invalid code from being compiled. ---------------------------------------------------------------------- MarkMLl · 2025-07-08 07:20 Quote from: 440bx on July 07, 2025, 08:15:54 pmDo you ever have something actually useful to the topic ? or you have some uncontrollable need to pretend you know way more than you actually do ? FFS, you've asked for a useful feature that a number of us believe should be implemented. Do you /rally/ have to insult senior members of the community to the detriment of everybody else? The best way of never seeing anything like this would be for the people who want it to look like utter twats. MarkMLl ---------------------------------------------------------------------- Martin_fr · 2025-07-08 07:36 Moderation note: Language please. (all parties). ---------------------------------------------------------------------- MarkMLl · 2025-07-08 07:52 Quote from: ASerge on July 07, 2025, 08:30:01 pmI think that if this feature is implemented, then later the code will immediately be littered with a bunch of type conversions that will only worsen the readability and clarity of the code. The biggest obvious problem is numeric  constants. But if the current untyped constants were, by definition, compatible with any numeric type that would remove the requirement for a lot of casts. Apropos the overall difficulty, one workaround would be if type declarations containing an explicit "type" were always hard types, i.e. not compatible with other integer types etc. Frankly, I'm unconvinced by Warfley's explanation of how numeric types are defined and implemented. Basically, what he's told us is that integers are an ordered set (without the possibility of intermediate values): but that's the basic mathematical definition of an integer. Quote from: Khrys on July 08, 2025, 07:54:31 amHere's my suggestion for a workaround: define a generic record type with a dummy parameter that is different for each specialization. Yes, I cited that earlier in the thread. However that's still thirty lines that works only with the main branch of the compiler: what we need is a single-line declaration that works as would be reasonably expected. Quote from: Kays on July 07, 2025, 11:00:53 pmNumeric handles are low‑level. Pascal is a high‑level programming language. In which case why not drop this pretence that Pascal is suitable for systems programming? Drop the pretence that it's good for talking directly to the OS, and that it's suitable for embedded programming. This is something that's been running for 30+ years: when VB came out it needed (V)C++ to write controls etc., while Anders's Pascal Compiler- both Turbo and Delphi- was general-purpose. However in the specific case of the handle example: handles, should never, by definition, be amenable to numeric manipulation by application code. If they're compatible with arithmetic types at the level of application code then something's broken... or something is about to be broken when a less-than-experienced coder gets his mitt on it. And half the point of this community is to try to attract new FPC and Lazarus users... MarkMLl ---------------------------------------------------------------------- MarkMLl · 2025-07-08 07:56 Quote from: Martin_fr on July 08, 2025, 09:36:23 amModeration note: Language please. (all parties). Mea culpa, my apologies to the moderators and community. But sometimes I feel that certain people are not content with merely presenting an argument to prove their opponent wrong, but insist on demeaning him and the validity of his expertise which will invariably backfire. MarkMLl ---------------------------------------------------------------------- 440bx · 2025-07-08 08:10 Quote from: MarkMLl on July 08, 2025, 09:20:04 amDo you /rally/ have to insult senior members of the community to the detriment of everybody else? I didn't insult him.  I pointed out facts. ---------------------------------------------------------------------- marcov · 2025-07-08 08:42 Quote from: MarkMLl on July 08, 2025, 09:56:34 amQuote from: Martin_fr on July 08, 2025, 09:36:23 amModeration note: Language please. (all parties). Mea culpa, my apologies to the moderators and community. But sometimes I feel that certain people are not content with merely presenting an argument to prove their opponent wrong, but insist on demeaning him and the validity of his expertise which will invariably backfire. Then you track a recent couple of such posts and report them, under no circumstance you start bickering in the threads. The constant discussion and veiled insults to intelligence has to cease, it has become toxic. ---------------------------------------------------------------------- Warfley · 2025-07-08 09:08 Quote from: MarkMLl on July 08, 2025, 09:52:19 amHowever in the specific case of the handle example: handles, should never, by definition, be amenable to numeric manipulation by application code. If they're compatible with arithmetic types at the level of application code then something's broken... or something is about to be broken when a less-than-experienced coder gets his mitt on it. That concept is called an opaque type and some languages like Swift or Haskell have them. It's a type the internals of which you do not know about or at best can make some assumptions about (like that it overloades the = and <> operators). C has them also for structs/records but only allows to access them via pointer. Pascal only has a few very specific opaque types, there are untyped pointers which are a pointer to an opaque type, there is the magical "File" types (like file of XYZ) which are a class of opaque types and then theres Variant which is an implementation specific opaque type. But Pascal does not have any user definable opaque types. There are simple types, which are basically defined as ordered sets, where Subrange types are defined as subsets of the number plane (and integer, byte, etc. are just instances of that), real types and enumeration types. And then there are composite types like arrays and records. Now opaque types can be added to this mix, but the question is how the opaque type should be handled. Because Pascal is a relatively low level language, unlike for example Haskell or Swift, there needs to be the decision if the opaque type should fix memory layout or fix interfaces. Extended Pascal has introduced an opaque typesystem called Schemata, afaik it was never put into practice, but at it's core it's a mix between interfaces and generics. It can be resolved during compile time like generics, or at runtime using pointers (in theory, as I said theres probably no real implementation out there). But this would not be quite fitting for Handles as Schemata do not define the memory layout but only the interfaces of the type (like opaque types in Swift or go). What handles would need would be some information about the memory layout but no information about the interface. Basically just a "Binary blob" type. Which would technically be possible to implement, but why not just use: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i TMyHandle = record data: array[0..HANDLESIZE-1] of Byte end;Now you have created a new type which has absolutely no defined interface other than it is assignable to itself and allowing raw byte access. If you need additional interfaces you can define them yourself (e.g. by overloading the explicit cast operator and/or the comparison operators = and <>). So I do not see why you would need to introduce a new kind of type to the language. ---------------------------------------------------------------------- Martin_fr · 2025-07-08 09:11 Back to the topic. Out of interest.... If that feature existed, may I assume you would want to have (or do your own) definition of Windows API calls? So that e.g. "setwindowpos" can not be called with anything but a HWND as first argument? And (as you stated yourself earlier), if a similar function would take special values, such as invalide_handle, then there had to be an INVALID_HWND. But, and this is my question, what would you do about a function such as SelectObject? https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi- selectobject The 2nd argument can be a Bitmap, Font, or other Handle? So now, you don't just need distinct types, you need types that also have some sort of inheritance / parent-type? Or would you rather add lots of overloaded functions? See https://learn.microsoft.com/en-us/windows/win32/api/handleapi/nf- handleapi-closehandle it takes more than a dozen different types of handles. I am not even sure that classic inheritance would solve the Windows API, there may be some types in different groups... ---------------------------------------------------------------------- Martin_fr · 2025-07-08 09:21 Quote from: Warfley on July 08, 2025, 11:08:57 amSo I do not see why you would need to introduce a new kind of type to the language. Because (as far as I understand) he feels that such a type is needed commonly enough to simplify the definition. And also so that certain operators should automatically already exists. So it is not needed to add your own unit of 10 to 30 lines to define all the needed ops. His example was that, in the record case, if you don't have an operator overload, then you can not do     TDistinctHandle(0) Though, on that actually, I think if there was a build in type, then it should not allow that either. Because either - the type is numeric/ordinal and comes with all that we have - It is not / It is a binary blob, it is opaque. But if it is truly opaque, then even its size is not know (some Pascal code does that for pointed to types that are declared an empty record). If the size is not known (to the user code), then you shouldn't be able to cast a number to it either.  Because that would imply that it must fit the number, and needs the minimum size. (zero does not have that, but 8192 requires a certain min size). Casting also violate opaqueness because it allows to gain info on the content. But such info should not be accessible. That is why I was doubtful a type for what he wants would be easy to define. Because, well, its not an opaque type. Its an inbetween. With very specific properties chosen. ---------------------------------------------------------------------- Thaddy · 2025-07-08 09:33 Ergo, this can only be done in languages where "everything is an object". ---------------------------------------------------------------------- marcov · 2025-07-08 09:38 Quote from: Warfley on July 08, 2025, 11:08:57 amThat concept is called an opaque type and some languages like Swift or Haskell have them. It's a type the internals of which you do not know about or at best can make some assumptions about (like that it overloades the = and <> operators). C has them also for structs/records but only allows to access them via pointer. IMHO opague adds another level that also prohibits you to mess with the internals of the handle type (and you can go pretty far with that, even disallowing casting). Preventing assignment compatibility is a less strong feature, but is IMHO the more desirable subset that is needed. The bit that prevent most of the mistakes so to say. Additional layers of information hiding have diminishing returns. QuoteWhat handles would need would be some information about the memory layout but no information about the interface. Basically just a "Binary blob" type. Which would technically be possible to implement, but why not just use: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i TMyHandle = record data: array[0..HANDLESIZE-1] of Byte end; Careful. Rules for parameter passing a structured type and an ordinal type might be different for some architectures. For values smaller than the word size, record packing/padding might also be a problem. QuoteSo I do not see why you would need to introduce a new kind of type to the language. I think it would be a good thing to have a modeswitch that forces Type x = type Y; to be not assignment compatible. But that might require an extra flag in the type system and thus non trivial work. But it would be the less hackish way of doing it. Quote from: Thaddy on July 08, 2025, 11:33:04 amErgo, this can only be done in languages where "everything is an object". I don't think so, and even then very few are pure in that regard (but rely on autoboxing of native types) ---------------------------------------------------------------------- Warfley · 2025-07-08 10:00 Quote from: Martin_fr on July 08, 2025, 11:21:11 amBecause (as far as I understand) he feels that such a type is needed commonly enough to simplify the definition. And also so that certain operators should automatically already exists. So it is not needed to add your own unit of 10 to 30 lines to define all the needed ops. Thats actually not that difficult either with generics: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i generic TBinaryBlob = packed record  strict private data: array[0..Size-1] of Byte;  class operator =(...): Boolean;  class operator <>(...): Boolean;  class operator Explicit(...): specialize TBinaryBlob;  ...end;It just needs to be defined once and you can create as many instances as you like of it. If this would be really necessary to be provided by the FPC this could also be put into the RTL. There could even be variants like TComparableBlob which has = and <>, OrdinalBlob which has the relational operators <, >, etc.. Just an hour or so of work and you have something you can use for the rest of your life in any code base without having to re-define it every time, no new compiler feature necessary. Quote from: Martin_fr on July 08, 2025, 11:21:11 amThat is why I was doubtful a type for what he wants would be easy to define. Because, well, its not an opaque type. Its an inbetween. With very specific properties chosen. The underlying problem is that Pascals Typesystem for base types is rooted in set theory, while the idea of having distinctive types where you define subtypes and specific conversion points is rooted in type theory. This is btw something that makes Pascal somewhat unique, because most programming languages are based around type theory. It's a shame that Pascal doesn't do much with it (like this would be perfect to enable constraint languages over types). But it also means that things like the original question of this thread are non trivial to put into practice because it's a completely different underlying theory. Type theory and set theory are two perfectly valid theories but they are two different ways to model a system. So mixing them is just not really trivial ---------------------------------------------------------------------- 440bx · 2025-07-08 10:09 Quote from: Martin_fr on July 08, 2025, 11:11:14 amBack to the topic. Out of interest.... If that feature existed, may I assume you would want to have (or do your own) definition of Windows API calls? So that e.g. "setwindowpos" can not be called with anything but a HWND as first argument? Yes and, I already have my own Windows API definitions, it's not quite complete yet but, I'm in the ballpark of 10,000 definitions (including overloads which account for about 35 to 40% of that.) Quote from: Martin_fr on July 08, 2025, 11:11:14 amAnd (as you stated yourself earlier), if a similar function would take special values, such as invalide_handle, then there had to be an INVALID_HWND. Yes and, that's why it's important for the type to behave like a normal type, that is, being able to typecast constants like it's done with enumerations (or other types for that matter.) Quote from: Martin_fr on July 08, 2025, 11:11:14 amBut, and this is my question, what would you do about a function such as SelectObject? https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi- selectobject The 2nd argument can be a Bitmap, Font, or other Handle? I'd simply create an overload for every type, which is what overloads are for.  That way if an HWND, an HMODULE or some other HWHATEVER that is not a GDI object then the compiler can reject that. Quote from: Martin_fr on July 08, 2025, 11:11:14 amSo now, you don't just need distinct types, you need types that also have some sort of inheritance / parent-type? Not at all, all that's needed is one definition/overload per type.  Clean and simple. Quote from: Martin_fr on July 08, 2025, 11:11:14 amOr would you rather add lots of overloaded functions? Yes, absolutely.  That's what overloading is for and, SelectObject only needs 5 overloads, copy/paste takes care of that rather quickly. Quote from: Martin_fr on July 08, 2025, 11:11:14 amSee https://learn.microsoft.com/en-us/windows/win32/api/handleapi/nf- handleapi-closehandle it takes more than a dozen different types of handles. Yes, CloseHandle takes more work but, those definitions need be done only once.  Yes, one time, CloseHandle has to be defined a dozen or more times, small price to pay for effective type checking in every program written after that. Quote from: Martin_fr on July 08, 2025, 11:11:14 amI am not even sure that classic inheritance would solve the Windows API, there may be some types in different groups... I don't see a problem.  My definitions have overloads galore, the compiler doesn't mind at all and, I get stronger type checking.   It's all good. ---------------------------------------------------------------------- Warfley · 2025-07-08 11:16 And why don't you do it with a record? You could even create an intermediary type where all of these types can cast onto but not back, so you have the ability to assign them to that common type but not between each other. This way you don't even need multiple overloads for the function, you just need one where the typesystem handles the rest. If that is so important to you that you spend hours in this thread, why don't you just sit down and write a generic record for this? It's like one hour you need to invest once, and you can use it for the rest of your life in any project you like. If it's really good you can even make it a merge request to the RTL and so everyone can benefit from the work you put in there. All you have written why records are not an option for you is because you think it's to much effort to write like 50 lines of code once. Like not even for every type or every usage, just once, forever. (Well my theory is that you don't know how as was obvious when you said you had trouble to enable casts even though this is incredibly easy. But if you don't know, just ask ) ---------------------------------------------------------------------- 440bx · 2025-07-08 11:44 Quote from: Warfley on July 08, 2025, 01:16:12 pmAnd why don't you do it with a record? Because I have not found a way to do it, with records or anything else, that I deemed satisfactory. Quote from: Warfley on July 08, 2025, 01:16:12 pmThis way you don't even need multiple overloads for the function, you just need one where the typesystem handles the rest. I WANT the multiple overloads.  That serves as documentation.  For instance, in the case of SelectObject, the overloads make it crystal clear what the function can handle. Quote from: Warfley on July 08, 2025, 01:16:12 pmIf that is so important to you that you spend hours in this thread, I didn't spend hours in this thread and, I'm responding only to be polite.  Quote from: Warfley on July 08, 2025, 01:16:12 pm why don't you just sit down and write a generic record for this? Because I want the solution to have two characteristics: 1. it must be simple.  2. it must _not_ require constant modifications and updating to accommodate new types.  IOW, it must be very straightforward, like what the double use of "type" is supposed to do, very straightforward but unfortunately that doesn't cut it. I tell you what, if you can provide a solution that complies with the above, not only I'd be grateful, I'd be impressed (I assure you, that doesn't happen very often.)  Just to emphasize the requirements, 1 & 2 above AND overloads MUST be required to accommodate multiple types in functions, e.g, SelectObject, a solution that does not require overloads is NOT a solution. Can you do that ? ETA: very important, the type must be usable to typecast constants that are assignment compatible with the _underlying_ type.  e.g, DESKTOP_WINDOW = HWND(0) presuming that HWND is now a hard type. Just in case, it must be possible to use the value _without_ referring to whatever field name the record uses (I'm presuming you're going to use a record) IOW, if the record has a field "v" to hold the value, "v'" must _never_ need to be explicitly referenced. ---------------------------------------------------------------------- Warfley · 2025-07-08 11:58 Well there seems to actually be a bug in the compiler that prevents this from being possible, because the Explicit typecast is not transfered correctly when the type copy is created. If this bug is fixed the following should satisfy all of your requirements: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$ModeSwitch advancedrecords} interface type  generic TTypeWrapper = record    data: T;     class operator =(const lhs, rhs: specialize TTypeWrapper): Boolean;inline;    class operator <>(const lhs, rhs: specialize TTypeWrapper): Boolean;inline;    class operator Explicit(const rec: specialize TTypeWrapper): T;inline;    class operator Explicit(const value: T): specialize TTypeWrapper;inline;  end; implementation class operator TTypeWrapper.=(const lhs, rhs: specialize TTypeWrapper): Boolean;begin  result:=lhs=rhs;end; class operator TTypeWrapper.<>(const lhs, rhs: specialize TTypeWrapper): Boolean;begin  result:=lhs<>rhs;end; class operator TTypeWrapper.Explicit(const rec: specialize TTypeWrapper): T;begin  result:=rec.data;end; class operator TTypeWrapper.Explicit(const value: T): specialize TTypeWrapper;begin  result.data:=value;end;Example: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  THWND = type specialize TTypeWrapper;  THandle = type specialize TTypeWrapper;  var  hw: THWND;  hnd: THandle;begin  hw := THWND(0);  hnd := THandle(-1);  WriteLn(Integer(hw));  WriteLn(Integer(hnd));  hw := 0; // Error  hnd := hw; // Errorend. The fix should also not be to difficult to do maybe I'll look into it later today ---------------------------------------------------------------------- Kays · 2025-07-08 12:00 Quote from: Warfley on July 08, 2025, 11:08:57 am[…] Extended Pascal has introduced an opaque typesystem called Schemata, […]You mean restricted data types? At any rate I endorse FPC supporting EP. ---------------------------------------------------------------------- Warfley · 2025-07-08 12:25 No, extended Pascal also defines restricted types, but those are basically the equivalent of making the internals of the type private in object pascal. I guess it's closest to what 440bx wants, but also not fully (because they remove all operations not just making them distinct). Schemata are different, they allow to define type interfaces. To take an example from the extpas standard: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  variant record(d : a subrange) =    record case d of      1: (f1 : integer);      2: (f2 : integer);    end;This would be roughly equivalent to Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  generic MyVariant =    record case d of      1: (f1 : integer);    2: (f2 : integer);    end;On a static level, but it can also be used dynamically, in which case it is like interfaces for classes in Object Pascal. Also it has full typechecking of the schema parameters. It's extremely powerful, but also extremely complicated. I guess the reason no one implemented it back in the day was because of that complexity. That said, modern compilers can probably do much better, and this feature is mostly implemented in go and Swift. So ExtPas was just way ahead of it's time with that About integrating ExtPas into FPC, I'm not sure how well it works, at least if you want 100% compliance to the standard, because ExtPas is pretty consistent in it's definitions, which means all the systems build on top of each other. For example all different types of strings are unified through the string schema and string manipulation functions just take the string schema as argument. So I guess there would be the possibility to take certain elements from ExtPas, but really full compliance would probably require some re-designs of the FPC internals. That said, if schemata would be fully incorporated, I'd probably never have to use generics or OOP ever again, because this single concept unifies pretty much all of that into one construct. I think a good ExtPas compiler with a few slight adjustments (e.g. UTF-8 strings, arbitrary sized integers) with would probably easily take my top spot for favorite language. ---------------------------------------------------------------------- 440bx · 2025-07-08 12:56 @Warfley, Before anything, thank you for your efforts.  I appreciate them. What you've offered looks quite good.  It's really unfortunate that there is a bug that prevents your solution from fully working but, that's a different problem. Obviously, I'd really like to use something that fully works.  You mentioned you might have time to work on fixing the bug.  Your best guess: do you think that the fix could be part of the upcoming v3.2.4 release ?  I know that's hoping for a lot but, I _really_ like to use the compiler's release/stable version. Anyway, if you can provide a way to "fix" v3.2.2 to make your solution operate properly, I would appreciate that as it would allow me to take your solution for a "walk" (more like a marathon... ) ETA: Fixed typo. ---------------------------------------------------------------------- Warfley · 2025-07-08 13:04 I will try to fix this based on 3.2.4 so maybe theres a chance to still get it in there (even though I doubt it). If it works well, I can assure you it will also become an indispensable tool for me, because I also was very frustrated about not being able to re-define types for overloading (personally for me it's even more about String types, because I want to distinguish between things like passwords, usernames and stuff like that to not accidentally enter the wrong thing in the wrong parameter) ---------------------------------------------------------------------- 440bx · 2025-07-08 13:14 Sounds good. ---------------------------------------------------------------------- Martin_fr · 2025-07-08 13:16 Quote from: Warfley on July 08, 2025, 01:58:33 pmWell there seems to actually be a bug in the compiler that prevents this from being possible, because the Explicit typecast is not transfered correctly when the type copy is created. Well the problem IMHO is QuoteCode: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  THWND = type specialize TTypeWrapper;  THandle = type specialize TTypeWrapper;  by doing an extra "type" THWND is a new type, not anymore the "specialized type". But the operator was specialized for the "specialized type". It does not work for the new distinct type created by the extra "type". ---------------------------------------------------------------------- Martin_fr · 2025-07-08 13:33 What you can do, but it is ONE EXTRA line per type. - I hardcoded the integer, but it could be a 2nd generic param - inside the generic, I use just the generic name as ref to the current specialization. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program Project1;  {$Mode objfpc}  {$ModeSwitch advancedrecords}     type    generic TTypeWrapper = record        data: integer;         class operator =(const lhs, rhs: TTypeWrapper): Boolean;inline;        class operator <>(const lhs, rhs: TTypeWrapper): Boolean;inline;        class operator Explicit(const rec: TTypeWrapper): integer;inline;      class operator Explicit(const value: integer): TTypeWrapper;inline;      end;       //generic TTypeWrapper = specialize TTypeWrapper;      class operator TTypeWrapper.=(const lhs, rhs: TTypeWrapper): Boolean;    begin      result:=lhs=rhs;    end;     class operator TTypeWrapper.<>(const lhs, rhs: TTypeWrapper): Boolean;    begin      result:=lhs<>rhs;    end;     class operator TTypeWrapper.Explicit(const rec: TTypeWrapper): integer;    begin    result:=rec.data;    end;     class operator TTypeWrapper.Explicit(const value: integer): TTypeWrapper;    begin    result.data:=value;    end;        type    t1 = record end;    t2 = record end;      THWND = specialize TTypeWrapper;      THandle = specialize TTypeWrapper;      var      hw: THWND;      hnd: THandle;    begin      hw := THWND(integer(0));      hnd := THandle(-1);      WriteLn(Integer(hw));      WriteLn(Integer(hnd));    hw := 0; // Error      hnd := hw; // Error    end. You could put the specialize into a macro... ---------------------------------------------------------------------- Warfley · 2025-07-08 14:27 Quote from: Martin_fr on July 08, 2025, 03:16:19 pmby doing an extra "type" THWND is a new type, not anymore the "specialized type". But the operator was specialized for the "specialized type". It does not work for the new distinct type created by the extra "type". I mean it's very inconsistent as some operators do work (like the explicit operator into the other direction) while others don't work. Thats why I consider it a bug, either no operators should work or all of them. And as methods etc. all work I believe operators should as well ---------------------------------------------------------------------- Martin_fr · 2025-07-08 15:15 Quote from: Warfley on July 08, 2025, 04:27:27 pmQuote from: Martin_fr on July 08, 2025, 03:16:19 pmby doing an extra "type" THWND is a new type, not anymore the "specialized type". But the operator was specialized for the "specialized type". It does not work for the new distinct type created by the extra "type". I mean it's very inconsistent as some operators do work (like the explicit operator into the other direction) while others don't work. Thats why I consider it a bug, either no operators should work or all of them. And as methods etc. all work I believe operators should as well Yes, I realized that while you replied. I myself more often define operators outside the type. And then of course, I would not expect Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i operator := (a: integer): TFooRecord;to work on Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type TBar = type TFooRecord; But, in your code operators were declared as part of the type. So (probably) one could expect that, if a new type is created, those operators are copied/recreated in the same manner as fields are. Btw, if you only need one base type, then you don't even need the generic. ---------------------------------------------------------------------- 440bx · 2025-07-08 15:27 Quote from: Martin_fr on July 08, 2025, 05:15:38 pmBtw, if you only need one base type, then you don't even need the generic. Can you elaborate on that ?... the majority of handles are ptruint (in Windows)... Your solution depends on defining records, which works but it really isn't desirable... but that solves the "type" problem... ---------------------------------------------------------------------- Martin_fr · 2025-07-08 15:36 Quote from: 440bx on July 08, 2025, 05:27:22 pmQuote from: Martin_fr on July 08, 2025, 05:15:38 pmBtw, if you only need one base type, then you don't even need the generic. Can you elaborate on that ?... the majority of handles are ptruint (in Windows)... Your solution depends on defining records, which works but it really isn't desirable... but that solves the "type" problem... No it doesn't solve the type problem. If you only need Ptruint, then you can define your "record wrapper" as a normal record instead of as a generic. But you still have do then do Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i TMFooHandle = type TMWrapper; And that still has the issue of not copying all operators. Mind that with the generic, even though you have a specialize on each declaration, that does not create new types. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var  a: specialize TFoo;  b: specialize TFoo;gives the same type to a and b. The 2nd specialize re-uses the same type that already exists. That is why you can specialize inline, even do stuff like (if TFoo was a class): begin a := specialize TFoo.create; However since it is just the explicit operator that does not get copied.... You can declare the := operator. That seems to get copied. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i class operator :=(const i: integer): TTypeWrapper; That also allows typecasts. Just unfortunately it also allows   TFooHandle := 0; // without typecast. but different handles are still incompatible. ---------------------------------------------------------------------- 440bx · 2025-07-08 15:53 Martin, thank you for elaborating. ---------------------------------------------------------------------- Warfley · 2025-07-09 23:45 Quick update on the matter, I found the issue and I've fixed it (tbh quite a horrible fix but seems to work). The following test case now compiles: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$mode objfpc}{$modeswitch advancedrecords}type  generic TTypeWrapper = record    data: T;      class operator =(const lhs, rhs: specialize TTypeWrapper): Boolean;inline;    class operator <>(const lhs, rhs: specialize TTypeWrapper): Boolean;inline;    class operator Explicit(const rec: specialize TTypeWrapper): T;inline;    class operator Explicit(const value: T): specialize TTypeWrapper;inline;  end;    class operator TTypeWrapper.=(const lhs, rhs: specialize TTypeWrapper): Boolean;begin  result:=lhs.data=rhs.data;end;  class operator TTypeWrapper.<>(const lhs, rhs: specialize TTypeWrapper): Boolean;begin  result:=lhs.data<>rhs.data;end;  class operator TTypeWrapper.Explicit(const rec: specialize TTypeWrapper): T;begin  result:=rec.data;end;  class operator TTypeWrapper.Explicit(const value: T): specialize TTypeWrapper;begin  result.data:=value;end; type  TIntegerWrapper = specialize TTypeWrapper;  THWND = type TIntegerWrapper;  THandle = type TIntegerWrapper;    var  hw: THWND;  hnd: THandle;begin  hw := THWND(0);  hnd := THandle(-1);  if Integer(hw) <> 0 then Halt(1);  if Integer(hnd) <> -1 then Halt(2);  if not (hw=hw) then Halt(3);  if hw<>hw then Halt(4);end. Pipeline still failing and need to test a bit more, but hopefully I get this done by the weekend