Skip to main content

Shadowing Rules

In CODESYS, you are generally allowed to use the same identifier for different elements. For example, a POU and a variable can be named the same. However, you should avoid this practice in order to prevent confusion.

Example 306. Example

Negative example: In the following code snippet, a local function block instance has the same name as a function:

In such a case as this, it is unclear whether the instance or the function is called in the program.

FUNCTION YYY : INT
;
END_FUNCTION

FUNCTION_BLOCK XXX
;
END_FUNCTION_BLOCK

PROGRAM PLC_PRG
VAR
    YYY : XXX;
END_VAR
YYY();
END_PROGRAM


Compiler behavior when shadowing

The compiler does not report any errors or warnings if the same identifier is used for different elements. Instead, the compiler searches the code in a specific order for the declaration of the identifier. If a declaration is found, then the compiler does not search for any other declarations elsewhere. If other declarations do exist, then they are "shadowed" for the compiler. The following section describes the shadowing rules (that is, the search order that the compiler uses when searching for the declaration for identifiers). The section "Ambiguous access and qualified access" provides ways to prevent ambiguous access and bypass shadowing rules.

How to prevent shadowing

To make sure that names are always unique, you should follow naming conventions, such as certain prefixes for variables.

For more information, see: Identifier Designation

Naming conventions can be checked automatically using the static code analysis of CODESYS. Static code analysis could also detect the duplicate use of the name YYY and report it as an error.

Also through the consistent use of the attribute qualified_only for enums and global variable lists and by using qualified libraries non-unique situations can be avoided.

To make sure that a POU of the same name in the Devices view is not called when a POU in the POUs view is called, the operator __POOL should be prepended when the name of the POU is called.

Example: svar_pou := __POOL.POU();

Search order in the application

When the compiler encounters a single identifier in the code of an application, it searches for the corresponding declaration in the following order:

  1. Local variables

    1. Local variables of a method

    2. Local variables in the function block, program, or function, and in any base function blocks

    3. Local methods of the POU

  2. Global variables in the application, if the qualified_only attribute is not set in the variable list where the global variables are declared

    1. Global variables in the application, if the qualified_only attribute is not set in the variable list where the global variables are declared

    2. Global variables in a parent application, if the qualified_only attribute is not set in the variable list where the global variables are declared

    3. Global variables in referred libraries when neither the library nor the variable list requires qualified access

  3. POU or type names

    1. POU or type names from the application (that is, names of global variable lists, function blocks, and so on)

    2. POU or type names from a parent application

    3. POU or type names from a library

  4. Libraries

    1. Namespaces of locally referred libraries and libraries that are published by libraries

  5. Global variables in the POUs view, unless the qualified_only attribute is set in the variable list where they are declared

    1. Global variables in the POUs view, unless the qualified_only attribute is set in the variable list where they are declared

    2. POU or type names from the POUs view (that is, names of global variable lists, function blocks, and so on)

    3. Libraries from POUs

Tip

Libraries that are inserted in the Library Manager of the POUs view are mirrored in the Library Manager in all applications in the project with the appropriate placeholder resolution. These libraries then form a common namespace with the libraries in the application. Therefore, there is no shadowing of libraries in the pool by libraries in the application.

Search order in the library

When the compiler encounters a single identifier in the code of a library, it searches for the corresponding declaration in the following order:

  1. Local variables

    1. Local variables of a method

    2. Local variables in the function block, program, or function, and in any base function blocks

    3. Local methods of the POU

  2. Global variables

    1. Global variables in the local library, if the qualified_only attribute is not set in the variable list where the global variables are declared

    2. Global variables in referred libraries when neither the library nor the variable list requires qualified access

  3. Libraries

    1. POU or type names from the local library (that is, names of global variable lists, function blocks, and so on)

    2. POU or type names from a referred library

    3. Namespaces of locally referred libraries and libraries that are published by locally refereed libraries

Ambiguous access and qualified access

Despite these search orders, ambiguous access can still occur. For example, this is the case when a variable with the same name exists in two global variable lists that do not require qualified access. Such a case is reported by the compiler as an error (for example: Ambiguous use of the name XXX).

This kind of ambiguous usage can be made unique by means of qualified access, for example by accessing via the name of the global variable list (example: GVL.XXX).

Qualified access can also always be used to avoid shadowing rules.

  • The name of the global variable list can be used to uniquely access a variable in the list.

  • The name of a library can be used to uniquely access elements in the library.

  • The THIS pointer be used to uniquely access variables in a function block, even if a local variable with the same name exists in a method of the function block.

To find the declaration location of an identifier at any time, use the Edit → Browse → Go to Definition command. This can be especially helpful if the compiler produces an apparently obscure error message.

Searching in instance paths

The search orders described above do not apply to identifiers that exist as components in an instance path or to identifiers that are used as inputs in calls.

For access of the following type yy.component, it depends on the entity described by yy where the declaration of component is searched for.

If yy denotes a variable with a structured data type (that is, type STRUCT or UNION), then component is searched for in the following order:

  • Local variables of the function block

  • Local variables of the base function block

  • Methods of the function block

  • Methods of the base function block

If yy denotes a global variable list or a program, then component is searched for in this list only.

If yy denotes a namespace of a library, then component is searched for in this library exactly as described in the section above "Search order in the library".

Only in the second instance does the compiler decide whether access to the found element is granted (that is, whether the variable is only locally accessible, or whether a method is private). If access is not allowed, an error is issued.