Ticket #43663

%zd and %td support in attribute format printf

Open Date: 2022-01-20 19:09 Last Update: 2022-01-25 00:19

Reporter:
Owner:
(None)
Type:
Status:
Closed
Component:
(None)
MileStone:
(None)
Priority:
5 - Medium
Severity:
5 - Medium
Resolution:
Works For Me
File:
None
Vote
Score: 0
No votes
0.0% (0/0)
0.0% (0/0)

Details

sample a.c

#include <stddef.h>
int foo(const char*,...) __attribute__((format(printf, 1, 2)));

int main(void)
{
	size_t f = sizeof(size_t);
	return foo("%zd\n", f);
}

this simple program, compiled with -Wall warns about unsupported %zd

$ x86_64-w64-mingw32-gcc a.c -Wall 
a.c: In function 'main':
a.c:7:22: warning: unknown conversion type character 'z' in format [-Wformat=]
    7 |         return foo("%zd\n", f);
      |                      ^
$ i686-w64-mingw32-gcc a.c -Wall 
a.c: In function 'main':
a.c:7:22: warning: unknown conversion type character 'z' in format [-Wformat=]
    7 |         return foo("%zd\n", f);
      |                      ^

whereas gcc compiles the snipnet just fine

version info
$ x86_64-w64-mingw32-gcc -v
Using built-in specs.
COLLECT_GCC=/usr/bin/x86_64-w64-mingw32-gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-w64-mingw32/11.2.1/lto-wrapper
Target: x86_64-w64-mingw32
Configured with: ../configure --prefix=/usr --bindir=/usr/bin --includedir=/usr/include --mandir=/usr/share/man --infodir=/usr/share/info --datadir=/usr/share --build=x86_64-redhat-linux-gnu --host=x86_64-redhat-linux-gnu --with-gnu-as --with-gnu-ld --verbose --without-newlib --disable-multilib --disable-plugin --with-system-zlib --disable-nls --without-included-gettext --disable-win32-registry --enable-languages=c,c++,objc,obj-c++,fortran --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-threads=posix --with-isl --enable-libgomp --target=x86_64-w64-mingw32 --with-sysroot=/usr/x86_64-w64-mingw32/sys-root --with-gxx-include-dir=/usr/x86_64-w64-mingw32/sys-root/mingw/include/c++
Thread model: posix
Supported LTO compression algorithms: zlib
gcc version 11.2.1 20210728 (Fedora MinGW 11.2.1-3.fc35) (GCC) 
version info
$ i686-w64-mingw32-gcc -v
Using built-in specs.
COLLECT_GCC=/usr/bin/i686-w64-mingw32-gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/i686-w64-mingw32/11.2.1/lto-wrapper
Target: i686-w64-mingw32
Configured with: ../configure --prefix=/usr --bindir=/usr/bin --includedir=/usr/include --mandir=/usr/share/man --infodir=/usr/share/info --datadir=/usr/share --build=x86_64-redhat-linux-gnu --host=x86_64-redhat-linux-gnu --with-gnu-as --with-gnu-ld --verbose --without-newlib --disable-multilib --disable-plugin --with-system-zlib --disable-nls --without-included-gettext --disable-win32-registry --enable-languages=c,c++,objc,obj-c++,fortran --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-threads=posix --with-isl --enable-libgomp --target=i686-w64-mingw32 --with-sysroot=/usr/i686-w64-mingw32/sys-root --with-gxx-include-dir=/usr/i686-w64-mingw32/sys-root/mingw/include/c++ --disable-sjlj-exceptions --with-dwarf2
Thread model: posix
Supported LTO compression algorithms: zlib
gcc version 11.2.1 20210728 (Fedora MinGW 11.2.1-3.fc35) (GCC) 

MS compiler supports %zd as the preferred way to print size_t values

(didn't included in the tests, but %td for ptrdiff_t suffers from the same issues)

Ticket History (3/4 Histories)

2022-01-20 19:09 Updated by: eric_pouech
  • New Ticket "%zd and %td support in attribute format printf" created
2022-01-23 21:41 Updated by: keith
Comment

Thank you for this exemplary issue report; I do so wish that all ticket submissions could be so diligently formatted, comprehensively detailed, and complemented by such a well specified, compact, yet complete test case.

I can confirm that your issue is reproduced by my own mingw32-gcc cross-compiler suite, (which FWIW, is the self-same implementation that I used to create the official MinGW.OSDN distribution of GCC-9.2.0):

$ mingw32-gcc -v -Wall -fsyntax-only a.c
Using built-in specs.
COLLECT_GCC=mingw32-gcc
COLLECT_LTO_WRAPPER=/home/keith/mingw32-gcc-9.2.0/bin/../libexec/gcc/mingw32/9.2.0/lto-wrapper
Target: mingw32
Configured with: ../src/gcc-9.2.0/configure --target=mingw32 --disable-win32-registry --with-arch=i586 --with-tune=generic --enable-static --enable-shared --enable-threads --enable-languages=c,c++,objc,obj-c++,fortran,ada --with-dwarf2 --disable-sjlj-exceptions --enable-version-specific-runtime-libs --enable-libgomp --disable-libvtv --with-libiconv-prefix=/mingw --with-libintl-prefix=/mingw --enable-libstdcxx-debug --disable-build-format-warnings --prefix=/home/keith/mingw32 --with-sysroot=/home/keith/mingw32 --disable-nls --with-pkgversion='MinGW.org Cross-GCC Build-20200531-1'
Thread model: win32
gcc version 9.2.0 (MinGW.org Cross-GCC Build-20200531-1) 
COLLECT_GCC_OPTIONS='-v' '-Wall' '-fsyntax-only' '-mtune=generic' '-march=i586'
 /home/keith/mingw32-gcc-9.2.0/bin/../libexec/gcc/mingw32/9.2.0/cc1 -quiet -v -iprefix /home/keith/mingw32-gcc-9.2.0/bin/../lib/gcc/mingw32/9.2.0/ -isysroot /home/keith/mingw32-gcc-9.2.0/bin/../../mingw32 a.c -quiet -dumpbase a.c -mtune=generic -march=i586 -auxbase a -Wall -version -fsyntax-only -o /dev/null
GNU C17 (MinGW.org Cross-GCC Build-20200531-1) version 9.2.0 (mingw32)
	compiled by GNU C version 9.3.0, GMP version 6.1.2, MPFR version 4.0.2, MPC version 1.1.0, isl version isl-0.21-GMP

GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
ignoring nonexistent directory "/home/keith/mingw32-gcc-9.2.0/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/include"
ignoring duplicate directory "/home/keith/mingw32-gcc-9.2.0/bin/../lib/gcc/../../lib/gcc/mingw32/9.2.0/include"
ignoring nonexistent directory "/home/keith/mingw32-gcc-9.2.0/bin/../../mingw32/usr/local/include"
ignoring duplicate directory "/home/keith/mingw32-gcc-9.2.0/bin/../lib/gcc/../../lib/gcc/mingw32/9.2.0/include-fixed"
ignoring nonexistent directory "/home/keith/mingw32-gcc-9.2.0/bin/../lib/gcc/../../lib/gcc/mingw32/9.2.0/../../../../mingw32/include"
#include "..." search starts here:
#include <...> search starts here:
 /home/keith/mingw32-gcc-9.2.0/bin/../lib/gcc/mingw32/9.2.0/include
 /home/keith/mingw32-gcc-9.2.0/bin/../lib/gcc/mingw32/9.2.0/include-fixed
 /home/keith/mingw32-gcc-9.2.0/bin/../../mingw32/mingw/include
End of search list.
GNU C17 (MinGW.org Cross-GCC Build-20200531-1) version 9.2.0 (mingw32)
	compiled by GNU C version 9.3.0, GMP version 6.1.2, MPFR version 4.0.2, MPC version 1.1.0, isl version isl-0.21-GMP

GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: 43d95314bbda36f2d474b814015ff720
a.c: In function 'main':
a.c:8:16: warning: unknown conversion type character 'z' in format [-Wformat=]
    8 |   return foo("%zd\n", f);
      |                ^
a.c:8:14: warning: too many arguments for format [-Wformat-extra-args]
    8 |   return foo("%zd\n", f);
      |              ^~~~~~~
COMPILER_PATH=/home/keith/mingw32-gcc-9.2.0/bin/../libexec/gcc/mingw32/9.2.0/:/home/keith/mingw32-gcc-9.2.0/bin/../libexec/gcc/:/home/keith/mingw32-gcc-9.2.0/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/bin/
LIBRARY_PATH=/home/keith/mingw32-gcc-9.2.0/bin/../lib/gcc/mingw32/9.2.0/:/home/keith/mingw32-gcc-9.2.0/bin/../lib/gcc/:/home/keith/mingw32-gcc-9.2.0/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/lib/:/home/keith/mingw32-gcc-9.2.0/bin/../../mingw32/lib/
COLLECT_GCC_OPTIONS='-v' '-Wall' '-fsyntax-only' '-mtune=generic' '-march=i586'
However, I do feel obliged to point out that the compilers, which you are using, are not products of our MinGW.OSDN Project, and we are unable to support them; consequently, while we may be able to offer generic advice, we can offer no guarantees that such advice will actually apply, in your specific use case. Furthermore, I would like to challenge this assertion, in your original submission:

MS compiler supports %zd as the preferred way to print size_t values

While this may be true of recent versions of the MS compiler, (and likely requires that the application be linked with Microsoft's non-free Universal C Runtime Library, or any the non-free runtime libraries which are distributed as components of the MSVC suite itself), it is not true in the case of applications which are linked (as MinGW applications must be) with MSVCRT.DLL; (note that, although itself technically non-free, MSVCRT.DLL is distributed as a fundamental OS component, and is sufficiently self-contained to let us use it under the GPL exemption for such OS components; unfortunately, although AIUI the UCRT is also provided as an OS component, it is not self-contained, and its dependencies, which are not so distributed, render it fundamentally useless in this context).

Now, I realize that I have not yet actually addressed the issue, which you have raised; in the interests of keeping follow-up comments at a manageable size, I will defer further discussion to a later comment.

2022-01-24 06:15 Updated by: keith
Comment

In my earlier comment, I indicated that I observe the same behaviour as that described in the original report; for reference, here is the compiler output, without the added noise of GCC version, and configuration information:

$ mingw32-gcc -Wall -fsyntax-only a.c
a.c: In function 'main':
a.c:8:16: warning: unknown conversion type character 'z' in format [-Wformat=]
    8 |   return foo("%zd\n", f);
      |                ^
a.c:8:14: warning: too many arguments for format [-Wformat-extra-args]
    8 |   return foo("%zd\n", f);
      |              ^~~~~~~
In addition to this, I also dropped a hint — you may, or may not have noticed it — as to why this may actually be considered to be correct behaviour. Unlike Microsoft's compilers — which use non-free runtime libraries, none of which are legally redistributable without purchase of a Microsoft developer's licence — MinGW compilers produce applications which are linked with MSVCRT.DLL. Although also proprietary, Microsoft distribute this as a component of the Windows operating system, so no redistribution is necessary; however, the stdio implementation in MSVCRT.DLL — and specifically its printf() family of functions — does not support the "%zd" or "%td" format specifications. Since the MinGW compilers — at least those which we distribute — use the MSVCRT.DLL printf() implementation, by default, it seems reasonable that -Wformat should warn that these format specifications are unsupported.

Of course, default behaviour isn't always appropriate ... and it probably isn't, in your particular use case. The intent of my emphasis — of by default — in the preceding paragraph, is to indicate that we are not simply stuck with default behaviour; there are techniques, which we may adopt to work around this issue, and which I will address in a further follow-up comment.

2022-01-25 00:19 Updated by: keith
  • Status Update from Open to Closed
  • Resolution Update from None to Works For Me
Comment

A review of the relevant section of the GCC manual should provide enlightenment ... search for the "format" attribute sub-heading. I kind of suspected that we might arrive at this conclusion: the behaviour here is exactly as documented, so there is no bug to address, (other than user error).

The GCC documentation is rather terse, so some further explanation may be helpful. If we review your original test case (with line numbers inserted):

  1. #include <stddef.h>
  2. int foo(const char*,...) __attribute__((format(printf, 1, 2)));
  3. int main(void)
  4. {
  5. size_t f = sizeof(size_t);
  6. return foo("%zd\n", f);
  7. }
This should compile fine, (probably) with any target variant of GCC except a MinGW variant. See, a MinGW target GCC implements (at least) two distinct flavours of the "printf" category for the "format" attribute, namely "gnu_printf" and "ms_printf" — whereas most targets comprehend only "gnu_printf" — and for the MinGW target, "printf" is interpreted as "ms_printf". Thus, your test case effectively becomes:
  1. #include <stddef.h>
  2. int foo(const char*,...) __attribute__((format(ms_printf, 1, 2)));
  3. int main(void)
  4. {
  5. size_t f = sizeof(size_t);
  6. return foo("%zd\n", f);
  7. }
and this leads to a -Wformat anomaly at line 7, because "%zd" is not a valid format specification, within the "ms_printf" category, as specified at line 2. If your function "foo()" interprets format specifications according to "gnu_printf" rules, then you need to specify that explicitly:
  1. #include <stddef.h>
  2. int foo(const char*,...) __attribute__((format(gnu_printf, 1, 2)));
  3. int main(void)
  4. {
  5. size_t f = sizeof(size_t);
  6. return foo("%zd\n", f);
  7. }
FWIW, our MinGW.OSDN builds of GCC, from GCC-6.3.0 onwards, implement a third category of "printf" Wformat checks, namely "mingw_printf"; (other distributions may not support this). Used thus:
  1. #include <stddef.h>
  2. int foo(const char*,...) __attribute__((format(mingw_printf, 1, 2)));
  3. int main(void)
  4. {
  5. size_t f = sizeof(size_t);
  6. return foo("%zd\n", f);
  7. }
this creates a hybrid of both "ms_printf" and "gnu_printf", (with any conflicts resolved in favour of the "gnu_printf" interpretation), and provides correct -Wformat checking for the "printf()" family replacement functions, which we provide in our libmingwex.a, and libmingwex-4.dll libraries.

With either "gnu_printf", or "mingw_printf" specified as the format attribute category, in line 2 of your test case, compilation succeeds without warnings:

$ mingw32-gcc -Wall -fsyntax-only a.c
$
Consequently, I am closing this ticket, with no further action required.

Attachment File List

No attachments

Edit

Please login to add comment to this ticket » Login