Default and inline handling conflict

Summary

Default and inline can lead to unreliable code generation

System Information

  • Operating system: Any, but resulting in linking error on AIX OS only!
  • Processor architecture: Any
  • Compiler version: trunk
  • Device:

Steps to reproduce

Copy the tree files below:

muller@cfarm14:~/pas/check$ cat ufpsockets.pp
unit ufpsockets;

{$mode ObjFPC}{$H+}
{$TypedAddress on}
{$modeswitch advancedrecords}
{$COPERATORS ON}

interface

uses
  {$IfDEF WINDOWS}WinSock2, {$ENDIF}
  {$IFDEF unix}BaseUnix, termio, {$EndIf}
  ctypes, classes,
  sysutils, sockets, nullable, tuples;

type

  TAddressType = (atIN4, atIN6, atUnixSock);
  TNetworkAddress = record
    Address: String;
    AddressType: TAddressType;
  end;

function isIPv4Address(const Address: String): Boolean; inline;
function isIPv6Address(const Address: String): Boolean; inline;

function NetAddr(const Address: String): TNetworkAddress;inline;
function DefAddr : TNetworkAddress;

implementation

function isIPv4Address(const Address:String):Boolean;
var
  dummy:sockets.in_addr;
begin
  Result := TryStrToHostAddr(Address, dummy);
end;

function isIPv6Address(const Address:String):Boolean;
var
  dummy:in6_addr;
begin
  Result := TryStrToHostAddr6(Address, dummy);
end;

{$ifndef SHOW_FIX}
function DefAddr : TNetworkAddress;
begin
  DefAddr:=Default(TNetworkAddress);
end;
{$endif}

function NetAddr(const Address: String): TNetworkAddress;
begin
 Result := Default(TNetworkAddress);
  if isIPv4Address(Address) then
    Result.AddressType := atIN4
  else if isIPv6Address(Address) then
    Result.AddressType := atIN6
  else // Filenames can be pretty much anything
    Result.AddressType := atUnixSock;
  Result.Address := Address;
end;

{$ifdef SHOW_FIX}
function DefAddr : TNetworkAddress;
begin
  DefAddr:=Default(TNetworkAddress);
end;
{$endif}

end.
muller@cfarm14:~/pas/check$ cat ussockets.pp
{$MODE objfpc}{$H+}
{$COPERATORS ON}
{$inline on}

unit ussockets;

interface

uses
{$ifdef windows}
  winsock2, windows,
{$endif}
  SysUtils, Classes, ctypes, sockets, ufpsockets, tuples;

operator:=(const AStr:String):TNetworkAddress;

var
  DefaultAddress : TNetworkAddress;
  LocalHostAddress : TNetworkAddress;

implementation

operator:=(const AStr:String):TNetworkAddress;
begin
  Result := NetAddr(AStr);
end;

begin
  DefaultAddress:=Default(TNetworkAddress);
  LocalHostAddress:='127.0.0.1';
end.

muller@cfarm14:~/pas/check$ cat tsockets.pp
uses
  ussockets;

begin
  Writeln('LocalHost address: ',LocalHostAddress.Address);
end.

And compile for AIX target:

muller@cfarm14:~/pas/check$ ppcppc64 -Taix  -gl -B -altr tsockets.pp -XR/home/muller/sys-root/powerpc64-aix
Free Pascal Compiler version 3.3.1-18415-g045cd97d73 [2025/08/30] for powerpc64
Copyright (c) 1993-2025 by Florian Klaempfl and others
Target OS: AIX for PowerPC64
Compiling tsockets.pp
Compiling ussockets.pp
Compiling ufpsockets.pp
Assembling ufpsockets
Assembling ussockets
Assembling program
Linking tsockets
/home/muller/bin/powerpc64-aix-ld: ./ussockets.o:ussockets.pp:(.pr+0x1e): undefined reference to `TC_.UFPSOCKETS_..__zero_.UFPSOCKETS_.._TNETWORKADDRESS'
/home/muller/bin/powerpc64-aix-ld: ./ussockets.o:ussockets.pp:(.tc+0x0): undefined reference to `TC_.UFPSOCKETS_..__zero_.UFPSOCKETS_.._TNETWORKADDRESS'
An error occurred while linking
tsockets.pp(5,32) Error: Error while linking
tsockets.pp(5,32) Fatal: There were 1 errors compiling module, stopping
Fatal: Compilation aborted

Example Project

See above.

What is the current bug behavior?

Local constant TC.*zero.*TNETWORKADDRESS is reused in other unit, but considered as local in first unit because the first use of Default function is in a non-inlined function DefAddr.

If you reverse the order of the two functions, the compilation is successful:

muller@cfarm14:~/pas/check$ ppcppc64 -Taix  -gl -B -altr tsockets.pp -XR/home/muller/sys-root/powerpc64-aix  -dSHOW_FIX
Free Pascal Compiler version 3.3.1-18415-g045cd97d73 [2025/08/30] for powerpc64
Copyright (c) 1993-2025 by Florian Klaempfl and others
Target OS: AIX for PowerPC64
Compiling tsockets.pp
Compiling ussockets.pp
Compiling ufpsockets.pp
Assembling ufpsockets
Assembling ussockets
Assembling program
Linking tsockets
111 lines compiled, 0.4 sec

What is the expected (correct) behavior?

The compiler needs to be fixed to always consider this constant to be global if it can appear in any code that can be inlined.

Assignee Loading
Time tracking Loading