The scope of parameters and local variables starts at the beginning of the procedure block and ends at the end of the procedure block. Where their visibility starts is not the same.
How a compiler implements scopes is completely irrelevant.
Make stuff up all you want but, the fact remains, in the code you showed the compiler declares a "duplicate identifier" because the parameters and the local variables are in the same scope. This has absolutely nothing to do with how it is implemented.
From one Pascal compiler to another there will be differences in the implementation but, they will ALL declare there is a duplicate identifier in that code because, again and again and again ad nauseam, parameters and local variables are in the same scope. If they weren't, they wouldn't be duplicates, even Homer Simpson can figure that out. (There might be an exception which is, a compiler that includes code from you or maybe Thaddy, in which case I would suspicious about its implementation of boolean operator precedence too.. LOL)
Maybe this will help you (probably won't but religions claim miracles happen, so just in case):
procedure foo1(x: Integer);
var
{ the "x" below is in a different scope than the parameter "x" }
rec : record
x : Double; { NO duplicate identifier }
end;
begin
end;
procedure foo2(x: Integer);
var
{ the variable "x" is in the same scope as the parameter "x" }
x: Double; { duplicate identifier - what a surprise!!!... LOL }
begin
end;
in foo1, the identifier "x" is in a different scope than the parameter "x", as a result of that, no duplicate identifier is emitted by the compiler.
in foo2, the compiler emits a duplicate identifier message, the difference ? in foo1, "x" is not in the same scope as the parameter "x". Really, really simple. Comprende ?
if not comprende yet, read this paragraph from "Compiler Construction" (page 7):
5 Symbol Table Handling
A symbol table maps the names in a program to their attributes such as their types and their addresses. It consists of several subtables (scopes), one for every module, procedure and record type. As a consequence of single pass compilation, which Wirth preferred in his later compilers, the scopes can be organized in a stack-like way. A new scope is opened at the top of the stack whenever the parser encounters a procedure, and it is closed when the parser has finished with this procedure.
In case you "no comprende" yet, that means, one scope per procedure block.