Pacific Blue Software Logo

Delphi Hint H2443: Inline Function Has Not Been Expanded

Delphi Hint H2443: Inline Function Has Not Been Expanded

The hint looks like this:

H2443 Inline function 'TFDParam.SetAsLargeInt' has not been expanded
because unit 'FireDAC.Stan.Param' is not specified in USES list

It is only a hint. The code compiles and runs correctly. The compiler is telling you that a function marked inline was called as an ordinary function instead of having its body copied into your code, and it names the unit you need to add to make the copy possible.

Why It Happens

When a function is marked inline, the compiler tries to replace each call with the body of the function. That body may use types, functions or constants from other units. To copy the body into your unit, the compiler must be able to resolve every one of those names from your unit - which means each unit the body depends on has to be in your unit's uses clause. If one is missing, the compiler falls back to a normal call and issues H2443 naming the missing unit.

The usual FireDAC case: you use TFDQuery from FireDAC.Comp.Client, but the parameter class TFDParam, whose property setters are inline, is declared in FireDAC.Stan.Param.

unit Params_Without;

interface

uses
  FireDAC.Comp.Client;

procedure FillParams(AQuery: TFDQuery; const ACount: Integer);

implementation

procedure FillParams(AQuery: TFDQuery; const ACount: Integer);
var
  I: Integer;
begin
  for I := 1 to ACount do
  begin
    AQuery.Params[0].AsLargeInt := I;      // H2443 here
    AQuery.Params[1].AsString := 'Name';   // and here
  end;
end;

end.

Delphi 13 gives four hints for those two lines - not two:

H2443 Inline function 'TFDParams.GetItem' has not been expanded because unit 'System.Classes' is not specified in USES list
H2443 Inline function 'TFDParam.SetAsLargeInt' has not been expanded because unit 'FireDAC.Stan.Param' is not specified in USES list
H2443 Inline function 'TFDParams.GetItem' has not been expanded because unit 'System.Classes' is not specified in USES list
H2443 Inline function 'TFDParam.SetAsString' has not been expanded because unit 'FireDAC.Stan.Param' is not specified in USES list

Params[0] goes through the inline getter TFDParams.GetItem, which needs System.Classes; the setters need FireDAC.Stan.Param. Code that uses ParamByName instead of Params[n] only gets the second kind.

The Fix

Add the unit the hint names. It can go in the implementation section's uses clause if nothing in the interface needs it.

unit Params_With;

interface

uses
  System.Classes, FireDAC.Stan.Param, FireDAC.Comp.Client;

procedure FillParams(AQuery: TFDQuery; const ACount: Integer);

implementation

procedure FillParams(AQuery: TFDQuery; const ACount: Integer);
var
  I: Integer;
begin
  for I := 1 to ACount do
  begin
    AQuery.Params[0].AsLargeInt := I;
    AQuery.Params[1].AsString := 'Name';
  end;
end;

end.

No hints. Note that the hint names one unit at a time: after you add one, a second hint can appear for another inline function on the same line, as with GetItem above. Build again until they are all gone.

What It Costs

The difference is the cost of one call per use - small, and only noticeable in tight loops. Both units above were compiled with optimisation on (Win64) and timed setting the two parameters 10,000,000 times each, five runs of 2,000,000:

About 4%. For most code that is not worth worrying about, but the fix is one line in the uses clause, and clearing the hints means the ones that matter are not lost among them.

When You Write the Inline Function

The same rule applies to your own code, and it affects the units that call yours. This function uses Power from System.Math:

unit Shapes;

interface

function CircleArea(const ARadius: Double): Double; inline;

implementation

uses
  System.Math;

function CircleArea(const ARadius: Double): Double;
begin
  Result := Pi * Power(ARadius, 2);
end;

end.

Any unit that calls it without using System.Math itself gets the hint:

unit Area_Report;

interface

function TotalArea(const ACount: Integer): Double;

implementation

uses
  Shapes;

function TotalArea(const ACount: Integer): Double;
var
  I: Integer;
begin
  Result := 0;
  for I := 1 to ACount do
    Result := Result + CircleArea(I);   // H2443 - needs System.Math
end;

end.

Moving System.Math to the interface uses clause of Shapes does not help. That was tested: the hint still appears in Area_Report, because only the calling unit's own uses clause counts. The choices are:

Summary


PBS Delphi - Inline Functions and the Uses Clause


Back to Articles for Developers
Back to Articles on Delphi
Delphi Warnings W1050, W1057 and W1058 Explained
PBS Delphi Logger - Thread-safe logging with multiple outputs

If you found this useful, then please consider chipping in.
Chip In with PayPal
QR Code to chip in Please chip in if helpful