https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126281

            Bug ID: 126281
           Summary: gcobol: TYPEDEF in LOCAL-STORAGE SECTION ICEs at
                    PROCEDURE DIVISION
           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 65059
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=65059&action=edit
typedef-local-storage-ice.cob (also inline in the bug report)

# Summary

Declaring a `TYPEDEF` in the **LOCAL-STORAGE SECTION** causes an internal
compiler error when the compiler reaches `PROCEDURE DIVISION`.  The same
`TYPEDEF` declared in WORKING-STORAGE compiles and runs correctly.  The IBM
Language Reference explicitly allows `TYPEDEF` in LOCAL-STORAGE.

# Environment

```
gcobol (GCC) 17.0.0 20260710 (experimental), target aarch64-linux-gnu
```

No special options are required (`-ffixed-form` is the default).

# Reproducer

`typedef-local-storage-ice.cob` (this directory).  The TYPEDEF alone is enough;
a `TYPE` instance is not required:

```cobol
       DATA DIVISION.
       LOCAL-STORAGE SECTION.
       01  INT32-T TYPEDEF PIC S9(9) USAGE COMP-5.
       01  LS-N    PIC S9(9) USAGE COMP-5.
       PROCEDURE DIVISION.
           MOVE 1 TO LS-N
           DISPLAY LS-N
           GOBACK.
```

```
$ gcobol -c typedef-local-storage-ice.cob -o /dev/null
typedef-local-storage-ice.cob:22:26: internal compiler error: parser_local_add:
parameter ‘new_var’ is variable INT32-T<FldNumericBin5> with NULL
‘var_decl_node’
   22 |        PROCEDURE DIVISION.
      |                          ^
0x… parser_local_add(cbl_field_t*)
        gcc/cobol/genapi.cc:14607
0x… procedure_division_ready
        gcc/cobol/parse_ante.h:3721
…
Please submit a full bug report…
```

A `TYPE` instance of that typedef in LOCAL-STORAGE (or a group TYPEDEF) fails
the same way; the ICE names the TYPEDEF field, never the instance.

# Control cases (do not ICE)

| Case | Result |
|---|---|
| Same `TYPEDEF` in **WORKING-STORAGE**, instance anywhere | compiles |
| `TYPEDEF` in WORKING-STORAGE, `TYPE` instance in LOCAL-STORAGE | compiles and
runs (`DISPLAY` prints `000000001`) |
| LOCAL-STORAGE item with an inline `PIC` (no TYPEDEF), with or without
`PROCEDURE DIVISION USING` | compiles |

So the defect is confined to treating a LOCAL-STORAGE TYPEDEF as if it were a
per-invocation local variable that needs a stack frame slot.

# Why it is a bug

IBM Enterprise COBOL for z/OS 6.5 Language Reference (SC27-8713-04),
**TYPEDEF clause** (p.240):

> "The TYPEDEF clause can only be specified in the WORKING-STORAGE,
> LOCAL-STORAGE, LINKAGE, or FILE sections of a program."
>
> "No storage is allocated for a type declaration."

LOCAL-STORAGE TYPEDEF is therefore conforming COBOL, and a TYPEDEF must not
own a `var_decl_node`.  ICEing on it is a compiler defect, not a dialect gap.

# Likely cause (trunk sources)

In `procedure_division_ready` (`gcc/cobol/parse_ante.h`, around line 3716)
every symbol with the `local_e` attribute is handed to `parser_local_add`:

```c++
std::for_each( symbols_begin(current.program_index()), symbols_end(),
               []( auto& elem ) {
                 if( elem.type == SymField ) {
                   auto f = cbl_field_of(&elem);
                   if( f->has_attr(local_e) ) {
                     parser_local_add(f);
                   }
                 }
               } );
```

A TYPEDEF declared in LOCAL-STORAGE inherits `local_e` (section attribute
propagation) but has no storage, so `var_decl_node` stays NULL.
`parser_local_add` then hits `CHECK_FIELD` (`gcc/cobol/show_parse.h`), which
ICEs exactly on a NULL `var_decl_node`.

A few dozen lines later in the same function, the INITIALIZE-island variable
counts correctly skip typedefs:

```c++
if( f->is_typedef() ) {
  auto isym = end_of_group( symbol_index(e) );
  e = symbol_at(--isym);
  continue;
}
```

The `parser_local_add` loop simply lacks that same `is_typedef()` guard.  A
one-line fix is therefore:

```c++
if( f->has_attr(local_e) && !f->is_typedef() ) {
  parser_local_add(f);
}
```

(and likewise skip, or otherwise not push, any field that is subordinate to a
TYPEDEF group).

# Workaround

Declare the TYPEDEF in WORKING-STORAGE SECTION.  Instances may still use
LOCAL-STORAGE:

```cobol
       WORKING-STORAGE SECTION.
       01  INT32-T TYPEDEF PIC S9(9) USAGE COMP-5.
       LOCAL-STORAGE SECTION.
       01  LS-N TYPE INT32-T.
```

That form compiles and runs with the same gcobol build that ICEs on the
reproducer.

# Impact / how found

Hit while building a gcobol comparison target for the COBOL version
of a silly pi calculation sample program.  A COBOL -> COBOL transformation
utility promoted working-storage to local-storage (an opt-in transformation, as
this program was known to have no requirement for "static" working-storage
semantics.) That transformed code, fed to gcobol, produced this ICE.
Keeping the TYPEDEF in WORKING-STORAGE was used as a workaround.

# Disclaimer

The root cause analysis for this bug report was generated by cursor. Having
previously pointed it at the gcc repo to help understand bugzilla 126277,
it took it upon itself to do the same when asked to write a minimal reproducer
for this issue -- I have not vouched for it's correctness.

Reply via email to