@Ryan,
The example you provided was useful.
Here is the _critical_ characteristic of "const" to always keep in mind: "const" only applies to the identifier that was made "const". In your example, it applies to the _parameter_ (r in this case) _and_ the compiler can only detect _direct_ conflicts. It cannot detect indirect conflicts (the great majority of compilers can't, it's not just FPC or Pascal.)
consider this modified example based on your code:
{$APPTYPE CONSOLE}
{$MODESWITCH ADVANCEDRECORDS ON}
program test;
type
PMyRec = ^TMyRec;
TMyRec = record
x: Integer; { note that "x" is not declared as "const" }
procedure SetX(v: Integer);
end;
var
r : TMyRec;
procedure xSetX(v : integer);
begin
r.x := v;
end;
procedure TMyRec.SetX(v: Integer);
begin
x := v;
end;
var
p : PMyRec = nil;
procedure TestRec(const r: TMyRec);
begin
r.SetX(100); { indirection }
writeln(r.x); { 100 }
xSetX(200); { indirection }
writeln(r.x); { 200 }
p := @r; { indirection }
p^.x := 300;
writeln(r.x); { 300 }
end;
begin
TestRec(r);
readln;
end.
In all cases, "r" is _not_ being assigned/modified _directly_ (which is what the compiler can detect), "r" is being modified indirectly and many, if not all, compilers would not be able to preserve the "const-ness" of the parameter. Actually, just about any language that allows pointer manipulation could not prevent modification through a pointer.
A crucial fact in the Pascal code is that the "const" is applied to "r", it is NOT applied to "x" which is where the value is actually stored. Some languages allow specifying "const" in the variable definition itself (just in case, I know of no language that allows mixing mutable and immutable in a record, IOW, no mixing allowed.) When "const" is applied to the variable's storage then the compiler can enforce it with reasonable ease. When "const" is applied to a parameter (instead of the storage) as done in Pascal then the compiler can only detect and enforce _direct_ attempts at changing the value. That's normal because attempting to detect those cases would be unreliable and, in some cases, would create a level of complexity that is totally unreasonable, e.g, the parameter could be passed down 100, 1000, 10,000 procedures before it is modified and it would be totally absurd for _any_ compiler to attempt tracking changes across procedures.
"const" works as intended: it prevents _direct_ modification, it does not prevent indirect modification.
If you feel like it, put this code above the "TMyRec.SetX" method,
Procedure Funproc(var p : TMyRec); begin end;
and add a call to Funproc in TestRec, you'll find that the compiler will not compile that code (which is as it should be.) IOW, the compiler is doing its job as it should.
HTH.