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

            Bug ID: 128118
           Summary: GCC miscompiles enum main for C2Y
           Product: gcc
           Version: 17.0
               URL: https://godbolt.org/z/Mx5voaMsa
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: rasheeqazad at howtonotwin dot net
  Target Milestone: ---

C2Y allows programs to define main as returning an enum type with explicit
underlying type int. Such a main is still required to have the same "automatic
return 0;" behavior as an ordinary int main(), but GCC does not implement this.

    int puts(const char*);
    enum exit_code : int { EXIT_OK = 0 };
    enum exit_code main() {
      puts("oops!");
    }

On x86_64-pc-linux-gnu, the expected behavior is

    $ gcc -std=c2y -o main main.c
    $ ./main
    oops!
    $ echo $?
    0

The actual behavior, as of GCC commit 7ae37a76b32, is

    $ gcc -std=c2y -o main main.c
    $ ./main
    oops!
    $ echo $?
    6

6 is the return value of puts. GCC appears to be returning whatever happens to
be in eax at the end of main.

GCC also emits a bunch of warnings that it shouldn't:

    $ gcc -Wall -std=c2y -o main main.c
    main.c:3:16: warning: return type of ‘main’ is not ‘int’ [-Wmain]
        3 | enum exit_code main() {
          |                ^~~~
    main.c: In function ‘main’:
    main.c:5:1: warning: control reaches end of non-void function
[-Wreturn-type]
        5 | }
          | ^

The draft standard's wording on this seems pretty clear. Since N3685 (Sept 2025
working draft), 5.2.2.3.2 has read

> The function called at program startup is named main. [...]
> It is defined with a return type of int and with no parameters:
>     int main(void) { /* ... */ }
> or [...]
> or using another type that is compatible with one of these two function types.

enum exit_code(void) and int(void) are compatible because they both have no
parameters and enum exit_code and int are compatible. enum exit_code and int
are
compatible because an enumerated type with a fixed underlying type is
compatible
with the underlying type. So, the example program's declaration of main is
allowed. Further, the program should get an implicit return 0; at the end,
because 5.2.2.3.4 reads

> If the return type of the main function is a type compatible with int, a
> return from the initial call to the main function is equivalent to calling the
> exit function with the value returned by the main function as its argument;
> reaching the } that terminates the main function returns a value of 0.

The return type enum exit_code is in fact compatible with int, so reaching the
}
at the end of main in the above program should return 0. (The -Wreturn-type
warning is the same as the one GCC emits when it thinks there could be UB from
running off the end of a function, but it appears that GCC doesn't treat the }
of main as UB here. Instead, it appears to be applying the next sentence of
5.2.2.3.4, "If the return type is not compatible with int, the termination
status returned to the host environment is unspecified.".)

The draft standard's wording is materially unchanged in N3886. The wording
change relative to C23—which just specified, vaguely, that the type of main be
"equivalent" to `int(void)`/...—was introduced by the adoption of N3623.
  • [Bug c/128118] New: GCC mi... rasheeqazad at howtonotwin dot net via Gcc-bugs

Reply via email to