When the declared type of an actual parameter does not match the type of a formal var or out parameter, the compiler will issue an error message: “Types of actual and formal var parameters must be identical.”
Sometimes this occurs with older code that has switched to a newer
compiler version. The code might have used Integer
interchangeably with DWord and THandle. In
Delphi 2 and Delphi 3, that was no problem, but Delphi 4 introduced true
32-bit unsigned types, so DWord and THandle
were no longer synonymous with Integer. Calls to functions
declared to receive a var DWord, such as RegCreateKeyEx, but that were actually passed
variables of type Integer, would fail to compile in newer
versions of Delphi even though they compiled fine in older versions.
(This change in types can also trigger range-checking errors at run
time.)
The compiler compiles each function separately, so when one function calls another function, the compiler does not consider the contents of the called function when generating the call. It only looks at the types of the formal parameters and makes sure they are compatible with the types of the actual parameters.
Consider what could happen if the compiler accepted the following code:
Listing 2
Why types of actual and formal var parameters must be identical
procedure AcceptVarParameter(var Obj: TObject);
begin
Obj.Free;
Obj := TFont.Create;
end;
procedure CallOtherCode;
var
List: TList;
begin
List := TList.Create;
try
AcceptVarParameter(List);
{ At this point, a TFont object is stored in a TList variable, but there has been no type casting to produce this illegal situation. The following line will generate an exception since the TList.Clear method cannot operate on TFont objects. }
List.Clear;
finally
List.Free;
end;
end;
Type theory
Suppose that formal var parameter p is declared to have type T1, and actual parameter a is declared to have type T2. Since the function is implemented assuming it receive a value of type T1 in p, the compiler needs to ensure that all possible values of a are valid values of type T1. That is, T2 must be a subtype of T1. In addition, the caller is written to assume that it receives a value of type T2 in a when the function returns. Since the function is only implemented to return values of type T1, any value of type T1 also needs to be a valid value of type T2, so T1 must be a subtype of T2. In order for T1 and T2 to be subtypes of each other, they must be identical types. (Any type is considered to be a subtype of itself.)