In this case, I'm not sure that the problem is whether the control variable is local or global.
The problem is global vs local but, the compiler can only go so far to enforce the rule: "for" index variables cannot be modified by user code. Even though that's the rule, the compiler has many, hard to impossible to avoid, limitations when it comes to enforcing it.
The following example is identical to the previous one, except that the control variable is a free-standing integer, not an integer field in a record.
In this case, no problem.
program Project1;
var
T : integer;
begin
for T:=1 to 10 do
writeln(T);
end.
A perfectly reasonable example but, when it comes to enforcing the rule "the index should not be modified by user code" there are problems with it. In a multi-threaded program, since the index is a global variable, there is nothing that prevents the other thread from modifying it. The solution is: "don't allow the index to be a global variable" but, fully enforcing it would cause the example you presented above to no longer compile, forcing the programmer to create a function with a local "T" variable in order to implement a trivial program. Not desirable since it is cumbersome.
Consider the following program:
program Project1;
type
TMyRect=record
X,Y : integer;
end;
procedure A1();
var
r : TMyRect;
i : integer absolute r; { dirty trick }
p : pinteger; { more dirty tricks }
begin
for i := 1 to 10 do
begin
writeln(i);
writeln(r.X);
end;
readln;
p := @i; { aliasing }
for i := 1 to 10 do
begin
{ cause an infinite loop }
writeln(i);
writeln(r.X);
//if i = 5 then i := 1; { this won't compile }
if i = 5 then p^ := 1; { but this will }
end;
readln; { will never get here }
end;
begin
A1();
readln;
end.
CAUTION: the above code causes an infinite loop (which is something that is not supposed to be possible when using a "for" loop.)
The compiler wants to enforce the rule "no changes to the index variable by user-code" but, its ability to enforce it is rather limited, it tries but, that's really all it can do.
As far as why it doesn't accept r.X as an index is because that would make it more difficult for the compiler to figure out if "x" is being modified in the loop (think about what would happen in a variant record, it would easy to overlay some other variable on top of "x" and change that one instead - similar to what "p" does in the above example.)
bottom line: the compiler tries to enforce the rule "no user changes to the index variable" but, its ability to enforce it is limited, as a result, there are things it won't allow (such as r.X as the index) to give itself a better chance but, that's all it is, a better _chance_.
Technically, there is no difference between using "i" or "r.X", both are offsets to an ordinal type (integer in this case.)
HTH.