It’s an ABAP mystery!

Suppose you see this kind of crash in SAP:

GETWA_NOT_ASSIGNED is an error familiar to most ABAP programmers. It is caused by an access to a field symbol which has not yet been assigned, for example:

1
2
3
4
5
6
7
8
9
DATA ls_mara TYPE mara.


FIELD-SYMBOLS <not_assigned> TYPE mara.

<not_assigned>-matnr = 'Value'.


ASSIGN ls_mara TO <not_assigned>.

The field symbol <not_assigned> does not initially point to anything, and trying to use it like a normal variable before any memory has been assigned to it will crash the program with a GETWA_NOT_ASSIGNED error.

An aside about field symbols: one might wonder why ABAP has both references/pointers (see “Limitations Not Expressible in the Type System” for examples of references) and field symbols. The answer is that a reference in ABAP is an actual type, in the sense that it can appear in method parameters (IMPORTING imr_mara TYPE REF TO mara) and in table definitions (STANDARD TABLE OF REF TO mara), whereas a field symbol just references the memory owned by another variable but one cannot pass a field symbol between modules or into a method, because a field symbol just looks like an instance of the referenced variable type. Ie. a field symbol of a MARA structure just looks like TYPE mara in ABAP. One cannot pass a field symbol into a method as an IMPORTING parameter with the expectation that the caller can alter the value of the referenced variable; instead, a CHANGING parameter would need to be used, because a field symbol behaves like a normal MARA variable would. The only cases where one must use a field symbol in ABAP are when one needs to access the fields of a structure dynamically, or when a generic reference/pointer needs to be dereferenced.

This time, however, the cause is not so obvious. The place where the code crashed looks like the following:

The first thing that sticks out is that there are no field symbols anywhere in the subroutine: all field symbol names must be surrounded with less-than and greater-than symbols, like <FIELD_SYMBOL_NAME>, so they would be easily recognizable.

The second point of interest is more subtle, but no less interesting. Here’s the full source code of the function group:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
FUNCTION-POOL zfunction_group.


FUNCTION zfunction_module.
*"------------------------------------------------------------------
*"*"Global Interface:
*"  TABLES
*"      MARA STRUCTURE MARA
*"------------------------------------------------------------------

  PERFORM subroutine.

ENDFUNCTION.


FUNCTION zanother_function_module.
*"------------------------------------------------------------------
*"*"Local Interface:
*"------------------------------------------------------------------

  PERFORM subroutine.

ENDFUNCTION.


FORM subroutine.

  IF mara-matnr IS INITIAL.
  ENDIF.

ENDFORM.

How can this be valid ABAP, given that nowhere in this program is neither a work area nor a variable called MARA defined such that it would be accessible in the scope of the subroutine? The only place where a MARA variable occurs is as a parameter of one of the functions, but those are only accessible inside the function module, right? Yet the program ran until the GETWA_NOT_ASSIGNED error occurred.

How can this code compile without issues, and how can we get a field symbol error when no field symbols are used anywhere in the program?

Global and Local Interfaces

The comments in the function modules hint at what is going on here, and taking a look at the function module attributes makes it clear:

ZFUNCTION_MODULE has the subtly named “Global” checkbox active. The F1 documentation for it reads as follows:

Global interface ID

You mark this field if the interface of the function module was made known globally through Edit -> Interface -> Globalize Parameters.

In this case, all the parameters that are fully typed and are defined for value transfer are treated as if global data objects with the same name were declared in the top-include - that is, they are visible in the entire function group and keep their value when the function module is exited. When the function module is called, all the parameters of a global interface are initialized and they get their defalut value.

All the other parameters are treated as if data objects of the same name were declared in the top Include (for table parameters, these are two in each case - one for the table body, one for the header line). But they can only be used during execution of the function module. This means they are visible in the entire function group, but they can only be accessed during execution of the function module. If such a parameter is accessed outside of actual execution of the function module, you get a runtime error GETWA_NOT_ASSIGNED with the meaning “Field symbol is not yet assigned” (such parameters are implemented internally through field symbols to which a data object is assigned during execution of the function module).

In the function group, no global data objects with the same name - such as a parameter of a global interface - may be created. If several function modules of a function group have global interfaces, parameters of the same name must be defined identically.

Note

The use of global interface parameters is obsolete and, in the new function modules, interfaces should generally not be globalized.

So, this attribute transforms the parameters of the function module, which are normally only valid in the function module scope, into global variables in the function group. Depending on how they are typed (TYPE vs. LIKE), they are either always available, or only available when the function group has been accessed through the correct function module.

Table parameters are typed using LIKE, which means that MARA is only available when ZFUNCTION_MODULE is called. Calling ZANOTHER_FUNCTION_MODULE, which does not have the matching MARA table parameter, and does not even have a global interface, means that the MARA field is not actually linked to any variable. This, in turn, results in a GETWA_NOT_ASSIGNED error, because the parameters are transparently globalized using field symbols.

Because these errors are only detectable during runtime, and because the GETWA_NOT_ASSIGNED error cannot be caught and handled, global function module interfaces become a likely, hard-to-detect source for crashes. And even though the globalized parameters are implemented using field symbols, one cannot inspect whether they are assigned like one would inspect normal field symbols:

Last but not least, globalized parameters break the rules that normally apply to ABAP variable scopes, allowing code that looks obviously wrong to compile. This was actually the thing that first really caught my interest in this case.

One could also consider this a cautionary example of what happens when it is not impossible to represent illegal states and actions in code.