According to Section 18.104.22.168 of the standard, an identifier declared in the parameter list of a function of a function has block scope, which terminates at the } closing the function definition. That means we need to create an environment at the beginning of the parameter list and discard it at the end of the function definition.
The problem is that when the beginning of the parameter list is encountered there is no way to tell whether the declarator is part of a function definition or not.
If the declarator is part of a nested declaration, then its parameter list is a function prototype and the environment can be discarded at the end of the parameter list.
If the declarator is at the outermost level of an external declaration, then its parameter list may be the parameter list of a function and the environment cannot be discarded at the end of the parameter list. It must be discarded at the end of the declaration.
A particular external declaration may or may not have a declarator specifying a parameter list. The current environment should be discarded at the end of an external declaration if and only if the declaration actually has a parameter list.
The necessary information can be provided by a variable that distinguishes four possible states:
Arbitrary nesting is handled without a stack by incrementing the current value of DeclarationState by IsNested: A value greater than or equal to IsNested indicates the nested state, but the state at the top level is also preserved. This is the reason that DeclarationState cannot be declared as a variable of type DeclarationStateValues; with arbitrary nesting, DeclarationState takes on values that are not valid DeclarationStateValues.