When a procedure has both a prototype (DCL-PR) and an interface (DCL-PI),
the interface must match the prototype, position by position: the same number of
parameters, the same parameter types, the same passing mode (by reference,
VALUE, or CONST), and the same OPTIONS(...) flags. A type-count mismatch is
reported as a compile error; a passing-mode mismatch is reported as TRN3008 and
an OPTIONS mismatch as TRN3009. Parameters are compared positionally - the
PR and PI parameter names need not be the same, only their declared
attributes.
Every prototyped call is also checked for argument count. The call must pass all
parameters except trailing OPTIONS(*NOPASS) ones: passing fewer arguments than
the number of mandatory parameters is reported as TRN3011, and passing more
arguments than the prototype declares is reported as TRN3012. Arguments are
matched to parameters positionally.
OPTIONS(*NOPASS) parameters must form a trailing run: once a parameter specifies
OPTIONS(*NOPASS), every parameter after it in the same prototype or interface must
also specify OPTIONS(*NOPASS). A non-*NOPASS parameter following a *NOPASS one
is reported as TRN3013.
A parameter declared OPTIONS(*NOPASS) may be omitted by the caller (along with
every parameter after it). Inside the procedure, %PARMS returns the number of
arguments the caller actually passed - not the declared maximum - so the body can
guard reads of optional parameters:
DCL-PR sub;
a PACKED(5:0) CONST;
b PACKED(5:0) CONST OPTIONS(*NOPASS);
END-PR;
sub(10); // b omitted
DCL-PROC sub;
DCL-PI *N;
a PACKED(5:0) CONST;
b PACKED(5:0) CONST OPTIONS(*NOPASS);
END-PI;
DSPLY ('P=' + %CHAR(%PARMS)); // P=1
END-PROC;
The same rule applies to a main program's entry interface (*ENTRY / the top-level DCL-PI). There, %PARMS returns the number of parameters actually passed on the call that activated the program - the equivalent of the PSDS *PARMS field - which may be fewer than the declared count when trailing OPTIONS(*NOPASS) parameters are omitted. A program with a three-parameter entry interface (two OPTIONS(*NOPASS)) called with one argument sees %PARMS = 1; called with three, %PARMS = 3. (A program instantiated and run directly, rather than through an RPG EXTPGM call, has no caller-supplied count and so reports the declared parameter count.)
%PASSED(paramName) returns *ON if the named OPTIONS(*NOPASS) parameter was
actually passed by the caller on this invocation, and *OFF otherwise. It is the
recommended alternative to comparing %PARMS >= %PARMNUM(param):
IF %PASSED(b);
DSPLY 'b was passed';
ENDIF;
A parameter declared OPTIONS(*OMIT) may be passed *OMIT by the caller. Inside
the procedure, %ADDR of an omitted parameter compares equal to *NULL (and
%OMITTED returns *ON), so the body can detect the omission:
DCL-PR sub;
a PACKED(5:0) CONST;
b PACKED(5:0) OPTIONS(*OMIT);
END-PR;
sub(10 : *OMIT); // b omitted
DCL-PROC sub;
DCL-PI *N;
a PACKED(5:0) CONST;
b PACKED(5:0) OPTIONS(*OMIT);
END-PI;
IF %ADDR(b) = *NULL;
DSPLY 'OMITTED';
ENDIF;
END-PROC;
A character parameter declared OPTIONS(*VARSIZE) accepts a character argument
shorter than the parameter's declared length. Inside the procedure the parameter
is still seen at its declared length - %LEN(s) returns the prototype length,
not the length of the argument that was passed, and a shorter argument is
blank-padded to the declared width. The procedure cannot learn the
actual argument length from %LEN; pass the length as a separate parameter when
the callee needs it:
DCL-PR sub;
s CHAR(20) OPTIONS(*VARSIZE);
END-PR;
DCL-S short CHAR(5);
short = 'ABCDE';
sub(short); // a CHAR(5) argument into a CHAR(20) param
DCL-PROC sub;
DCL-PI *N;
s CHAR(20) OPTIONS(*VARSIZE);
END-PI;
DSPLY ('V=' + %SUBST(s : 1 : 5)); // V=ABCDE (the bytes the argument provided)
DSPLY ('L=' + %CHAR(%LEN(s))); // L=20 (the declared length, not 5)
END-PROC;
OPDESC and CEEDOD
When a prototype or interface specifies the OPDESC keyword, the caller passes
operational descriptor metadata alongside each argument. The callee can query
this metadata - specifically the actual length, descriptor type, and data type - via the ILE bindable API CEEDOD:
DCL-PR showLen OPDESC;
str CHAR(50) CONST OPTIONS(*VARSIZE);
END-PR;
showLen('Hello');
DCL-PROC showLen;
DCL-PI *N OPDESC;
str CHAR(50) CONST OPTIONS(*VARSIZE);
END-PI;
DCL-PR CEEDOD EXTPROC('CEEDOD');
parmNum INT(10) CONST;
descType INT(10);
dataType INT(10);
descInfo1 INT(10);
descInfo2 INT(10);
dataLen INT(10);
fc CHAR(12) OPTIONS(*OMIT);
END-PR;
DCL-S descType INT(10);
DCL-S dataType INT(10);
DCL-S descInfo1 INT(10);
DCL-S descInfo2 INT(10);
DCL-S dataLen INT(10);
CEEDOD(1 : descType : dataType : descInfo1 : descInfo2 : dataLen : *OMIT);
// dataLen = 5 (actual argument length, not the prototype's 50)
END-PROC;
OPDESC is supported on both DCL-PR and DCL-PI in free-format and
fixed-format. In fixed-format C-specs, CALLB(D) is the equivalent of OPDESC
on a prototype - the (D) operation extender passes operational descriptors
for the PARM variables, producing the same descriptor metadata that a
prototyped OPDESC call does. CEEDOD returns:
| Parameter | Meaning |
|---|---|
descType |
Descriptor type (2 = element) |
dataType |
Data type (2 = character) |
descInfo1 |
CCSID (0 = job default) |
descInfo2 |
Reserved (0) |
dataLen |
Actual byte length of the passed argument |
A character parameter passed by CONST or VALUE may be declared
OPTIONS(*TRIM) or OPTIONS(*RIGHTADJ). *TRIM is valid on both fixed-length
and varying-length (VARCHAR) character parameters; *RIGHTADJ applies to a
fixed-length parameter. The transformation is applied to the argument at the call
site before it is passed:
OPTIONS(*TRIM)passes the argument with leading and trailing blanks removed (as if%TRIMwere applied), then padded to the parameter length.OPTIONS(*RIGHTADJ)right-adjusts a shorter argument within the parameter's declared length (blanks on the left).
DCL-PR showTrim;
p CHAR(10) CONST OPTIONS(*TRIM);
END-PR;
DCL-PR showRadj;
p CHAR(10) CONST OPTIONS(*RIGHTADJ);
END-PR;
showTrim(' hi '); // procedure receives 'hi' (blanks removed)
showRadj('hi'); // procedure receives ' hi'
*TRIM applies to fixed- or varying-length character parameters passed by CONST
or VALUE (and to an OPTIONS(*STRING) pointer); *RIGHTADJ applies to
non-varying character parameters passed the same way. Any other kind of parameter
is a compile-time error (TRN3005 for *TRIM, TRN3006 for *RIGHTADJ). They combine with the other
options via colons, e.g. OPTIONS(*NOPASS:*TRIM).
OPTIONS(*CONVERT)
A non-varying character parameter passed by CONST or VALUE may be declared
OPTIONS(*CONVERT). The call site then accepts an argument of any type - the
value is converted to character exactly as %CHAR renders it, and then assigned
to the parameter, blank-padded to its declared length.
DCL-PR show;
value CHAR(30) CONST OPTIONS(*CONVERT);
END-PR;
DCL-S d DATE INZ(D'2026-03-17');
DCL-S amount PACKED(7:2) INZ(-1234.56);
show(d); // procedure receives '2026-03-17'
show(amount); // procedure receives '-1234.56'
show(12345); // procedure receives '12345'
What the procedure receives, by argument kind:
| Argument | Received |
|---|---|
DATE |
ISO form, e.g. 2026-03-17 |
TIME |
ISO form, e.g. 14.05.09 |
TIMESTAMP |
2026-03-17-14.05.09.123456 - all six fractional digits |
PACKED / ZONED |
no leading zeros, no leading blank, decimal point at the declared scale, minus sign only when negative |
INT / UNS |
the digits, with a minus sign only when negative |
| character | unchanged |
Every result is left-justified and blank-padded to the parameter's declared
length. Without OPTIONS(*CONVERT), passing a non-character argument to a
character parameter is a compile-time error (TRN3010).
The option combines with the others via colons, e.g. OPTIONS(*CONVERT:*NOPASS).
OPTIONS(*STRING)
A basing-pointer parameter passed by CONST or VALUE may be declared
OPTIONS(*STRING). The call site then accepts a character argument and passes
the address of a null-terminated temporary copy of it. The procedure reads the
value back with %STR.
The temporary is terminated from the character value as passed. A
fixed-length CHAR argument therefore contributes its full blank-padded width,
while a VARCHAR contributes only its current length:
DCL-PR showStr VARCHAR(30);
p POINTER VALUE OPTIONS(*STRING);
END-PR;
DCL-S fld CHAR(10) INZ('ABC');
DCL-S vfld VARCHAR(10) INZ('XY');
showStr(fld); // %STR(p) is 'ABC ' - length 10, blanks kept
showStr(vfld); // %STR(p) is 'XY' - length 2
showStr('LIT'); // %STR(p) is 'LIT' - length 3
Combine it with *TRIM to strip leading and trailing blanks before the value is
terminated:
DCL-PR showTrim VARCHAR(30);
p POINTER VALUE OPTIONS(*STRING:*TRIM);
END-PR;
showTrim(fld); // %STR(p) is 'ABC' - length 3
Passing an argument that is already a pointer (a pointer variable, %ADDR(...),
or *NULL) passes that pointer through unchanged - no temporary is built.
The parameter must be a basing pointer passed by CONST or VALUE; anything
else is a compile-time error (TRN3007). The option is available in both
fixed-format D-specs and free-format DCL-PR/DCL-PI.