Bug 90005

Summary: No error produced for the wrong type of string used in gcc >= 5.0
Product: gcc Reporter: Pawel <pawel.wrobel>
Component: c++Assignee: Not yet assigned to anyone <unassigned>
Severity: normal Keywords: diagnostic
Priority: P3    
Version: 8.3.0   
Target Milestone: ---   
See Also: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64867
Host: Target:
Build: Known to work:
Known to fail: Last reconfirmed:

Description Pawel 2019-04-08 08:51:03 UTC

I have the question regarding the following behaviour of gcc 8.3 (it behaves the same way from version 5.0 up to 8.3 - when I tested with the https://gcc.godbolt.org/).

When I compile following program (it has an obvious omission - missing .c_str() conversion of std::string to char*) 
    #include <stdio.h>
    #include <string>
    int main()
      std::string txt("there");
      printf("Hello %s ! \n", txt);

On the gcc 4.9 and before - it correctly notifies me with an error like : 

<source>: In function 'int main()':

<source>:6:30: error: cannot pass objects of non-trivially-copyable type 'std::string {aka class std::basic_string<char>}' through '...'
   printf("Hello %s ! \n", txt);
Compiler returned: 1

However, starting from the gcc 5.0 and above (gcc 8.3 included) - no error is generated - and the binary is being produced. It obviously prints the garbage when run ("Hello (`e !"). So lack of the error should be considered as an bug ?
Or maybe do I need to provide some special flag for this error message to appear and stop the compilation in gcc 5.0 and above ? 
Both the gcc <= 4.9 works correctly here (and the newer clang compilers also point out this error correctly). Can I make the gcc >= 5.0 to generate an error here with some flag ?
Comment 1 Andrew Pinski 2019-04-08 08:53:47 UTC
Have you tried -Wformat ?
Comment 2 Pawel 2019-04-08 09:10:30 UTC


Adding -Wformat indeed show up a waring here : 

$ g++ -Wformat main.cpp -o out 
main.cpp: In function ‘int main()’:
main.cpp:6:30: warning: format ‘%s’ expects argument of type ‘char*’, but argument 2 has type ‘std::string* {aka std::basic_string<char>*}’ [-Wformat=]
   printf("Hello %s ! \n", txt);

I can make it into an error using the -Werror flag then - however - that seems to be too strict. In the actual, real-scenario code, I think, I cannot afford to use the -Werror flag globally - since it will turn many of otherwise harmless warnings into errors.

Is there a reason why this is considered to be 'just a harmless warning' by newer(>=5.0) gcc - whereas other gcc(<5.0)/complers consider this a "hard problem" (doing this omission changes program into printing the complete garbage, so just a warning seems to be a little too soft there)...
The warning/error message also is so different between gcc versions here..
Comment 3 Andrew Pinski 2019-04-08 09:20:31 UTC
Because GCC allows passing non pods via varargs now.  This is an explicit change due to newer c++ changes.

You could do -Werror=format to get only the format warnings changed to errors.
Comment 4 Andrew Pinski 2019-04-08 09:20:54 UTC
Comment 5 Jonathan Wakely 2019-04-08 09:41:05 UTC
(In reply to Andrew Pinski from comment #3)
> Because GCC allows passing non pods via varargs now.  This is an explicit
> change due to newer c++ changes.

Right. The C++ standard says:

"Passing a potentially-evaluated argument of class type (Clause 11) having a non-trivial copy constructor, a non-trivial move constructor, or a non-trivial destructor, with no corresponding parameter, is conditionally-supported with implementation-defined semantics."

GCC supports passing a non-trivial type such as std::string to "...", with implementation-defined semantics. Some other compilers do not support it.

But printf still requires a char* for a %s argument, which is what -Wformat will warn about. Passing invalid arguments to printf often results in complete garbage, e.g. printf("%s", &printf). That's not specific to passing a std::string, it's just how printf works: you need to pass the right arguments.
Comment 6 Pawel 2019-04-08 10:19:14 UTC

Thanks for the explanation. Indeed, for example, the clang does not support the non-POD(ex. std::string) to variadic function - as : 
error: cannot pass non-trivial object of type 'std::__cxx11::string' (aka 'basic_string<char>') to variadic function;
Comment 7 Pawel 2019-04-08 10:27:51 UTC
The "-Werror=format" solution seems to work for me - it triggers the error here (missing .c_str()) even for the gcc >= 5.0 - leaving all the other, non-related warnings untouched.

Comment 8 Eric Gallager 2019-04-09 15:09:35 UTC
There's also a warning about passing POD thru varargs under -Wconditionally-supported, and bug 64867 would split it off into a separate -Wnon-pod-varargs flag
Comment 9 Pawel 2019-04-10 08:07:53 UTC
Hello Eric,

Thank You so much for this answer - in our case that turned out also to be really useful.
We took the "-Werror=conditionally-supported" version - to trigger an error in our custom variadic function - as we really do not want the non-POD(std::string in our case) argument there - and we prefer having the error visible, when someone accidentally use std::string instead of char* in the large codebase.

Thanks !