There’s one table with all identifiers identifying types.
And there’s one table with all identifiers identifying routines.
As far as that goes, the compiler writer could choose to do it that way but, the most common way (because it's the most convenient) is to have only _one_ table which, among other things has the identifier name (usually an id) and it type (also an id) then, there is the scope table, which is a list of references into that previous table which tracks which identifiers (and their types) are valid/known in that scope.
When the compiler encounters "procedure ProcedureName", the "ProcedureName" is added to the scope that was started by "ProcedureA" and creates a _new_ scope for "ProcedureName" because procedure parameters are one scope below the procedure name, that's what allows a parameter name to have the same name as the procedure yet, be considered a different identifier and also makes the procedure's local variables be in the same scope as the procedure parameters (as they should be.)
In the test program I posted, when the compiler looks for what "ProcedureName" means, it should be finding the definition from "Procedure ProcedureName" which identifies "ProcedureName" as a procedure, not a data type. For this reason it should not accept "ProcedureName" as a data type identifier since it isn't one.
While parsing the formal parameters of the procedure ProcedureName after the colon the compiler expects a type identifier, thus (only) looks up the type identifier table. At that point, however, the routine ProcedureName is neither declared (= forward declaration) nor defined (= parsing of the begin…end frame).
It is declared and, it finds it. Proof of that is the message from the compiler stating that it expected a semicolon ";" when an attempt is made to use ParameterA in a statement, it knows ParameterA is a procedure, specifically a "ProcedureName".
Are you saying, all identifiers have to be unique and can’t be both a label and a type identifier at the same time?
No. In this particular case, I'm saying that "ParameterName" does not identify a data type therefore it is invalid to use it as if it were one.
I know that this is a very minor niggle, but on the principle that when a programmer has a hard time finding a bug it's usually because he's looking in the wrong place I'd suggest replacing your //{$DEFINE CALL_A} and similar with { DEFINE CALL_A} etc., just in case different compilers or different FPC modes are getting confused by the continued presence of <comment marker><literal dollar>
Your suggestion is welcome. I just want to mention that this is just a simple little test. If the compiler gets confused with those defines, that would be another bug. <chuckle>