4

I was using assignment of Component Tag (NativeInt) value adding it to the set of bytes. The program worked properly when compiled for WIN32 but doesn't compile for WINx64. (Error 2001 Ordinal type required) Here is an MCVE:

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, System.Classes;
  var 
  S:set of byte;
  C:TComponent;

begin
   C:=TComponent.Create(nil);
   C.Tag:=1;
   s:=[C.Tag];
   C.Free;
 end.

How can I adjust the code to make it suitable for WINx64 compilation?

asd-tm
  • 3,381
  • 2
  • 24
  • 41

2 Answers2

8

Tag is NativeInt. That is 32 bits in x86, and 64 bits in x64. As I understand it, a 32 bit integer is deemed to be an ordinal, and a 64 bit integer is not an ordinal. That is the reason for the compiler error, I think, although quite why Integer is deemed to be an ordinal type, and Int64 is not, I cannot tell you. If I had to guess, I would imagine that this is related to the fact that Int64 does not fit into a register on x86, and so requires quite different treatment from the compiler in comparison to 1, 2 and 4 byte ordinal types.

Since you probably don't want something that changes size, I expect you are fine to cast to Integer:

s := [Integer(C.Tag)];

And since you are only using the low 8 bits anyway, you should probably cast it to Byte:

s := [Byte(C.Tag)];

Preferably with an assertion that you are in range:

Assert((C.Tag >= low(Byte)) and (C.Tag <= high(Byte)))

Frankly though, you are, in my view, better avoiding Tag altogether. Store your data in a variable dedicated to the task, with a type that you can choose. As a general rule, in my view, Tag is something that you should avoid using. It's not type safe unless you happen to have a NatoiveInt to store, its name gives no indication of its contents, and it's all to easy to have collisions when multiple parties attempt to use it.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Thank you, David for your detailed answer. Actually, I had never used Tag property before I started working with D XE5 and FMX library for the same reasons as you stated in your answer. And I entirely agree that we should avoid using it. However it seems that Embarcadero is encouraging using `Tag` property now. You can see it for instance by starting a new FMX Split pane Metropolis application. The property is used in SplitPaneView (detail) form for the selection of TabItems in the right layout. `TagString` is actively used in FMX.Grid etc. – asd-tm Oct 30 '15 at 08:59
  • Tag becomes especially useful if you fill lists with `LiveBindings` and you need to store the value from the id data field somewhere in the `ListItem`. May I ask your advice, what we can use instead of `Tag`? I am afraid that declaring a collection/array variable might be not reliable as we can not easily make sure if the array is filled with the same number of items as the List `Count` and that they have the same order (some items might be invisible)... – asd-tm Oct 30 '15 at 09:07
  • I'd imagine that you declare your own derived class of the GUI object, and place the property in that class, so that the lifetime is handled automatically. Generally though I try to avoid keeping business logic in the UI objects. I think it's better to view the UI as a presentation of a view of the underlying system state. That state is held in non-visual objects. – David Heffernan Oct 30 '15 at 09:30
  • Personally I rarely use, and don't like, Tag. However, I do find TagObject useful particularly when I am dynamically creating GUI items to tie a particular item to its underlying structure - or CURRENT underlying structure if different objects can present to the same GUI interface at different times, or accessing relevant objects in events. – Dsm Oct 30 '15 at 09:43
  • If `int64` is not ordinal then what the term `ordinal` means? – kludg Oct 30 '15 at 10:49
  • Thank you, David, I think I got your point. However it would require more coding and probably even registering new components for easier application of `LiveBindings`. I am currently using `Tag` in almost the same circumstances as @Dsm. for instance when adding buttons to `ListBoxItems` in FillingListItem event handler. It helps to distinguish the `id`s of records in the buttons' `OnClick` event handler. Or in other cases when distinguishig the calling UI component in ther similar event handlers. – asd-tm Oct 30 '15 at 10:50
  • @user246408 I don't understand it either. But try a for loop with `Int64` loop variable. You'll get `E2032 For loop control variable must have ordinal type`. So yeah, what does ordinal mean?! – David Heffernan Oct 30 '15 at 10:51
  • @user246408 Exactly! It looks like a compiler bug. `var i64: int64; s: set of byte; begin s:=[i64];...` rises compiler error E2001 even in WINx86 compiler – asd-tm Oct 30 '15 at 10:55
  • @asd-tm I don't think it's a compiler bug. On x86, `Int64` is a special type because it cannot fit in a register. Thus the compiler has to rely on compiler magic and RTL helper functions to perform operations on this type. – David Heffernan Oct 30 '15 at 11:00
  • Seems like they didn't want to add a new compiler error message string for using `int64` as `for` variable, and so made the meaning of `ordinal` mysterious. – kludg Oct 30 '15 at 11:09
  • David, @user246408 I continued investigating. The WINx86 compiler accepts `var ui64: UInt64; i64: int64; ... i64:=ui64 div 64;` – asd-tm Oct 30 '15 at 11:18
  • @asd-tm Yes it does. It supports arithmetic operations on this type. But, the compiler is clear enough in stating that it does not regard it as an *ordinal type*, whatever that signifies. There are some compiler internals behind this that none of us can see. I think this is more likely a documentation issue. – David Heffernan Oct 30 '15 at 11:19
  • I believe it can only signify that an *ordinal type* size can't exceed 4 bytes due to some compiler internals; it has nothing to do with what *ordinal* means. – kludg Oct 30 '15 at 11:46
  • [Ordinal Types](http://docwiki.embarcadero.com/RADStudio/en/Simple_Types#Ordinal_Types) states that `Ord()` does not take `Int64` arguments. Perhaps this is a clue. – LU RD Oct 30 '15 at 13:39
  • @LURD - in Delphi XE `Ord()` accepts `Int64`, but returns 32-bit LongInt; so as I posted before the ordinality seems to be limited by 4 bytes. – kludg Oct 30 '15 at 16:33
  • @LU RD - The Ordinal Types documentation also states unequivocally that Int64 *is an ordinal type* (being a Platform Independent Integer Type). Ord() is mentioned as one exception made for Int64 (with a non-specific further reference to "other routines" that do not support Int64 arguments). But the documentation says *nothing* about different behaviours with x86 and x64 compilers w.r.t implicit downcasting, which is the point of the question: that the two compilers are inconveniently *behaving differently* in this respect. – Deltics Oct 31 '15 at 04:50
  • @LURD interestingly, this documentation that you pointed to shows no reason why `Int64` wouldn't be an Ordinal Type. It gives more of a mathematical definition that any Integer subset type definitely satisfies. There's no insight on the compiler internals that establish the *real restriction* over what an ordinal type is. – GabrielF Nov 05 '15 at 16:32
  • @Gabriel There's no docs on this. All I can do is guess that it related to the fact that Int64 is not supported on hardware for 32 bit machines. – David Heffernan Nov 05 '15 at 16:39
7

Add a typecast of the Tag property to cast it to a Byte:

s:=[Byte(C.Tag)];

Although not necessary for the Win32 compiler it will accept this and it does allow the Win64 compiler to accept the code and has the same effect and result in both cases.

On the face of it this would appear to be a bug in the Win64 compiler as there is no apparent reason why it should require this cast.

Deltics
  • 22,162
  • 2
  • 42
  • 70
  • Thank you, Deltics for your answer and . However it is difficult to understand, why WINx64 NativeInt is not ordinal. Unfortunately, I can accept only one answer. I could only upvote yours. – asd-tm Oct 30 '15 at 09:01
  • I don't think it's a bug. I think it's a consequence of `Int64` not being deemed an ordinal type. Doesn't fit in a register on x86, requires quite different style of code gen, and so on. – David Heffernan Oct 30 '15 at 11:02
  • @DavidHeffernan however, `case i64 of 1: s := [1]; end;` is accepted by WINx86 compiler. And thus the compiler consideres the int64 to be ordinal. – asd-tm Oct 30 '15 at 11:07
  • @asd-tm Yes, but so what? Presumably the compiler is happy to use Int64 in a case statement, but not in things like set expressions or for loop variables. The compiler has told you that it does not consider `Int64` to be an ordinal type. I've added some more text to my answer. Of course, we are guessing at the moment because nobody has found any documentation of why `Int64` is not considered an ordinal type. – David Heffernan Oct 30 '15 at 11:09
  • There's something very confusing about compiler's definition of `Ordinal`, then. Because when you try to do a `case` with a `String` parameter, compiler says: `Ordinal type required`. So, you would assume `Int64` is considered an ordinal type, since it's accepted in a `case` statement. – GabrielF Oct 30 '15 at 12:05
  • @GabrielF, Yes. Exactly. That is what I was saying. – asd-tm Oct 30 '15 at 12:30
  • @David - You say you don't think it's a bug but a consequence of `Int64` not being an ordinal type. I don't see how you reach that conclusion. `Int64` is *documented* as an ordinal type. Irrespective of the underlying implementation it is the compilers job to translate source code to object code consistently with the language spec. In other places that an ordinal type is required (e.g. Succ/Pred/High/Low as well as case statements) both x86 and x64 compilers accept an **Int64**. So if not a bug, the inconsistency in the OPs case at least is an inexplicable difference in the compilers. – Deltics Oct 31 '15 at 04:46
  • I suppose I don't think it's a bug in that I think the compiler is behaving as intended. But it's surely a documentation bug. – David Heffernan Oct 31 '15 at 08:34
  • Which one ? The x86 compiler or the x64 compiler ? They both cannot be behaving as intended since they behave differently to each other, there is no documented difference between them and no reason to *expect* any such difference that would suggest a documentation error or omission. The "Int64 is not an ordinal type" does not stack up (it is explicitly documented as an ordinal type) and I have yet to see any valid reasoning as to why one would expect a difference in behaviours in this area w.r.t x86 vs x64 compilers. Yes, it's a side-issue, but an interesting one. – Deltics Nov 03 '15 at 00:24
  • @Deltics I don't think there's a difference in compiler behaviour between x86 and x64 here. The difference is in the **definition** of the Tag property's type (it's a 32bit integer type for x86 and a 64bit integer type for x64). – GabrielF Nov 05 '15 at 16:36
  • @Gabriel - This is the same circular argument that David used, treating behavior that deviates from the specification as valid simply because it is the behaviour. I wish I could close my bugs the same way! ;) Both x86 and x64 compilers reject Int64 as an ordinal in this case, but only one rejects NativeInt. Since NativeInt and Int64 are both documented as ordinal types this means that the x64 compiler has a bug in common with the x86 compiler (Int64 behaviour) and that the x64 compiler arguably has an additional bug arising from that bug combined with the Int64 implementation of NativeInt. – Deltics Nov 06 '15 at 05:01