Parameters and local variables are in the same scope [...]
Why can Bar access Foo's parameter X but not its local variable Y then?
You know the answer to that: because Y is defined _after_ procedure Bar. As I have no doubt you know, Pascal requires all identifiers to be defined before they can be accessed.
Here is your code slightly modified to illustrate some cases (Note that "Y" is still in Foo but it's declared sooner):
procedure Foo(X: Integer); // ---------+
var
Y : String;
procedure Bar(W: Integer); // Foo: |----------+
var // Intf. | Bar: |--------+
Z : Double; // (Params) | Intf. | Bar: |
X : byte; { masks (or shadows if you prefer) the parameter X }
begin // | (Params) | Impl. |
{"X" -> Foo.X} // | | (Body) |
{"Y" is not accessible} // | | |
X := 355; { will emit a warning if X (in bar) exists, won't otherwise }
{ --------------- }
Y := ''; { access Y in Bar }
{ --------------- }
end; // |----------+--------+
//var // |--------+
// Y: String; // | Foo: |
begin // | Impl. |
{Compiles just fine} // | (Body) |
end; // ---------+--------+
Now Bar can access Y without any problem.
In the above, the local variable "X" in Bar masks (or shadows if you prefer) Foo's parameter "X". Here is a little experiment I'd like you to do: compile the above code and you'll get a warning stating that the value of "X" is out of range (355 is larger than what fits in a byte), that proves that the compiler is using the definition "X : byte". After that, comment out the definition "X : byte" and recompile. Note that the warning will be gone proving that it is the parameter "X" that is being accessed.
The local "X : byte" and parameter "X : integer" are most definitely NOT in the same scope but, "W" and "Z" are most definitely in the _same_ scope. the same way that the parameter "X" and local variable "Y" are also in the same scope (just in case, a different scope than the one that contains "W" and "Z".)
Scoping is very nicely explained in PBH's book as well as a simple yet effective and fully functional implementation. I've recommended PBH's book for years precisely because it focuses on the concepts, gives clear explanations and fairly simple implementations that are easy to understand and they work.
To be fair, he did cut a few corners here and there but not in concepts. That's fair because he clearly states in the introduction, it's a book meant as a basic introduction to language grammars and compiler construction. It's an introductory course.
Also and likely very important in this discussion, in Pascal the scope resolution implementation always accounts for nested scopes but
that doesn't mean that in the code:
procedure foo(x: Integer);
var
x: Double;
begin
end;
that the parameter "x" and local variable "x" are in different scopes,
they are not, they are in the same scope. However, there is no doubt in my mind that the code that handles that simple case is the very same code that handles nested scopes since "procedure foo" is nested in the program, therefore defines a nested scope.
IOW, it is completely incorrect to conclude that because the code can handle nested scopes that somehow parameter "x" and local variable "x" are different scopes.
Again: parameter "x" and local variable "x" are in the same scope. which is why the compiler emits a "duplicate identifier" message.