Assembler XF requires a macro's local variable symbols to be declared with LCLA/LCLB/LCLC (or GBLA/GBLB/GBLC). as370 accepts an undeclared one, assigns it, and substitutes it. IFOX00 flags every use — IFO006 UNDEFINED VARIABLE SYMBOL, severity 8 — never substitutes it, and generates nothing.
So this is not only a missing diagnostic. The same source produces a different object module.
Reproducer
Ours:
* A macro SET symbol used without an LCLA/LCLB/LCLC declaration.
MACRO
&NAME UNDECL
&LOOSE SETC 'LOOSE'
&TIGHT SETC 'TIGHT'
LOOSEDC DC C'/&LOOSE/'
TIGHTDC DC C'/&TIGHT/'
MEND
T CSECT
UNDECL
END
|
IFOX00 (JOB02903) |
as370 |
| RC |
8 |
0 |
| messages |
8 × IFO006 UNDEFINED VARIABLE SYMBOL |
none |
| statements flagged |
8 |
0 |
&LOOSE in the model statement |
not substituted — the card reads DC C'/&LOOSE/' |
substituted → C'/LOOSE/' |
LOOSEDC / TIGHTDC |
not defined (cross-reference holds only T) |
defined |
| section length |
000000 |
00000E |
IFOX flags the cards twice: statements 4–7, inside the macro definition, before any call, and 11–14 in the expansion. So the check runs at definition time as well as at expansion time.
What IFOX does
erms.asm:15
GENERR ERR6,'UNDEFINED VARIABLE SYMBOL $ '
raised at ifnx1j.asm:860, in the dictionary lookup, with IBM's own comment naming the case:
VERRC MVC DSEVCD(D2),=AL1(SEV6,ERR6) FLAG UNDECLARED VAR SYMB
SEV6 EQU 8 (jermsgcd.asm:33).
Where as370 lets it through
as370.c:1477-1479 — SETA/SETB/SETC canonicalise the name and set_put it, with no check that anything declared it:
if (!strcmp(op, "SETA")) { long v = eval_seta(c, opnd); ... set_put(c, sn, nb); return 1; }
The declarations themselves are handled just above (:1471-1474): an array form goes into c->arrb, a scalar is set_put with its type's default. So a declared scalar exists in the store before its first assignment and an undeclared one does not — which is the whole test the SET path is missing.
Risk of enforcing it: measured, and it is nil
An instrumented build reported every first assignment to a SET symbol that no LCL/GBL declared:
| corpus |
modules |
with an undeclared SET symbol |
| ecosystem (libc370 743, rexx370, ufsd, ftpd, httpd, httplua, httprexx, lua370, mvsmf, lstring370, nsf370) |
826 |
0 |
| a local tree of real IBM MVS assembler, spread sample |
277 |
0 |
Nothing measured relies on the leniency — unsurprising for the second corpus, which was written for this assembler in the first place.
How it surfaced
Writing the fixture for #94. The first capture came back with 22 statements flagged and nothing else wrong: a fixture written against as370 is not automatically a fixture the guest will take, and the difference was exactly this. Recorded in tests/listref/README.md at the time.
Two halves to a fix
- The diagnostic is small: check at
SETA/SETB/SETC that the canonical name was declared, and at the reference site in vref. Severity 8, and IFOX checks inside the definition as well as in the expansion.
- The code effect is the larger half: IFOX leaves the reference unsubstituted and the statement generates nothing, so the section comes out empty. Matching that means the substitution path has to distinguish "undeclared" from "declared but null", which today it does not.
The first half alone already turns silent divergence into a loud one, and the measurement says no module pays for it. Worth doing even if the second half waits.
Assembler XF requires a macro's local variable symbols to be declared with
LCLA/LCLB/LCLC(orGBLA/GBLB/GBLC). as370 accepts an undeclared one, assigns it, and substitutes it. IFOX00 flags every use —IFO006 UNDEFINED VARIABLE SYMBOL, severity 8 — never substitutes it, and generates nothing.So this is not only a missing diagnostic. The same source produces a different object module.
Reproducer
Ours:
IFO006 UNDEFINED VARIABLE SYMBOL&LOOSEin the model statementDC C'/&LOOSE/'C'/LOOSE/'LOOSEDC/TIGHTDCT)IFOX flags the cards twice: statements 4–7, inside the macro definition, before any call, and 11–14 in the expansion. So the check runs at definition time as well as at expansion time.
What IFOX does
erms.asm:15raised at
ifnx1j.asm:860, in the dictionary lookup, with IBM's own comment naming the case:SEV6 EQU 8(jermsgcd.asm:33).Where as370 lets it through
as370.c:1477-1479—SETA/SETB/SETCcanonicalise the name andset_putit, with no check that anything declared it:The declarations themselves are handled just above (
:1471-1474): an array form goes intoc->arrb, a scalar isset_putwith its type's default. So a declared scalar exists in the store before its first assignment and an undeclared one does not — which is the whole test the SET path is missing.Risk of enforcing it: measured, and it is nil
An instrumented build reported every first assignment to a SET symbol that no
LCL/GBLdeclared:Nothing measured relies on the leniency — unsurprising for the second corpus, which was written for this assembler in the first place.
How it surfaced
Writing the fixture for #94. The first capture came back with 22 statements flagged and nothing else wrong: a fixture written against as370 is not automatically a fixture the guest will take, and the difference was exactly this. Recorded in
tests/listref/README.mdat the time.Two halves to a fix
SETA/SETB/SETCthat the canonical name was declared, and at the reference site invref. Severity 8, and IFOX checks inside the definition as well as in the expansion.The first half alone already turns silent divergence into a loud one, and the measurement says no module pays for it. Worth doing even if the second half waits.