3

Delphi.

Why

type 
  myInt = Integer;
  myNewInt = -2147483648..2147483647;

var 
  a: myint;
  b: myNewInt;

begin
  a := b;
end;

It is compiled normally though types formally different - one is declared here, another undertakes from other module. And if

uses 
  windows;

type
   _FILETIMEA = record // Copy from windows.pas
     dwLowDateTime: DWORD;
     dwHighDateTime: DWORD;
   end;

var 
  x: _FILETIMEA;
  y: windows._FILETIME;

begin
  x := y;
end;

Will cause a compilation error (in line x:=y; [DCC Error] ... E2010 Incompatible types: 'windows._FILETIME' and '_FILETIMEA'), though type _FILETIMEA = Windows._FILETIME ?

Ken White
  • 123,280
  • 14
  • 225
  • 444
Gu.
  • 1,947
  • 4
  • 30
  • 49

2 Answers2

9

Delphi doesn't support duck typing. You have 2 different records. They just look a like, but they are 2 different types for the compiler. If you want to assign the two variables you have to type cast them what is possible because they have the same size.

x := _FILETIMEA(y);
Andreas Hausladen
  • 8,081
  • 1
  • 41
  • 44
  • 6
    It's not "very sad". It's a very good thing. It avoids lots of bugs and mistakes in your code. – Ken White Dec 16 '11 at 23:04
  • 1
    You've explained by `x := y` is invalid, but you haven't explained why `a := b` *is* valid. – Rob Kennedy Dec 16 '11 at 23:26
  • @RobKennedy: a and b are primitive types. And for those the assignment operator is definied. By overloading the _FILETIMEA "Implict" operator, the Windows._FILETIME could be made assignable. – Andreas Hausladen Dec 17 '11 at 09:48
  • @Gu: If you are interested in doing `Duck Typing` with Delphi please head to this [post](http://stackoverflow.com/q/9497708/744588). – menjaraz Feb 29 '12 at 10:59
7

Because you're not doing the same thing. :) Delphi is strongly typed (there aren't many exceptions - almost everything has a specific type).

type 
  myInt = Integer;
  myNewInt = -2147483648..2147483647;

There is nothing incompatible about these types. They're both the same type (integer), and support the same range of values. There's no conflict about allowing the assignment of one to the other.

// Your unit name
unit GuTypes;

Type
 _FILETIMEA = record // Copy from windows.pas
   dwLowDateTime: DWORD;
   dwHighDateTime: DWORD;
 end;

This is a new type, defined by your code (even though it's identical to the one in Windows.pas, it's not the same). It's GuTypes._FileTimeA, which is not the same as Windows._FileTimeA.

This is actually a good thing, because it allows different units to use the same typenames without conflict; you can prefix with the unit name as a "namespace" to clarify which one you mean to use. For instance, if you have yours defined in GuTypes, and Windows has one declared there, in a third unit MyAppCode, you can do this safely:

var
  GFileTime: GuTypes._FILETIMEA;
  WFileTime: Windows._FILETIMEA;

Even if one of the types changes, your code is safe from side effects, because the two types can't accidentally be interchanged.

You can typecast to force the assignment to work, but that's usually a bad idea; it defeats the whole purpose of using a strongly typed language. Typecasting tells the compiler "I'm smarter than you, and I know this is wrong but I want you to ignore it and do it anyway."

A better solution would be either to declare your variable as you did the y in your example (Windows._FILETYPEA), or to declare a compatible type (type TMyFileTimeA = Windows._FILTETIMEA;).

See the XE2 Wiki Pages on Type Compatibility and Identity. Look specifically at the Assignment Compatibility section.

Ken White
  • 123,280
  • 14
  • 225
  • 444
  • Your reason for `a := b` being valid is that they have the same size and the same range of values. Doesn't that same description apply to the records? – Rob Kennedy Dec 16 '11 at 23:25
  • Not according to the page I linked to, or to the compiler. The compatibility listing specifically says `compatible ordinal types`, which `myInt` and `myNewInt` are; it doesn't say anything about `records defined in different units that happen to contain the same fields and types in the same order of declaration`. :) Delphi's (loose) support for what other languages call `namespaces` includes type differentiation between types declared in those namespaces. – Ken White Dec 16 '11 at 23:35
  • 3
    See also [Structured Types](http://docwiki.embarcadero.com/RADStudio/en/StructuredTypes.html), in the section entitled `Records (traditional)`; see the last code snippet and paragraph that follows it, just before `Variant Parts in Records` begins. It specifically defines a localized `var s: record...` and states "... separately declared records of this kind will not be assignment-compatible, even if their structures are identical.", which seems to apply to the examples posted in the question about the record declarations. – Ken White Dec 16 '11 at 23:48
  • The ordinal types does not have to be the same size. An `integer` can be assigned a `byte`. Doing the revese is also possible, but may render a compiler warning. – LU RD Dec 17 '11 at 00:14
  • If you have `range checking` and `hints and warnings` enabled, the compiler will alert you in the case of assigning an integer to a byte (if, of course, the integer value is too large to assign to a byte). In the case of the question's example, though, no hint or warning will be generated. (And I think you have the compiler warning backward - a warning will be generated if you assign an integer to a byte, but not if you assign a byte to an integer, unless I'm missing something.) – Ken White Dec 17 '11 at 00:18
  • Semantics, sorry if I was not clear. I think you understood. Point is ordinal types does not have to match size. – LU RD Dec 17 '11 at 00:25
  • From the link I posted about `Assignment Compatibility`, the types are compatible if `T1 and T2 are compatible ordinal types`; that was the point I was making. Good catch about not having to be the same size, though. I'll edit to fix it. :) – Ken White Dec 17 '11 at 00:28