Currently, RPG has exactly two types of pointers:
Basing pointers
Procedure pointers
These pointer types hold no type information about the pointer's target. Currently, when defining a BASED variable, the compiler cannot verify that the variable's type matches the pointer's target and thus cannot emit a diagnostic if there's a mismatch. Likewise, when defining a prototype with a procedure pointer, the compiler cannot verify that the prototype's signature is correct. However, in most circumstances, the target type is static and it's necessary for all parts of a system manipulating a given pointer to agree on the pointer's target type.
I propose adding two new families of types to RPG: typed basing pointers and typed procedure pointers. The syntax I suggest is POINTER(type), where type is a keyword defining either a data type or a prototype template. This syntax would be allowed anywhere POINTER and POINTER(*PROC) are currently allowed. This syntax could be available in free-form code only. Examples of valid pointer types:
POINTER(CHAR(12)): Basing pointer to a CHAR(12).
POINTER(INT(10)): Basing pointer to an INT(10).
POINTER(LIKE(my_var)): Basing pointer to a variable with the same type as my_var. For example, if my_var has type CHAR(12), then POINTER(LIKE(my_var)) is equivalent to POINTER(CHAR(12)).
Keywords defined on my_var that affect the "type", such as CCSID or DIM, should also be considered as part of the pointer type. For example, if my_var has type CHAR(12) and keyword CCSID(*UTF8), then POINTER(LIKE(my_var)) should be equivalent to POINTER(CHAR(12) CCSID(*UTF8)). It would be acceptable if additional keywords like CCSID could not be specified directly inside the POINTER keyword and could only be specified indirectly using LIKE or LIKEDS.
Keywords that don't affect the "type", such as TEMPLATE, must not be considered as part of the pointer type.
POINTER(LIKEDS(my_ds)): Basing pointer to a data structure defined like my_ds.
POINTER(LIKEPR(my_prototype)): Procedure pointer to a procedure whose signature is the same as my_prototype.
POINTER(POINTER(INT(10)): Basing pointer to a basing pointer to an INT(10). (I would be fine if nested pointers like this were not allowed, as long as an alternative such as POINTER(LIKE(ptr_to_int)), where ptr_to_int has type POINTER(INT(10)), exists.)
The %ADDR and %PADDR built-in functions would be modified to yield typed pointers instead of untyped pointers. *NULL would be compatible with all typed pointer types.
The following ideas may interact with this idea:
IBMI-I-1051: Add template for function prototypes/Procedure Interfaces - There needs to be a way to define a procedure prototype without referring to any specific procedure. Allowing the TEMPLATE keyword on prototypes and introducing a LIKEPR or LIKEPROC keyword to allow defining a prototype with the same signature as another prototype (TEMPLATE or not, similar to LIKE/LIKEDS) would provide the necessary foundation for strongly-typed procedure pointers.
IBMI-I-2505: A new OPTION to force pointer declaration - Implicitly defined pointers would still be untyped. This new option would prevent accidental definition of untyped pointers.
It would be beneficial to extend pointer types to allow specifying the target type (both for data and procedures) for the following reasons:
The compiler would raise an error when defining a BASED variable with a type that doesn't match the basing pointer's target type (including keywords such as CCSID or DIM).
The compiler would raise an error when assigning pointers of incompatible types (e.g., when assigning a POINTER(CHAR(4)) to a POINTER(INT(10))).
The compiler would raise an error when defining a procedure prototype with EXTPROC that doesn't have the same signature as the procedure pointer.
The compiler could allow "pointer math" on typed basing pointers, as in C. For example, if variable p is a POINTER(INT(10)), then p += 1; would increment the pointer by 4, because the size of an INT(10) is 4 bytes. This would be useful in situations where the pointer points to an array rather than a single value.
The language could introduce ways to dereference typed basing pointers directly within expressions, without having to define a BASED variable. In C, this is done using the * prefix unary operator (e.g., *p) and the -> binary operator (e.g., p->f). In Pascal, this is done using the ^ suffix unary operator (e.g., p^). In RPG, this could be done with a new built-in function, let's call it %DEREF, or with new operators. Pascal's suffix operator has the benefit of not requiring as many parentheses as C's prefix operator. (For example, in C, *p.f is equivalent to *(p.f), not (*p).f. In Pascal, p^.f is equivalent to C's (*p).f and p.f^ is equivalent to C's *p.f.)
The language could allow calling a procedure through a typed procedure pointer directly, without having to define a prototype whose EXTPROC refers to the pointer.
Collectively, these additions would enable RPG programmers to use techniques such as dependency injection and dependency inversion more safely without requiring support for object-oriented programming or interfaces in the language.
For interoperability with existing untyped pointers, and to enable pointer type coercion when needed, the compiler should allow assigning any typed basing pointer to an untyped POINTER and assigning any typed procedure pointer to an untyped POINTER(*PROC). Assignments in the other direction should also be allowed. I would like to have some explicit syntax (e.g., with a built-in function) for casting an untyped pointer to a typed pointer, but that's not a hard requirement for me.
Addendum 2026-07-15: It would also be nice if typed basing pointers supported pointing to CONST values (e.g., an equivalent to const int * or int const * in C/C++). Currently, %ADDR is rejected on CONST parameters. With this change, %ADDR on a CONST parameter would produce a CONST pointer type. For example, if parameter X has type INT(10) and has keyword CONST, then %ADDR(X) would have type POINTER(INT(10) CONST). Pointers to CONST would allow reading the pointed-at value but not modifying it, just like with CONST parameters.
If this Idea is implemented, it is more likely that variables would be defined as usual with a new keyword indicating that the variable is actually a pointer to the specified type. An parameter to this keyword would indicate that number of levels of indirection. The compiler would automatically dereference the variable when it was used.
// Pointer to int(10)
dcl-s pI int(10) newkeyword;
// Pointer to pointer to int(10)
dcl-s pI int(10) newkeyword(2);
dcl-s i int(10);
i = pI; // automatically dereferences pI
- IBM Power Systems Development
- IBM Power Systems Development