Legacy Features and Migration
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
This page is written against ISO/IEC 1989:2023 (edition 3, January 2023; an upcoming 4th edition is in ballot), in which
the obsolete elements of COBOL 85 are gone; against IBM Enterprise COBOL for z/OS 6.5 (PTFs through September 2026) on
z/OS 3.2, which still accepts most of them; and against GnuCOBOL 3.2 (28 July 2023, the latest release), with which every
program on it was compiled with cobc -x -Wall and run on 2026-10-20 (and again with -std=ibm where it differs).
z/OS facts are labelled "z/OS; not run here" and checked against the Language Reference and Programming Guide.
This is the only page in the guide that shows obsolete elements. Every other page uses the structured form. Each item below is labelled old code you may meet, then shows the modern form next to it: a maintenance programmer needs to read such code and to replace it safely, not to write it. The pages that teach the modern forms are PERFORM and Structured Programming, Conditions, IF and EVALUATE, STRING, UNSTRING and INSPECT and Dates and Times; the strategy for deciding what to do with a legacy estate is on Modernization Strategies.
Where the book is outdated: Taulli’s Modern Mainframe Development (2022, chapter 4) presents GO TO alongside the
other control-flow forms and its migration discussion (chapters 12 and 13) stops at moving to the cloud. It does not say
that the 85 standard already classed ALTER and GO TO without a target as obsolete, that the 2002 standard removed
them, that Enterprise COBOL 6 no longer behaves like COBOL 4 on invalid data, or that the Enterprise COBOL 6 compiler
changed the code it generates. All three are covered below.
How the compilers flag obsolete elements
IBM’s Language Reference lists the elements that the 85 COBOL standard categorised as obsolete. The 2002 standard does not contain them, and the manual says that this does not imply IBM will remove them from a future release. The list for Enterprise COBOL for z/OS 6.5 is:
-
the
ALTERstatement andGO TOwithout a procedure-name; -
the
AUTHOR,INSTALLATION,DATE-WRITTEN,DATE-COMPILEDandSECURITYparagraphs and the comment entry; -
the
DATA RECORDS,LABEL RECORDS,MEMORY SIZE,MULTIPLE FILE TAPE,RERUNandVALUE OFclauses and theREVERSEDphrase; -
the
DEBUG-ITEMspecial register, debugging sections and theUSE FOR DEBUGGINGdeclarative; -
the
ENTERstatement, theSTOPliteral format ofSTOP, and the segmentation module; -
the figurative constant
ALLliteral with a length greater than one, when it is associated with a numeric or numeric-edited item.
EXAMINE, TRANSFORM, NOTE, ON and REMARKS do not appear in that list or anywhere in the 6.5 Language Reference
and Programming Guide: they belong to the older OS/VS COBOL language, which the 85 standard (and every IBM compiler since
VS COBOL II) did not carry forward, so a program that still uses them must be changed before it can move to Enterprise
COBOL 6. The 6.5 Programming Guide also shows NOCMPR2 as "always off" in the object-module signature table: the
compatibility option that once let VS COBOL II compile OS/VS COBOL programs no longer exists.
GnuCOBOL reports the 85-obsolete items itself. -Wall turns on -Wobsolete and -Warchaic, and each diagnostic names
the dialect (obsolete in GnuCOBOL, obsolete in IBM COBOL (lax)), so the same source gives different messages under
-std=ibm. Elements GnuCOBOL does not know are errors, with a note that the word "may exist in another dialect".
Old code you may meet
Identification paragraphs, LABEL RECORDS, DATA RECORDS (old code you may meet)
Before the 85 standard, documentation lived inside the program and files carried label information:
IDENTIFICATION DIVISION.
PROGRAM-ID. OLD1.
AUTHOR. CIRCULATION TEAM.
INSTALLATION. MAIN LIBRARY.
DATE-WRITTEN. 1987-03-02.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT LOANTRAN ASSIGN TO "loantran.dat"
ORGANIZATION IS LINE SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD LOANTRAN
LABEL RECORDS ARE STANDARD
DATA RECORD IS LOAN-LINE.
01 LOAN-LINE PIC X(20).
WORKING-STORAGE SECTION.
77 SWITCH-1 PIC 9 VALUE 0.
PROCEDURE DIVISION.
MAIN-PARA.
DISPLAY "OLD1 STARTED".
STOP RUN.
Compiled with cobc -x -Wall old1.cob it prints one warning per element, and with -std=ibm it still compiles and
runs (the dialect is lax about them):
old1.cob:3: warning: AUTHOR is obsolete in GnuCOBOL [-Wobsolete]
old1.cob:4: warning: INSTALLATION is obsolete in GnuCOBOL [-Wobsolete]
old1.cob:5: warning: DATE-WRITTEN is obsolete in GnuCOBOL [-Wobsolete]
old1.cob:14: warning: LABEL RECORDS is obsolete in GnuCOBOL [-Wobsolete]
old1.cob:15: warning: DATA RECORDS is obsolete in GnuCOBOL [-Wobsolete]
(Each real message is followed by a few lines of source context; they are omitted here.) The same program with
-std=ibm -Wall reports LABEL RECORDS is obsolete in IBM COBOL (lax) and DATA RECORDS is obsolete in IBM COBOL (lax)
and no more, because IBM accepts the identification paragraphs. A related OS/VS item, REMARKS, and the
MEMORY SIZE clause of OBJECT-COMPUTER, give REMARKS is obsolete in GnuCOBOL and MEMORY SIZE is obsolete in GnuCOBOL.
The modern form: delete LABEL RECORDS and DATA RECORDS (the system handles labels, the record name is the 01
under the FD), and replace the paragraphs with comments, or better with the source-control history and the file
header of the repository. In free format a comment is *>; in fixed format it is a * in column 7.
IDENTIFICATION DIVISION.
PROGRAM-ID. OLD1M.
* Circulation loan listing. Owner and history: see the
* repository, not this header.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT LOANTRAN ASSIGN TO "loantran.dat"
ORGANIZATION IS LINE SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD LOANTRAN.
01 LOAN-LINE PIC X(20).
WORKING-STORAGE SECTION.
PROCEDURE DIVISION.
MAIN-PARA.
DISPLAY "OLD1M STARTED"
STOP RUN.
ALTER and GO TO (old code you may meet)
ALTER changes, while the program runs, the target of a GO TO in another paragraph, which must contain nothing else.
The program text no longer says where control goes: you have to know which ALTER ran last.
IDENTIFICATION DIVISION.
PROGRAM-ID. ALTER1.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 PASS-NO PIC 9 VALUE 0.
PROCEDURE DIVISION.
MAIN-PARA.
PERFORM RUN-ONCE 2 TIMES.
STOP RUN.
RUN-ONCE.
ADD 1 TO PASS-NO.
PERFORM SWITCH-PARA THRU SECOND-STEP.
SWITCH-PARA.
GO TO FIRST-STEP.
FIRST-STEP.
DISPLAY "PASS " PASS-NO " TOOK THE FIRST BRANCH".
ALTER SWITCH-PARA TO PROCEED TO SECOND-STEP.
SECOND-STEP.
DISPLAY "PASS " PASS-NO " REACHED SECOND-STEP".
Compiled with cobc -x -Wall alter1.cob it prints one warning, and with -std=ibm the same warning reads
ALTER is obsolete in IBM COBOL (lax):
alter1.cob: in paragraph 'FIRST-STEP':
alter1.cob:17: warning: ALTER is obsolete in GnuCOBOL [-Wobsolete]
15 | FIRST-STEP.
16 | DISPLAY "PASS " PASS-NO " TOOK THE FIRST BRANCH".
17 > ALTER SWITCH-PARA TO PROCEED TO SECOND-STEP.
18 | SECOND-STEP.
19 | DISPLAY "PASS " PASS-NO " REACHED SECOND-STEP".
and when run, it prints:
PASS 1 TOOK THE FIRST BRANCH
PASS 1 REACHED SECOND-STEP
PASS 2 REACHED SECOND-STEP
On the second pass SWITCH-PARA jumps straight to SECOND-STEP, because the first pass changed it: the behaviour of
the paragraph depends on the history of the run, and a program that is called again starts in the state the previous call
left. The modern form is a switch, ideally with level 88 names
(see VALUE, INITIALIZE and Condition Names):
IDENTIFICATION DIVISION.
PROGRAM-ID. MODERN1.
* Circulation: the same two jobs as ALTER1 and GOTO1,
* written with structured statements only.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 PASS-NO PIC 9 VALUE 0.
01 BRANCH-FLAG PIC X VALUE "F".
88 FIRST-BRANCH VALUE "F".
88 SECOND-BRANCH VALUE "S".
01 TRAN-CODE PIC 9 VALUE 2.
01 A PIC 9 VALUE 1.
01 B PIC 9 VALUE 0.
PROCEDURE DIVISION.
MAIN-PARA.
PERFORM RUN-ONCE 2 TIMES
PERFORM DISPATCH-PARA
PERFORM NESTED-IF-PARA
STOP RUN.
RUN-ONCE.
ADD 1 TO PASS-NO
IF FIRST-BRANCH
DISPLAY "PASS " PASS-NO " TOOK THE FIRST BRANCH"
SET SECOND-BRANCH TO TRUE
END-IF
DISPLAY "PASS " PASS-NO " REACHED SECOND-STEP".
DISPATCH-PARA.
EVALUATE TRAN-CODE
WHEN 1 DISPLAY "LOAN"
WHEN 2 DISPLAY "RETURN"
WHEN 3 DISPLAY "RENEW"
WHEN OTHER DISPLAY "UNKNOWN CODE"
END-EVALUATE.
NESTED-IF-PARA.
IF A = 1
IF B = 1
DISPLAY "A AND B"
ELSE
CONTINUE
END-IF
DISPLAY "STILL IN THE IF"
END-IF
DISPLAY "AFTER THE IF".
Compiled with cobc -x -Wall modern1.cob (and with -std=ibm), no warnings, it prints the same two passes, then the
results of the next two sections:
PASS 1 TOOK THE FIRST BRANCH
PASS 1 REACHED SECOND-STEP
PASS 2 REACHED SECOND-STEP
RETURN
STILL IN THE IF
AFTER THE IF
GO TO without a procedure-name is the other half of the pair: GO TO. was written in a paragraph that an ALTER
filled in later. If you find one, there is an ALTER somewhere. Replace both together.
How to remove ALTER safely. Find every ALTER and the paragraph it names; list the possible targets of each; write a
test that drives each target (see Testing COBOL Programs); replace the
GO TO and the ALTER by an IF or EVALUATE on a switch; run the tests again. Do not combine this with other
changes.
GO TO DEPENDING ON and PERFORM THRU (old code you may meet)
GO TO DEPENDING ON is a computed jump. It is still valid in the 2002 and 2023 standards, so no compiler warns about
it, but it is the ancestor of EVALUATE and is replaced by it:
IDENTIFICATION DIVISION.
PROGRAM-ID. GOTO1.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 TRAN-CODE PIC 9 VALUE 2.
PROCEDURE DIVISION.
MAIN-PARA.
GO TO LOAN-PARA RETURN-PARA RENEW-PARA
DEPENDING ON TRAN-CODE.
DISPLAY "UNKNOWN CODE".
STOP RUN.
LOAN-PARA.
DISPLAY "LOAN".
STOP RUN.
RETURN-PARA.
DISPLAY "RETURN".
STOP RUN.
RENEW-PARA.
DISPLAY "RENEW".
STOP RUN.
Compiled with cobc -x -Wall goto1.cob, no warnings, it prints:
RETURN
The jump uses the number in TRAN-CODE; a value outside 1 to 3 falls through to the next statement ("UNKNOWN CODE"),
which is the behaviour to keep. The EVALUATE in DISPATCH-PARA in the modern program above does the same with
WHEN OTHER. Similarly, PERFORM THRU over a range of paragraphs makes the end of the range a hidden contract:
adding a paragraph in the middle changes what runs. Prefer PERFORM of one paragraph or of a SECTION. When you meet
a THRU range, find the exit paragraph (usually named ...-EXIT. and containing only EXIT.) and every GO TO that
targets it before touching anything.
NEXT SENTENCE (old code you may meet)
NEXT SENTENCE jumps to the statement after the next full stop. Inside a scope-terminated IF that is not the end of
the IF: it can skip statements that look as if they belong to it.
IDENTIFICATION DIVISION.
PROGRAM-ID. NEXTSENT1.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 A PIC 9 VALUE 1.
01 B PIC 9 VALUE 0.
PROCEDURE DIVISION.
MAIN-PARA.
IF A = 1
IF B = 1
DISPLAY "A AND B"
ELSE
NEXT SENTENCE
END-IF
DISPLAY "STILL IN THE IF"
END-IF
DISPLAY "AFTER THE IF".
IF A = 1 NEXT SENTENCE ELSE DISPLAY "NOT ONE".
DISPLAY "NEXT SENTENCE LANDED HERE".
STOP RUN.
Compiled with cobc -x -Wall nextsent1.cob it warns twice and, when run, prints one line:
nextsent1.cob: in paragraph 'MAIN-PARA':
nextsent1.cob:13: warning: NEXT SENTENCE is archaic in GnuCOBOL [-Warchaic]
nextsent1.cob:18: warning: NEXT SENTENCE is archaic in GnuCOBOL [-Warchaic]
NEXT SENTENCE LANDED HERE
(The real messages carry source context.) Neither "STILL IN THE IF" nor "AFTER THE IF" was shown, because the first
NEXT SENTENCE skipped to the full stop after AFTER THE IF. In the modern program the same shape uses CONTINUE, and
both lines are printed. Note that the IBM Language Reference says explicitly that NEXT SENTENCE combined with END-IF
does not pass control to the statement following END-IF, and it lists the combination among the things the 85
standard disallows. It remains a style hazard, not a removed feature: replace it by CONTINUE when you restructure.
EXAMINE and TRANSFORM (old code you may meet)
These OS/VS COBOL statements count, replace and translate characters. INSPECT replaced both (EXAMINE ... TALLYING is
INSPECT ... TALLYING, EXAMINE ... REPLACING is INSPECT ... REPLACING, TRANSFORM is INSPECT ... CONVERTING).
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAM1.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 TITLE-TEXT PIC X(8) VALUE "TOO COOL".
PROCEDURE DIVISION.
MAIN-PARA.
EXAMINE TITLE-TEXT REPLACING ALL "O" BY "0".
DISPLAY TITLE-TEXT.
STOP RUN.
The default GnuCOBOL dialect does not know the word, so cobc -x -Wall exam1.cob stops:
exam1.cob: in paragraph 'MAIN-PARA':
exam1.cob:8: error: syntax error, unexpected Identifier, expecting SECTION or .
6 | PROCEDURE DIVISION.
7 | MAIN-PARA.
8 > EXAMINE TITLE-TEXT REPLACING ALL "O" BY "0".
9 | DISPLAY TITLE-TEXT.
10 | STOP RUN.
exam1.cob:8: note: unknown statement 'EXAMINE'; it may exist in another dialect
Compiled with -std=ibm or -std=mvs, the same file compiles and prints T00 C00L: those dialects make EXAMINE a
reserved word and accept it. TRANSFORM TITLE-TEXT FROM "O" TO "0" compiles in the default dialect too and prints the same
text. Do not rely on that on z/OS: the Enterprise COBOL 6.5 manuals do not describe either statement, so assume an IBM
compile will reject them. The modern program:
IDENTIFICATION DIVISION.
PROGRAM-ID. INSPECT1.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 TITLE-TEXT PIC X(8) VALUE "TOO COOL".
01 O-COUNT PIC 9 VALUE 0.
PROCEDURE DIVISION.
MAIN-PARA.
INSPECT TITLE-TEXT TALLYING O-COUNT FOR ALL "O".
INSPECT TITLE-TEXT REPLACING ALL "O" BY "0".
INSPECT TITLE-TEXT CONVERTING "OL" TO "01".
DISPLAY O-COUNT " " TITLE-TEXT.
STOP RUN.
Compiled with cobc -x -Wall inspect1.cob (and with -std=ibm), no warnings, it prints:
4 T00 C001
The four is the count of capital O in TOO COOL (TALLYING runs first); REPLACING changes them to zero digits, and
CONVERTING "OL" TO "01" then turns the remaining letter L into 1.
NOTE, ENTER, ON and USE FOR DEBUGGING (old code you may meet)
-
NOTEbegan a comment sentence in the Procedure Division. Replace it with a real comment (*in column 7, or*>). GnuCOBOL reportsunknown statement 'NOTE'and, under-std=ibm, a syntax error. ANOTEswallows everything to the next full stop, so deleting only the first word changes the program. -
ENTERnamed a language (ENTER LINKAGE,ENTER COBOL) so that a statement in another language could follow. It is obsolete in the 85 standard.cobc -std=ibmsays'ENTER' is a reserved word, but isn't supported. Replace it with aCALLto the other-language routine (see Subprograms: CALL and Linkage and Interoperability with C, Java and Python). -
ON(ON 1 AND EVERY 5 DISPLAY "HEADER") ran a statement on the first and every n-th pass through the statement. It is not in the standard and not in Enterprise COBOL 6;cobcgivessyntax error, unexpected ON. Replace it with a counter and anIF(IF CNT = 1 OR FUNCTION MOD(CNT, 5) = 0). -
USE FOR DEBUGGINGintroduced a declarative that ran when a named item changed. GnuCOBOL accepts it silently by default, and-std=ibmsaysUSE FOR DEBUGGING is obsolete in IBM COBOL (lax); in the test used here compiling with-debugdid not run the declarative either, so do not count on it. The modern tools are theTESTcompiler option with the interactive debugger on z/OS, andcobc -debugwithgdborDISPLAYtracing in GnuCOBOL (see Debugging and Troubleshooting).
Two-digit years (old code you may meet)
ACCEPT ... FROM DATE returns six digits, YYMMDD. Programs written before 2000 kept the year in two digits, and the
shipped fix was a window: if the year is below 50 it is 20xx, otherwise 19xx. The bug moves to the year the window closes.
IDENTIFICATION DIVISION.
PROGRAM-ID. DATE1.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 OLD-DATE PIC 9(6).
01 NEW-DATE PIC 9(8).
01 DUE-YY PIC 99 VALUE 05.
PROCEDURE DIVISION.
MAIN-PARA.
ACCEPT OLD-DATE FROM DATE.
ACCEPT NEW-DATE FROM DATE YYYYMMDD.
DISPLAY "LENGTHS " FUNCTION LENGTH(OLD-DATE) " "
FUNCTION LENGTH(NEW-DATE).
IF DUE-YY < 50
DISPLAY "20" DUE-YY " (WINDOWED)"
END-IF
STOP RUN.
Compiled with cobc -x -Wall date1.cob, no warnings, it prints (the second line is the windowed date):
LENGTHS 6 8
2005 (WINDOWED)
The modern form keeps four digits end to end: ACCEPT ... FROM DATE YYYYMMDD or FUNCTION CURRENT-DATE
(see Dates and Times), a four-digit year in every file and copybook, and date
arithmetic with FUNCTION INTEGER-OF-DATE. Enterprise COBOL once had compiler-supported windowing (the DATEPROC and
YEARWINDOW options); those names do not appear in the 6.5 manuals, so on a current compiler the window must be explicit
in your own code or removed by widening the field. Widening a field changes the record length and every program that
shares the copybook, which is why the Y2K projects took years; do it with a copybook change and a full recompile.
The other obsolete elements, in one table
| Element | What it was | Modern replacement |
|---|---|---|
|
Documentation inside the program. |
Comments, source control, the compiler listing. |
|
Storage hint in |
Nothing; delete it. |
|
Tape and checkpoint features of the 1960s and 1970s. |
JCL/restart ( |
|
Stop and wait for the operator. |
|
Segmentation ( |
Overlay memory management. |
Delete; the system handles storage. |
|
Debug facility. |
Compiler |
|
Fill a numeric item with a pattern. |
|
Migrating from OS/VS COBOL, VS COBOL II and Enterprise COBOL 4.2 to 6.x
Moving up a compiler version is the most common legacy task on the mainframe, and it is not only a recompile. Three stages are in play: OS/VS COBOL and VS COBOL II programs (1970s to 1990s), Enterprise COBOL 3 and 4 (to 4.2), and the rewritten compiler of Enterprise COBOL 5 and 6. IBM’s Migration Guide is the authoritative checklist (Enterprise COBOL for z/OS 6.5 Migration Guide); the points below are the ones that repeatedly cause defects.
From OS/VS COBOL and COBOL II
-
The OS/VS-only statements (
EXAMINE,TRANSFORM,NOTE,ON,CURRENT-DATEandTIME-OF-DAYas special registers) must be rewritten as in the sections above. TheCMPR2compatibility option does not exist any more. -
The 85-obsolete elements still compile, as the list at the top shows. Do not remove them in the same step as the compiler move.
-
LABEL RECORDSand similar clauses are ignored; keep them out of new code.
From Enterprise COBOL 4.2 to 6.x: invalid data
Enterprise COBOL 6 generates different code for numeric items, and a program that has been running with invalid data for
years, because the older compiler happened to tolerate it, can change its output or abend with S0C7. The IBM text calls
the data "invalid": a DISPLAY or PACKED-DECIMAL item with a non-digit, a wrong sign code or invalid zone bits, which
a REDEFINES or a bad file can put there. Two options control the migration:
-
INVDATAtells the compiler not to assume that numeric data is valid: withNOINVDATA(the default) it generates the most efficient code and assumes valid data.INVDATA(NOFORCENUMCMP, CLEANSIGN)is the setting that most resembles the old compilers when you usedNUMPROC(NOPFD)orNUMPROC(PFD), andINVDATA(FORCENUMCMP, NOCLEANSIGN)is closest to the oldNUMPROC(MIG).INVDATAslows the program; IBM’s advice is to correct the data and go back toNOINVDATA. -
NUMCHECKgenerates implicit class tests on the sending fields ofDISPLAY,COMP-3and binary items, so that bad data is reported with a message (MSG) or abends (ABD) at the statement. Use it in test to find the invalid data, then remove it.
IBM’s quick-reference table for choosing the settings, in words:
| Coming from | Invalid data present? | Old setting | Setting on 6.2 and later |
|---|---|---|---|
Before COBOL 5 |
No |
|
|
Before COBOL 5 |
Yes |
|
|
Before COBOL 5 |
Yes |
|
|
COBOL 5 or later |
No |
|
|
COBOL 5 or later |
Yes |
|
|
COBOL 5 or later |
Yes |
|
|
IBM’s own example puts a blank in the middle of a PIC 9(4) item through a REDEFINES of PIC X(4) and shows that the
result of IF value = ZERO depends on the compiler, the optimisation level and the option. The same shape in the
Circulation scenario, run here with GnuCOBOL (which is a different compiler and uses its own rules, so this shows the
hazard, not the IBM result):
IDENTIFICATION DIVISION.
PROGRAM-ID. INV1.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 COPIES-RAW PIC X(4) VALUE "00 0".
01 COPIES-NUM REDEFINES COPIES-RAW PIC 9(4).
01 TOTAL-COPIES PIC 9(5) VALUE 10.
PROCEDURE DIVISION.
MAIN-PARA.
IF COPIES-NUM IS NUMERIC
DISPLAY "COPIES is numeric"
ELSE
DISPLAY "COPIES is not numeric: " COPIES-RAW
END-IF
IF COPIES-NUM = ZERO
DISPLAY "COPIES = ZERO is true"
ELSE
DISPLAY "COPIES = ZERO is false"
END-IF
ADD COPIES-NUM TO TOTAL-COPIES
DISPLAY "TOTAL-COPIES " TOTAL-COPIES
STOP RUN.
Compiled with cobc -x -Wall inv1.cob (and with -std=ibm), no warnings, it prints:
COPIES is not numeric: 00 0
COPIES = ZERO is true
TOTAL-COPIES 00010
The class test finds the problem and the comparison does not. The ADD quietly adds zero here; on z/OS with NOINVDATA
the same statement may abend, and which of these two happens depends on the compiler settings. The fix is always the same:
test with IS NUMERIC before using data from outside, and fix the producer. See
Arithmetic and COMPUTE and
USAGE and Numeric Representation.
Other changes to check
-
ARCHandTUNE. A program compiled for a higherARCHlevel does not run on older hardware. In 6.5,ARCH(11)is the default andARCH(10)was removed, whileARCH(15)andTUNE(15)target the z17. -
SMARTBIN. Adds binary metadata that IBM’s Automatic Binary Optimizer can use later (introduced in 6.4; aNAMESsuboption adds symbol names in 6.5). It is not supported withLP(64), and module size grows. See Compiling, Linking and Language Environment. -
Read and write behaviour. The Programming Guide points to "wrong length READ" messages for finding record-length errors that older compilers hid.
-
Program objects and AMODE. Programs compile into program objects (PDSE), not load modules in a PDS, and 64-bit AMODE interop has its own rules (see the Compiling page and Performance and Compiler Options).
-
Behaviour on incorrect programs. The Migration Guide section on error behaviour changes lists programs that used to work by accident.
The sequence that works: inventory the compilers and options in use; recompile with 6.x with the old NUMPROC and
INVDATA settings that match; run the regression tests or a parallel run (see
Testing COBOL Programs); turn on NUMCHECK in test; fix the data; go
to NOINVDATA; only then optimise with ARCH, TUNE and OPTIMIZE.
Moving code between compilers: IBM to GnuCOBOL (and back)
A program written for Enterprise COBOL can often be compiled by GnuCOBOL for development and testing on a PC. The
dialect switch is -std=ibm (and -std=ibm-strict to reject extensions); it changes the reserved-word list and a set of
configuration values, but it does not make GnuCOBOL an IBM compiler. The checks below cover the differences that
change results rather than just compile errors; the complete dialect story is on
Compilers and Dialects.
Collating sequence
z/OS compares characters in EBCDIC order: lower-case letters, then upper-case, then digits. GnuCOBOL on Linux or
macOS compares in ASCII order: digits, then upper-case, then lower-case. A comparison, a SORT or a table kept in key order
can therefore give a different answer. Compiled with cobc -x -Wall collate.cob, which declares an alphabet but does not use it:
native order: 1A sorts before A1
Naming it in OBJECT-COMPUTER with PROGRAM COLLATING SEQUENCE IS EBCDIC-ORDER (program COLLATE2, same declarations):
program collating sequence EBCDIC: A1 first
IDENTIFICATION DIVISION.
PROGRAM-ID. COLLATE2.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
OBJECT-COMPUTER. CIRC-HOST
PROGRAM COLLATING SEQUENCE IS EBCDIC-ORDER.
SPECIAL-NAMES.
ALPHABET EBCDIC-ORDER IS EBCDIC.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 CODE-A PIC X(2) VALUE "A1".
01 CODE-B PIC X(2) VALUE "1A".
PROCEDURE DIVISION.
MAIN-PARA.
IF CODE-A < CODE-B
DISPLAY "program collating sequence EBCDIC: A1 first"
ELSE
DISPLAY "program collating sequence EBCDIC: 1A first"
END-IF
STOP RUN.
-std=ibm gives the same two results. The clause applies to comparisons in the program, so use it when the data or
the expected order comes from z/OS, and test the order of any file that SORT or MERGE produces and a downstream program
reads (see SORT and MERGE).
COMP sizes and truncation
IBM stores PIC 9(1) to 9(4) binary items in two bytes, five to nine digits in four bytes and ten to eighteen in eight
bytes. GnuCOBOL’s default dialect also uses one byte for a single digit (binary-size: 1-2-4-8); -std=ibm uses 2-4-8.
The size matters in record layouts and for LENGTH, and so does what happens when a value is too big for the PICTURE:
IDENTIFICATION DIVISION.
PROGRAM-ID. SIZES.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 B1 PIC 9(1) COMP.
01 B4 PIC 9(4) COMP.
01 B5 PIC 9(5) COMP.
01 B9 PIC 9(9) COMP.
01 B10 PIC 9(10) COMP.
01 B18 PIC 9(18) COMP.
01 P7 PIC S9(7) COMP-3.
01 CNT PIC 9(4) COMP VALUE 0.
PROCEDURE DIVISION.
MAIN-PARA.
DISPLAY "COMP 9(1) " FUNCTION LENGTH(B1)
DISPLAY "COMP 9(4) " FUNCTION LENGTH(B4)
DISPLAY "COMP 9(5) " FUNCTION LENGTH(B5)
DISPLAY "COMP 9(9) " FUNCTION LENGTH(B9)
DISPLAY "COMP 9(10) " FUNCTION LENGTH(B10)
DISPLAY "COMP 9(18) " FUNCTION LENGTH(B18)
DISPLAY "COMP-3 S9(7) " FUNCTION LENGTH(P7)
MOVE 12345 TO CNT
DISPLAY "12345 into 9(4) COMP: " CNT
STOP RUN.
Compiled with cobc -x -Wall sizes.cob it warns about the MOVE (value size exceeds data size) and prints the
default-dialect sizes below; with -std=ibm it has no warning and prints the second column.
| Item | Default dialect | -std=ibm |
|---|---|---|
|
1 byte |
2 bytes |
|
2 bytes |
2 bytes |
|
4 bytes |
4 bytes |
|
4 bytes |
4 bytes |
|
8 bytes |
8 bytes |
|
8 bytes |
8 bytes |
|
4 bytes |
4 bytes |
|
stores 2345 (truncated to the |
stores 12345 (the dialect sets |
The byte order is big-endian in both dialects, like IBM Z; COMP-5 is native (little-endian on x86). On z/OS the TRUNC
option decides what happens to the value and costs performance in the TRUNC(STD) mode; to make a GnuCOBOL test match
TRUNC(STD), compile with -fbinary-truncate (observed here: the value is then reduced to the digits the 2-byte item holds).
If you compare the results of the two compilers on arithmetic, compare these settings first.
The rest of the checklist
-
Source format. Fixed format with
CBL/PROCESSstatements on z/OS;cobcignores compiler directives it does not know and warns; check the-fformatoption (see Reference Format). -
Files.
ASSIGN TOnames a DD name on z/OS and a file name or environment variable in GnuCOBOL;LINE SEQUENTIALexists on z/OS only for UNIX files. Re-run the file status checks (see File Status Codes). -
Subsystems.
EXEC SQLandEXEC CICSdo not compile without a preprocessor or emulator (see Embedded SQL and Db2); Language Environment callable services (CEE...) are IBM-only. -
Intrinsic and runtime differences. GnuCOBOL 3.2 changed the
FUNCTION RANDOMalgorithm and the sizes ofBY VALUEarguments on 64-bit; neither equals the IBM result. -
Going the other way. A program developed with GnuCOBOL and
-std=defaultmay use extensions IBM lacks (TRANSFORM,LINE SEQUENTIALeverywhere,ACCEPT ... FROM ENVIRONMENT,--coverage); compile with-std=ibm-strictbefore you promote it.
Common mistakes
-
Changing the compiler and the code at once. Move the compiler first, with settings that mimic the old behaviour, then clean up.
-
Deleting
ALTERwithout tracing every target. The hidden state is the program. -
Turning on
INVDATAand leaving it on. It hides data defects and costs speed. Use it to buy time, not as the final setting. -
Testing on ASCII and trusting EBCDIC order. Test sorts and comparisons with the collating sequence of production.
-
Fixing two-digit years by widening one program. The copybook and every file layout have to change together.
References
-
Enterprise COBOL for z/OS 6.5 Language Reference (PDF), Obsolete language elements
-
Enterprise COBOL for z/OS 6.5 Programming Guide (PDF), INVDATA and NUMCHECK options
-
Enterprise COBOL for z/OS 6.5 Language Reference: INSPECT statement
-
Enterprise COBOL for z/OS 6.5 Language Reference: PERFORM statement
-
Enterprise COBOL for z/OS 6.5 Language Reference: EBCDIC and ASCII collating sequences
-
Taulli, Modern Mainframe Development (O’Reilly), consulted for the topics only