Expected behaviour.
Taking the address of a string element prevents the "copy on write" for the string.
And by that they also circumvent making the "constant write accessible".
Use "UniqueString" as in the last example below.
In Detail.
The string '12345' is a constant. The variable "s" is not a constant, but the initial value is.
That initial value '12345' is (maybe depending on OS) in write protected memory.
Taking the address of @s[1] => takes the address of the initial data, in the write protected memory.
Taking the address, also looses the info that this is a string.
A string has the special property that the variable itself is a pointer, and that the data can be in protected memory. If you accessed this without typecasts, then that data would be copied (copy on write) to write-able memory.
If you had a "var c: char" variable, that issue could never arise. As the value (the char) is always in the variable (without internal pointer).
You can trigger the error much simpler:
And you can do your call, if you ensure the data is not in write-protected memory
s := s + IntToStr(Random(9));
Modify(PAnsiChar( @S[1] )^);
or
UniqueString(s);
Modify(PAnsiChar( @S[1] )^);
Mind that
var
S, S2: AnsiString;
begin
s := '1111' + IntToStr(Random(9));
s2 := s;
pchar(@s[1])^ := '2';
writeln(s);
writeln(s2);
Has the same issue.
Both strings will be modified.
Because again, taking the address prevents "copy on write". And again "UniqueString" before taking the address is the correct solution.