-
Notifications
You must be signed in to change notification settings - Fork 6.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enforce C99 inttypes usage for standard C functions #46032
Comments
This commit adds the missing `PRIx{FAST,LEAST}N` C99 integer type format macros that correspond to the C99 integer types overridden in the `zephyr_stdint.h` header: PRIdFAST8, PRIdFAST16, PRIdFAST32, PRIdFAST64 PRIdLEAST8, PRIdLEAST16, PRIdLEAST32, PRIdLEAST64 PRIiFAST8, PRIiFAST16, PRIiFAST32, PRIiFAST64 PRIiLEAST8, PRIiLEAST16, PRIiLEAST32, PRIiLEAST64 PRIoFAST8, PRIoFAST16, PRIoFAST32, PRIoFAST64 PRIoLEAST8, PRIoLEAST16, PRIoLEAST32, PRIoLEAST64 PRIuFAST8, PRIuFAST16, PRIuFAST32, PRIuFAST64 PRIuLEAST8, PRIuLEAST16, PRIuLEAST32, PRIuLEAST64 PRIxFAST8, PRIxFAST16, PRIxFAST32, PRIxFAST64 PRIxLEAST8, PRIxLEAST16, PRIxLEAST32, PRIxLEAST64 PRIXFAST8, PRIXFAST16, PRIXFAST32, PRIXFAST64 PRIXLEAST8, PRIXLEAST16, PRIXLEAST32, PRIXLEAST64 Note that these macros will eventually need to be defined according to the toolchain-specified types when the `zephyr_stdint.h` hack is removed in the future; refer to the the GitHub issue zephyrproject-rtos#46032 for more details. Signed-off-by: Stephanos Ioannidis <root@stephanos.io>
Just to record for posterity: I hate the ISO I won't -1 this. I won't stand in the way of a merge. But this is just awful. We're now committing to explaining to generations of new developers why Zephyr's printf and logging layers are awful. And they're all going to ask "Why doesn't Linux have this problem?!". What's our answer going to be? [1] There's no typesafety problem with passing e.g. a "int64_t" to a "%ld" on arm64, for example. The compiler knows the types, it knows how to pass a 64 bit argument, it knows how big a "long" is, and it can trivially (and does!) emit a printf format error on i386 builds. Why is that a problem? If the user wrote non-portable code they should fix it. They don't need new (and bad!) syntax in the language to help with that. [2] e.g. By using |
This commit adds the missing `PRIx{FAST,LEAST}N` C99 integer type format macros that correspond to the C99 integer types overridden in the `zephyr_stdint.h` header: PRIdFAST8, PRIdFAST16, PRIdFAST32, PRIdFAST64 PRIdLEAST8, PRIdLEAST16, PRIdLEAST32, PRIdLEAST64 PRIiFAST8, PRIiFAST16, PRIiFAST32, PRIiFAST64 PRIiLEAST8, PRIiLEAST16, PRIiLEAST32, PRIiLEAST64 PRIoFAST8, PRIoFAST16, PRIoFAST32, PRIoFAST64 PRIoLEAST8, PRIoLEAST16, PRIoLEAST32, PRIoLEAST64 PRIuFAST8, PRIuFAST16, PRIuFAST32, PRIuFAST64 PRIuLEAST8, PRIuLEAST16, PRIuLEAST32, PRIuLEAST64 PRIxFAST8, PRIxFAST16, PRIxFAST32, PRIxFAST64 PRIxLEAST8, PRIxLEAST16, PRIxLEAST32, PRIxLEAST64 PRIXFAST8, PRIXFAST16, PRIXFAST32, PRIXFAST64 PRIXLEAST8, PRIXLEAST16, PRIXLEAST32, PRIXLEAST64 Note that these macros will eventually need to be defined according to the toolchain-specified types when the `zephyr_stdint.h` hack is removed in the future; refer to the the GitHub issue #46032 for more details. Signed-off-by: Stephanos Ioannidis <root@stephanos.io>
While I am not a very big fan of this either, this is the only way to be portable across different compilers and we have no option but to do this, especially as we start introducing more non-GNU compiler support to the Zephyr. I believe this is an inevitable decision for us. p.s. It is as simple as, if you want to use C99 integer types ( |
"But why doesn't Linux need this?" :) |
Just to record for posterity: I hate the ISO `PRI*` macro nonsense with a fiery passion.
Amen. I'm 100% with you.
|
Because they use their own type system? ( We can go back to doing that again; but, as long as we are using the C99 integer types, we will have to use the corresponding C99 integer type format specifiers too. |
as long as we are using the C99 integer types, we will have to use the
corresponding C99 integer type format specifiers too.
C99 integer types aren't the problem. The basic types to which they're
mapped is. Make int32_t onto an int and int64_t onto a long long at the
toolchain level and you'll be fine. That's trivial to accomplish there.
The alternative is way more painful and far more work for everyone.
…
--
Reply to this email directly or view it on GitHub:
#46032 (comment)
You are receiving this because you were mentioned.
Message ID: ***@***.***>
|
Or we can commit a silly hack that we've been living with just fine. I mean, I get that clang picked some different defaults (or whatever, I'm no expert on the linked bug), but we already have a toolchain abstraction where we could synchronize this. It's far simpler for us to have a layer that manages some platform-specific typedefs than it is to inflict this weird non-C/non-printf alien syntax on app developers. That's the core of the problem here. This PRI nonsense just "isn't C code". It's a mistake. The printf() API has a four decade tradition of hackers (some of them even older than me!) who understand it in their bones. It's not the only way to format strings, but it's the C way. Or consider it this way: if we really did want to change our string formatting syntax, why on earth would we pick THIS one? Why not move to something that looked more like Rust std::fmt, for example, which I think is quite nice personally? I mean, your answer is going to be "because that's rust and not C", right? Well... this isn't C either. No one uses these things in practical code, they're a wart to paper over a mistake in the standard. |
Yes, and that is allowed by the C standard -- that is the real problem. As long as that is allowed, we will have to use the proper abstraction provided by the standard.
For the GNU toolchains and maybe Clang, yes; for others like Arm Compiler, IAR, and whatever we add support for in the future, likely not. |
I have been saying we cannot do that as I noted above "
For GCC, yes; for others, not really.
Because it is a part of the C99 standard. As I said above, "it is as simple as, if you want to use C99 integer types (int32_t, ...), you will have to use the corresponding C99 integer type format specifier macros (PRId32, ...) too." |
If you are really unhappy with this, we can consider going back to the days of I will emphasise again: "it is as simple as, if you want to use C99 integer types (int32_t, ...), you will have to use the corresponding C99 integer type format specifier macros (PRId32, ...) too." |
Right, so just make our headers produce the same types as the toolchain/platform expects (again, we already have a framework for doing this). That fixes the build errors, and leaves the portability issue as a task for the developer, which is where it should be anyway. |
> Make int32_t onto an int and int64_t onto a long long at the toolchain level and you'll be fine. That's trivial to accomplish there.
For the GNU toolchains and maybe Clang, yes; for others like Arm Compiler, IAR, and whatever we add support for in the future, likely not.
Do we really have to support all compilers under the sun?
If so, shouldn't we try to ask them to create a "Zephyr" mode first?
This is typically defined in a single header file therefore very easy to do.
|
That may not be feasible for all compilers. Some compilers may not even have something like
Maybe not "all compilers," but we are committed to supporting multiple toolchains.
Ideally, yes; but, not everything is ideal in real life ... |
Some compilers may not even have something like `__INT32_TYPE__` which we can use to override the compiler-default C library-defined C99 fixed-width types.
They might have another similar mechanism, maybe?
That ought to be investigated for sure.
> Do we really have to support all compilers under the sun?
Maybe not "all compilers," but we are committed to supporting multiple toolchains.
Having gcc and clang arguably fits the definition of "multiple" already.
> If so, shouldn't we try to ask them to create a "Zephyr" mode first?
Ideally, yes; but, not everything is ideal in real life ...
We should at least aim for that first.
Having int32_t sometimes map to an int, sometimes to a long is a
complete abomination. It is not because the standard says you can do it
that it is good and you _should_ do it. Enforcing a coherent type
mapping across architectures and bitwidths is a small pain on the
toolchain maintainers compared to the alternatives which imposes a much
greater pain on everyone but the toolchain maintainers.
So yes, our actions should always aim for the best scenario first.
|
As a side note, if we, as project, want to introduce a non-standard string formatting syntax (and that is probably warranted, if I may add), we can; but, that is a whole another issue and does not change the fact that the use of standard C functions need to comply with the C standard. |
That is indeed an abomination. But for |
As a side note, if we, as project, want to introduce a non-standard string formatting syntax (and that is probably warranted, if I may add), we can; but, that is a whole another issue and does not change the fact that the use of standard C functions need to comply with the C standard.
It is possible for the toolchain to be stricter in its type association
and still be within the C standard.
|
I am inclined to argue that something like below is not C99-compliant and is indeed wrong:
At least, for For other similar non-standard/Zephyr-specific functions like |
(I don't think anyone likes the C99 format specifiers; I wish they had just added more size prefixes to the printf spec instead) Does zephyr support any platforms for which
If not, then we could use %d for int32_t and %lld for int64_t. Unfortunately, this can still generate warnings on platforms for which sizeof(long) == 4 and int32_t is a typedef for long (sigh), but you could stick a cast to (int) for int32_t in the printf call. Not great. It would be good to evaluate where we could be using size_t and ptrdiff_t instead of explicit bit sizes as those can be represented with %zd and %td in printf fomat strings. Alternatively, cast everything to long long and use %lld everywhere. Easier to read than "%"PRId32"\n". That does depend on printf supporting 64-bit integers (which is optional on 32-bit platforms). One significant benefit for using the right format specifiers is that we can use the existing code in gcc and clang which evaluate printf format strings and make sure the arguments are of the right type. I think this is worth a small amount of pain |
I would +1 removing Not crazy about using I find |
This commit adds the missing `PRIx{FAST,LEAST}N` C99 integer type format macros that correspond to the C99 integer types overridden in the `zephyr_stdint.h` header: PRIdFAST8, PRIdFAST16, PRIdFAST32, PRIdFAST64 PRIdLEAST8, PRIdLEAST16, PRIdLEAST32, PRIdLEAST64 PRIiFAST8, PRIiFAST16, PRIiFAST32, PRIiFAST64 PRIiLEAST8, PRIiLEAST16, PRIiLEAST32, PRIiLEAST64 PRIoFAST8, PRIoFAST16, PRIoFAST32, PRIoFAST64 PRIoLEAST8, PRIoLEAST16, PRIoLEAST32, PRIoLEAST64 PRIuFAST8, PRIuFAST16, PRIuFAST32, PRIuFAST64 PRIuLEAST8, PRIuLEAST16, PRIuLEAST32, PRIuLEAST64 PRIxFAST8, PRIxFAST16, PRIxFAST32, PRIxFAST64 PRIxLEAST8, PRIxLEAST16, PRIxLEAST32, PRIxLEAST64 PRIXFAST8, PRIXFAST16, PRIXFAST32, PRIXFAST64 PRIXLEAST8, PRIXLEAST16, PRIXLEAST32, PRIXLEAST64 Note that these macros will eventually need to be defined according to the toolchain-specified types when the `zephyr_stdint.h` hack is removed in the future; refer to the the GitHub issue zephyrproject-rtos#46032 for more details. Signed-off-by: Stephanos Ioannidis <root@stephanos.io>
While I am not a very big fan of this either, this is the only way to be portable across different compilers and we have no option but to do this, especially as we start introducing more non-GNU compiler support to the Zephyr.
Why do we have to bow to them?
Zephyr is becoming the most important RTOS project out there. That
should be sufficient incentive for those other compilers to create a
config with sane defaults for Zephyr.
…
I believe this is an inevitable decision for us.
--
Reply to this email directly or view it on GitHub:
#46032 (comment)
You are receiving this because you were mentioned.
Message ID: ***@***.***>
|
|
I think I slightly disagree with this. The (significant) benefit of using printf is that the C compiler will typecheck arguments against the format strings. Any printf-like-function which doesn't match the printf formatting conventions cannot use the compiler's printf typechecking, so you lose that benefit. Even if you call it If you don't like Using the standards also lowers the barrier to entry for new Zephyr developers; they can leverage knowledge gained in other environments without having to constantly second-guess where Zephyr differs. There are other benefits, like being able to validate the implementation by using existing test suites and using printf to implement printk and cbprintf (which Zephyr already does when using picolibc). |
@stephanosio What is the status of this? Will this make it into the 3.4 release? |
Looking forward to c23, we should expect to have %w format specifiers in the standard libraries at some point soon, which means we can use %w32d instead of PRId32. |
This does not seem realistic. Some of Linus Torvalds' complaints about gcc have fallen on deaf ears. Kernels are a small "market" for compilers. Also, Linux supports just one toolchain (+ clang, a... gcc emulator on top of LLVM) whereas Zephyr bends over backwards to support unknown and proprietary toolchains thanks to an elaborate CMake indirection layer. Zephyr also has a licensing model welcoming closed source libraries and even a tool for binary blobs. Expecting to rally all these in the crusade against PRIu64 is not realistic.
Everyone agrees PRIu64 is incredibly ugly and a huge mistake. Probably for backwards compatibility, always a huge problem with C/C++. But why is it necessary to (over)engineer a solution that avoids PRIu64 at all costs? Code style and readability matter, a lot. But "necessary" means something different. If there is a simple and reliable solution that avoids PRIu64 for C and C++ across a reasonable range of unmodified toolchains then perfect; "send patches!" However @stephanosio's experience and expertise + the length of this thread and of a good few other before make me feel very sceptical. Speaking of patches, does Zephyr already have some build-time test case(s) for this?
Great news! So the compilers "bowed" after all. Now we can get rid of PRIu64 around 2030 ;-) |
@jgl-meta This is still under discussion and is unlikely to make into the 3.4 release.
@keith-packard That is a great news; but, using those format specifiers will require us to set the minimum required C standard version for Zephyr to C23, which I do not think is going to happen in the foreseeable future.
@marc-hb Given that we are still debating whether it is reasonable to require C11 in 2023, maybe 2040 is more realistic :) |
Another option could be to add the C23 As long as we use the This will effectively allow us to not use the wart that |
🥲 Could rust help here? |
Is your enhancement proposal related to a problem? Please describe.
Zephyr currently overrides the toolchain default C99 integer types (
[u]intN_t
) to fixed types such thatint32_t
can always be processed without a size modifier, andint64_t
withll
size modifier.This "hack," introduced in #16645, has caused various toolchain compatibility issues (e.g. #37712 (comment)) and will not be sustainable as we start adding more non-GNU toolchains as supported toolchains for Zephyr -- note that
__INT32_TYPE__
and its friends defined byzephyr_stdint.h
are non-standard and the toolchain default C library is not obliged to set up the type system using these.Describe the solution you'd like
Stop re-defining the toolchain default types (i.e. remove
zephyr_stdint.h
) and always use the C99 inttypes macros with the C99 integer types; e.g.:For this, the following changes need to be implemented:
inttypes.h
to not assume the fixed types defined byzephyr_stdint.h
; the size modifiers need to be resolved based on the native toolchain types.zephyr_stdint.h
.Additional context
It may be useful to go over the following related issues:
hh
with newlib-nano #45336The text was updated successfully, but these errors were encountered: