← board

TStringList lookups use three different comparisons, two wrong

Measured — pxx vs FPC 3.2.2

CaseSensitive defaults to False in both, and that default was already right; what disagreed was whether the lookups honoured it.

sl.Add('Alpha'); sl.Add('beta'); sl.Add('GAMMA');
before FPC
IndexOf('alpha') (default, insensitive) -1 0
IndexOf('ALPHA') -1 0
IndexOf('BETA') -1 1
IndexOf('gamma') -1 2
IndexOf('Alpha') exact 0 0
sl.Add('Key=val');  sl.CaseSensitive := True;
before FPC
IndexOfName('key') 0 -1
Values['key'] 'val' ''
IndexOfName('Key') exact 0 0

So IndexOf was too strict and IndexOfName/Values too lax, and each was wrong exactly where the other was right.

Cause — three mechanisms

path compared with honoured CaseSensitive?
TStringList.Find / Sort / sorted Add CompareStrings yes
TStrings.IndexOf Get(i) = S no — always sensitive
TStrings.IndexOfName (and Values through it) SameText(...) no — always insensitive

CompareStrings was declared on TStringList, so the two lookups inherited from TStrings could not reach it and each hardcoded a comparison instead. TStringList.IndexOf delegates to inherited IndexOf when the list is unsorted, which is how the sorted and unsorted paths of the same method came to disagree: sorted went through Find (correct), unsorted through =.

FPC does not have this split — its CompareStrings is virtual on TStrings precisely so every lookup shares it.

Fix — delete cases, do not add them

CompareStrings moves up to TStrings as a virtual, defaulting to CompareText (case-insensitive, FPC's default for a bare TStrings). TStringList keeps its body and marks it override. TStrings.IndexOf and TStrings.IndexOfName then route through the virtual.

Net effect: one comparison mechanism where there were three, and TStringList.IndexOf's unsorted path is fixed without being touched — it calls inherited IndexOf, which now dispatches back to TStringList's override. The diff removes a SameText and a =; it adds no branch.

The stale interface comment on the Name=Value block ("Name matching is CASE-INSENSITIVE, as FPC's is") was true only by accident and is now covered by the virtual, which follows the flag as FPC does.

Gate

The two tables above match FPC on every row, plus the sorted/unsorted and CaseSensitive True/False cross-product for IndexOf (all four combinations verified), and a 20-row TStringList sweep — Sort, CommaText, Values, Names, Insert/Delete/Clear, quoted CommaText parsing — unchanged and identical to FPC. make lib-test green.

Log