https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127543
Bug ID: 127543
Summary: gcobol: an all-digit procedure name is mishandled —
ICE, or "never defined" for a name that is defined
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: cobol
Assignee: unassigned at gcc dot gnu.org
Reporter: peeterjoot at protonmail dot com
Target Milestone: ---
Created attachment 65651
--> https://gcc.gnu.org/bugzilla/attachment.cgi?id=65651&action=edit
run.sh referenced in report
A COBOL paragraph-name or section-name may consist entirely of digits.
`gcobol` accepts such a name at its definition but not at every reference
to it: depending on the shape, it crashes, or it rejects a reference to a
name the program defines a few lines earlier. Giving the same procedure
a name with one letter in it makes every case compile.
Found while giving `gcobol` a segmentation-free rendering of the NIST
COBOL-85 `SG/` programs, which name each section after its segment
number (`36 SECTION 36.`) - so once the priority number is gone, the
section is simply named `36`. It is not a rewriting artifact: the
reproducers below are hand-written, and the shortest is three lines.
## The rule this relies on
Both specifications carve procedure names out of the "must contain a
letter" rule for user-defined words, explicitly:
- **IBM Enterprise COBOL for z/OS 6.5 Language Reference**
(SC27-8713-04) p.12: "Most user-defined words (all except
**section-names, paragraph-names**, priority-numbers, and
level-numbers) must contain at least one alphabetic character."
- **ISO/IEC 1989:2023** 8.3.2.2 *User-defined words*: "With the
exception of **section-names, paragraph-names**, and level-numbers,
each user-defined word shall contain at least one basic letter or
extended letter."
- **ISO/IEC 1989:2023** 8.3.2.2.19 *Paragraph-name* NOTE: "The
paragraph-names '00123' and '123' are different paragraph-names." -
the standard itself uses all-digit spellings as examples of
paragraph-names. Section-names are the same class of word
(8.3.2.2.28 *Section-name*).
So `36 SECTION.` and `36.` are conforming, and `GO TO 36` / `PERFORM 36`
are conforming references to them.
## Versions tested
- `gcobol (GCC) 17.0.0 20260821 (experimental)` - built from trunk,
`/opt/gcc-trunk-20260821`
- `gcobol (Ubuntu 15.2.0-16ubuntu1) 15.2.0` - the distribution package
Host: x86_64 Linux. All cases are fixed-form and need no flags beyond
`-ffixed-form`.
## What each case does
`./run.sh` prints this table; `GCOBOL=/path/to/gcobol ./run.sh` picks the
compiler.
| case | gcobol 17.0.0 (trunk) | gcobol 15.2.0 |
|---|---|---|
| `r1-perform-twice.cob` | **ICE**, GIMPLE `cfg` | compiles |
| `r2-backward-goto.cob` | "never defined" [1] | **ICE** [2] |
| `r3-undefined.cob` | **ICE**, GIMPLE `cfg` | "never..." [3] |
| `ok1-perform-once.cob` | compiles | compiles |
| `ok2-forward-goto.cob` | compiles | compiles |
| `ok3-letter-in-name.cob` | compiles | compiles |
| `ok4-undefined-named.cob` | diagnosed [4] | diagnosed [5] |
1. `error: line 9: 36 is used on line 9 and never defined`
2. `Assertion '0 == key.line' failed` (full text under `r2` below)
3. `error: line 7: 36 is used on line 9 and never defined`
4. `error: procedure not found: PERFORM FOO-BAR`
5. warnings plus `failed compiling`
Read against its control, each `r` case isolates one thing:
**`r1` - two `PERFORM`s of the same all-digit section name crash the
trunk compiler.** `ok1` is the identical program with one `PERFORM`
instead of two, and it compiles and runs. This one is a
**regression**: 15.2.0 compiles `r1`.
```cobol
MAIN SECTION.
M1.
PERFORM 36
PERFORM 36
STOP RUN.
36 SECTION.
P36.
DISPLAY "IN 36".
```
```
during GIMPLE pass: cfg
In function 'r1':
cobol1: internal compiler error: Segmentation fault
```
**`r2` - a reference to an all-digit name defined *earlier* is not
resolved.** `ok2` is the same program with the definition moved *after*
the reference, and it compiles; so the direction of the reference is
what decides it, which points at name resolution rather than at parsing.
On trunk the failure is a diagnostic that contradicts the source - the
name is defined three lines up - and on 15.2.0 it is an assertion
failure in the label table:
```cobol
36.
STOP RUN.
P.
GO TO 36.
```
```
17.0.0: error: line 9: 36 is used on line 9 and never defined
15.2.0: cobol1: ../../src/gcc/cobol/symbols.cc:386: bool
label_cmp(const cbl_label_t&, const cbl_label_t&,
bool): Assertion `0 == key.line' failed.
cobol1: internal compiler error: Aborted
```
**`r3` - an all-digit name that really *is* undefined crashes the trunk
compiler** instead of being diagnosed. `ok4` is the same mistake with
an ordinary name, and it produces a clean `procedure not found`. A
three-line program:
```cobol
PROGRAM-ID. R3.
PROCEDURE DIVISION.
PERFORM 36
```
**`ok3` is the control that ties the three together**: it is `r2` with
the name `36A` instead of `36` - one letter added, nothing else
changed - and it compiles on both compilers. A name that merely
*begins* with a digit is fine; the trigger is a name that is *all*
digits. `1` and `0036` behave the same as `36`.
## Why it matters beyond the reproducers
The NIST COBOL-85 suite names a segment's section after its priority
number, so this shape is pervasive in its `SG/` module. Rendered
without the segmentation syntax that `gcobol` does not implement, those
programs hit this bug and not the one they were rewritten to avoid:
- `SG101A`, `SG201A` - ICE, GIMPLE pass `cfg`
- `SG203A` - `36 is used on line 976 and never defined`, for a section
defined on line 919
The same two shapes as `r1` and `r2`. Renaming the all-digit sections
in the generated source makes all three compile, which is how the cause
was found.
## Running it
```
# whatever gcobol is on PATH
./run.sh
# a specific build
GCOBOL=/opt/gcc-trunk-20260821/bin/gcobol ./run.sh
```
`run.sh` only reports; it always exits 0. Each `.cob` also carries a
comment saying what it is for, so a single case can be handed to a
compiler on its own.