cgccclangcompiler-optionssignedness

What does it mean by `-fno-signed-char, --signed-char` for gcc & clang?


In clang's documentation, I find a strange option:

-fno-signed-char, --signed-char

I know -fno-signed-char is equivalent to -funsigned-char, I just wonder:

1. What does it mean by following --signed-char?

2. Is --signed-char equivalent to -fsigned-char ?


Solution

  • Behavior

    The documentation says:

    -fsigned-char, -fno-signed-char, --signed-char

    char is signed

    This somewhat cryptic notation means that -fsigned-char and --signed-char both cause char to be a signed type, and -fno-signed-char causes char to be an unsigned type.

    (Part of the reason the documentation is so cryptic is that it is automatically generated from the internal implementation shown below. That's not an excuse, just an explanation.)

    Later, the documentation says:

    -funsigned-char, -fno-unsigned-char, --unsigned-char

    without any further description. The intent is for the user to infer the meaning from the option names and from the behavior of the similarly named options above. Specifically, -funsigned-char and --unsigned-char cause char to be unsigned, and -fno-unsigned-char causes char to be signed.

    Demonstration

    Test program test.c:

    // test.c
    // Is `char` signed?
    
    #include <stdio.h>                     // printf
    
    int main(void)
    {
      char c = (char)128;
      printf("c is %d, so char is %s\n",
             (int)c,
             (c > 0? "unsigned" : "signed"));
      return 0;
    }
    
    // EOF
    

    Compiling and running with all six options on Linux/x86_64 using Clang-16.0.0:

    $ clang -Wall -fsigned-char test.c && ./a.out
    c is -128, so char is signed
    $ clang -Wall -fno-signed-char test.c && ./a.out
    c is 128, so char is unsigned
    $ clang -Wall --signed-char test.c && ./a.out
    c is -128, so char is signed
    $ clang -Wall -funsigned-char test.c && ./a.out
    c is 128, so char is unsigned
    $ clang -Wall -fno-unsigned-char test.c && ./a.out
    c is -128, so char is signed
    $ clang -Wall --unsigned-char test.c && ./a.out
    c is 128, so char is unsigned
    

    Internal definition

    The clang driver program options are defined in clang/include/clang/Driver/Options.td. As one example, the --signed-char option is defined on line 5669 of version 18.1.8 as an alias for -fsigned-char:

    def _signed_char : Flag<["--"], "signed-char">, Alias<fsigned_char>;
    

    In Clang, the driver options --signed-char, --unsigned-char, -fsigned-char, and -funsigned-char were all added together on 2009-06-05 in commit 327f0b55936a6.

    The driver program interprets these options and maps them to the -fno-signed-char option of clang -cc1 (the compiler front-end proper) starting at line 3959 of clang/lib/Driver/ToolChains/Clang.cpp:

      // -fsigned-char is default.
      if (const Arg *A = Args.getLastArg(options::OPT_fsigned_char,
                                         options::OPT_fno_signed_char,
                                         options::OPT_funsigned_char,
                                         options::OPT_fno_unsigned_char)) {
        if (A->getOption().matches(options::OPT_funsigned_char) ||
            A->getOption().matches(options::OPT_fno_signed_char)) {
          CmdArgs.push_back("-fno-signed-char");
        }
      } else if (!isSignedCharDefault(T)) {
        CmdArgs.push_back("-fno-signed-char");
      }
    

    The clang -cc1 option -fno-signed-char (the only variant it recognizes) is also defined in Options.td:

    defm signed_char : BoolFOption<"signed-char",
      LangOpts<"CharIsSigned">, DefaultTrue,
             // ^^^^^^^^^^^^
             //  effect is to control this language option
      NegFlag<SetFalse, [], [ClangOption, CC1Option], "char is unsigned">,
                                       // ^^^^^^^^^
                                       // recognized by clang -cc1
      PosFlag<SetTrue, [], [ClangOption], "char is signed">>,
      ShouldParseIf<!strconcat("!", open_cl.KeyPath)>;
    

    Since you linked to the documentation of Clang, I assume that's the implementation of interest, so I didn't dig into implementation or history for GCC. But Clang generally follows conventions that GCC established, so if you're interested in the pre-2009 history, that is where the trail goes next.