Since Delphi 2009, string is UnicodeString and Char
is a 2-byte WideChar. Code written before then, or library code that still uses
ShortString, AnsiString and AnsiChar on purpose, produces
three warnings over and over:
Below is what each one means, which ones point at a real problem, and fixes that keep the
code fast and keep ShortString and AnsiChar where they are wanted.
Every result quoted was measured with Delphi 13, on both Win32 and Win64.
A Pascal set holds at most 256 values, so a set of characters is always a set of
AnsiChar. Testing a Char against one gives W1050:
function IsDigit(const C: Char): Boolean;
begin
Result := C in ['0'..'9']; // W1050
end;
The warning reads as if the character is cut down to one byte, so that #$0130
(capital I with a dot, low byte $30) would count as the digit '0'. In
current Delphi that does not happen. A test of all 65,536 characters against literal sets,
untyped constants, typed constants and set variables found no false matches on Win32 or
Win64 - the compiler checks that the character is below 256 before it looks in the set.
The real danger is the usual way people silence the warning:
if AnsiChar(C) in ['0'..'9'] then // no warning, but wrong
A typecast of a Char variable to AnsiChar keeps only the low byte.
Across all 65,536 characters this gave 2,550 false matches, the first being
#$0130. The warning goes away and a real bug arrives in its place.
The fixes that are both quiet and correct:
// Any set, since Delphi 2009
Result := CharInSet(C, ['0'..'9']);
// A simple range - no set at all
Result := (C >= '0') and (C <= '9');
CharInSet is an inline function in System.SysUtils that returns
False for anything above 255 and tests the set otherwise. It does not exist before
Delphi 2009, so code that must still compile on older versions needs a
{$IFDEF UNICODE} around it or a fallback of its own.
Speed is not a reason to choose either way. In a test of 131 million lookups, Win64 took
336 ms with in and 256 ms with CharInSet; Win32 took 52 ms and
59 ms.
Where the character really is an AnsiChar - a character from a
ShortString or AnsiString - there is no warning and nothing to fix.
Leave that code alone.
If W1050 appears on a type declaration such as TCharSet = set of AnsiChar,
look for an alias. Old compatibility units sometimes contain this:
type
AnsiChar = Char; // left over from an old compatibility unit
type
TCharSet = set of AnsiChar; // W1050 here
With that alias in scope, every AnsiChar in every unit that uses it is really a
2-byte Char - in a test, SizeOf(AnsiChar) was 2. The set itself is
still 32 bytes, because a set holds 256 values whatever the element type, so nothing looks
wrong until you notice the warning on the declaration. Every variable, parameter and record
field declared as AnsiChar is twice the size its author meant, and every
AnsiChar overload is really a Char overload. Remove the alias for
desktop compilers. If the unit is shared with TMS WEB Core, which has no AnsiChar
at all, keep the alias for that compiler only:
{$IFDEF PAS2JS}
type
AnsiChar = Char;
{$ENDIF}
W1057 means the compiler inserted a conversion you did not write. Nothing is lost - it is telling you that a conversion runs every time this line runs. Two common sources:
Str is an old Turbo Pascal routine whose output is always a
ShortString. Given a string, the compiler converts behind the
scenes:
function NumToStr(const ANum: Int64; const ALen: Integer): string;
var
I: Integer;
begin
Str(ANum:ALen, Result); // W1057
for I := 1 to Length(Result) do
if Result[I] = ' ' then
Result[I] := '0';
end;
Do the work in a ShortString and convert once, explicitly:
function NumToStr(const ANum: Int64; const ALen: Integer): string;
var
I: Integer;
S: ShortString;
begin
Str(ANum:ALen, S);
for I := 1 to Length(S) do
if S[I] = ' ' then
S[I] := '0';
Result := string(S);
end;
The padding loop now edits a buffer on the stack instead of a reference-counted string, and the conversion happens once. Over 5 million calls the second version took 305 ms against 329 ms - about 7% faster. The results were identical for every value from -100,000 to 100,000.
Result := Result + C; // C: AnsiChar - W1057
Result := Result + string(C); // same result, no warning
Use string(C), not Char(C). They are not the same. The implicit
conversion and string(C) translate the byte through the code page, so on a
Windows-1252 system byte $80 becomes the euro sign, #$20AC.
Char(C) keeps the byte value and gives #$0080. Over all 256 byte
values, string(C) matched the implicit conversion every time and
Char(C) differed for 27 of them.
W1057 on a call often means the overload you expected does not exist. A library with
LeftStr overloads for ShortString, AnsiString and
string may only declare the first two inside {$IFDEF}s. When they are
switched off, every ShortString call goes to the string version and
converts both ways. Calling Copy directly avoids the question, because
Copy works natively on every string type. The same applies to Pos
with an AnsiChar and an AnsiString: Pos(AnsiString(ACh),
ALine) keeps the search in AnsiString.
W1058 is W1057 in the dangerous direction - from string to
ShortString or AnsiString:
function ShortCode(const AName: string): ShortString;
begin
Result := Copy(AName, 1, 3); // W1058
end;
Two things can be lost. Characters that do not exist in the code page become
?, and a ShortString keeps at most 255 characters. Write the cast only
after deciding that is acceptable for this value:
Result := ShortString(Copy(AName, 1, 3));
If you cannot say for certain that the text is short and single-byte, the right fix is to
keep it as string.
#0 to #$FFFF tested with seven forms of
in (literal ranges, literal lists, untyped constants, typed constants, variables
of TSysCharSet and of set of AnsiChar, and CharInSet)
on Win32 and Win64: no false matches and no misses.AnsiChar(C) in ['0'..'9']: 2,550 false
matches on both platforms.AnsiChar values converted to string three ways
(implicitly, with string(C) and with Char(C)) on a Windows-1252
system.Char in [...]: harmless in current Delphi; change to
CharInSet or a range test to clear it. Never cast to AnsiChar to
silence it.AnsiChar = Char alias.string.
Please donate if helpful