-
Notifications
You must be signed in to change notification settings - Fork 12.4k
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
[Headers] Don't declare unreachable() from stddef.h in C++ #86748
Conversation
@llvm/pr-subscribers-backend-x86 @llvm/pr-subscribers-clang Author: Ian Anderson (ian-twilightcoder) ChangesEven if __need_unreachable is set, stddef.h should not declare unreachable() in C++ because it conflicts with the declaration in <utility>. Full diff: https://github.com/llvm/llvm-project/pull/86748.diff 1 Files Affected:
diff --git a/clang/lib/Headers/__stddef_unreachable.h b/clang/lib/Headers/__stddef_unreachable.h
index 518580c92d3f5d..61df43e9732f8a 100644
--- a/clang/lib/Headers/__stddef_unreachable.h
+++ b/clang/lib/Headers/__stddef_unreachable.h
@@ -7,6 +7,8 @@
*===-----------------------------------------------------------------------===
*/
+#ifndef __cplusplus
+
/*
* When -fbuiltin-headers-in-system-modules is set this is a non-modular header
* and needs to behave as if it was textual.
@@ -15,3 +17,5 @@
(__has_feature(modules) && !__building_module(_Builtin_stddef))
#define unreachable() __builtin_unreachable()
#endif
+
+#endif
|
I'm checking with the C and C++ Compatibility study group (SG22) for what's expected here. |
Even if __need_unreachable is set, stddef.h should not declare unreachable() in C++ because it conflicts with the declaration in <utility>.
bfa1640
to
e67c757
Compare
Prior to adding |
Why do the Clang builtin headers even try to define this? Shouldn't this be provided by the platform's C library instead? I am really confused about what's the intended layering here and it seems to me like Clang is overstepping its responsibilities by basically implementing parts of the C library. |
I did that in a9797d7 It's a freestanding feature in C23, so we need to expose it from We set llvm-project/clang/lib/Headers/stddef.h Line 59 in c388690
We only include llvm-project/clang/lib/Headers/stddef.h Line 102 in c388690
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Marking as requesting changes so this isn't accidentally landed when there's an open question as to why it's needed in the first place.
Thanks for the explanation. I think I don't understand the relationship between that feature being freestanding and the fact that it's implemented in the Clang builtin headers. I might be misunderstanding (or just ignorant of) the Clang approach for freestanding in C. Doesn't Clang still rely on there being a C library even in Freestanding mode? |
In C, freestanding used to be "here's a pile of macros", so the compiler was able to easily supply a freestanding set of headers. That's changed with C23 (for example, we now include |
We do? I don't see those in lib/Headers |
Sorry for the confusion! "We" being WG14 (the C committee). We (Clang) haven't done anything about either of these headers as we're expecting the user to have to find a freestanding runtime library that provides them. |
Thanks for the explanation, @AaronBallman . I think I am generally deeply confused about what should be provided by the compiler and what should be provided by the C Standard Library on any given platform. From your reply, it looks like there's no clear rule and Clang basically provides anything that seems "easy enough" to provide. I still don't understand how that works in case you do end up including a header from the platform that (re)defines The same problem also applies today to e.g. Anyway, this might turn out to be nothing more than a documentation issue, but in any case I think it would be valuable to write down how this is intended to work somewhere (even if only in a comment), since I suspect it's not clear to most people. I work on the C++ Standard Library and I don't understand how this works in our own implementation :-) |
We do indeed run into issues, the redeclarations cause two problems.
We're needing to carefully remove our duplications in the Apple headers and always use the clang builtins. The coupling is unfortunate, but I don't know of any practical way around it. |
For C17 and earlier, the rule was that we provided everything required for freestanding by the standard, and anything else the user wanted had to come from a library the user supplied. For C23, which we're still in the process of implementing, the situation is different and we've not documented what the expectations are yet. I suspect we're going to end up requiring the user to provide their own freestanding library implementation and the compiler will provide only the bits that the compiler has to provide (things like The
If the platform defines
Definitely agreed that we need to document this, but we need to figure out what we want to document in the first place -- some of this is new-ish territory because of the additional interfaces. I think we may need to look at what existing freestanding CRTs and hosted CRTs expect to help give us a path forward? |
Thanks, this clarifies a lot of things in my head.
Personally, my naive take on this is that Clang should basically define the least amount of things from the C Standard Library, and let a proper C Standard Library implement it. Having interacted with a good number of folks who work on C libraries (including embedded flavors), I think they all expect that they need to provide basically all the declarations/definitions in the C Standard, except perhaps stuff from A C library is not super complicated in the first place, so I don't think we're saving a lot of people a lot of time by trying to give them a few definitions here and there. Additionally, there's many freestanding-friendly C libraries around so it's not like most people have to write their own. That's just my immediate reaction to this. It's pretty naive but at this specific point I believe we should basically not provide any Clang builtin headers except the ones that clearly provide compiler-related stuff (e.g. SLA note: I'll be OOO for a few days so I'll only see replies early next week. |
FWIW I don't think they are morally similar. |
I think we're saying the same thing but in different ways. I'm saying that the compiler implementation is generally what's positioned best in terms of what the optimizer can work with but the library implementation can be positioned to have a conforming implementation with different QoI properties. There's benefits to allowing either approach; the compiler can provide a high-quality implementation so that C standard library maintainers don't have to do anything but the compiler's headers should not prevent a C standard library from providing additional/different functionality.
There's two angles to this. We provide something already today and we don't want to break code, so whatever we provide tomorrow needs to have a good enough user experience that code either still works, or users have an easy way to react to the changes. So reducing the set of interfaces we provide is risky, though I would argue that the C23 offerings are the easiest for us to change because I'm unaware of any fully conforming C standard library for C23 yet. The other angle is where we want to be in an ideal situation. I do not think think "because of GCC compatibility, let's do what glibc does" is the correct default ideal situation for us to look at -- if we're going to make structural changes and document a policy, I would love for us to gather feedback from glibc, MSVC CRT, musl, ulibc, llvm-libc, bionic, and any other implementations (including STL implementations) we can find contacts for so we can intentionally support a wide set of C standard libraries from Clang without introducing significant burdens for C++ standard library maintainers. For example, we have problems like "who provides what for freestanding", but we also have serious issues with "who defines what" for predefined macros, because some of them ( So I think I see a short-term and a long-term project here. Long-term, we should find a way to migrate our existing headers to a more intentional approach (whatever that may be). Short-term, we need to do something for WDYT? Also, I'd love to hear from some llvm-libc folks. @jhuber6 @lntue (feel free to tag others) |
I'm not sure whether you mean it, but I'd expect that it's handled like |
Yeah, that would one way to handle it, but it does still defy some user expectations (and perhaps that's fine):
Uncommenting But another way WG21 could perhaps approach this is:
which retains the macro rather than the function. |
I mean that |
Yeah, that could be done too, but it seems pretty unclean IMO (at least when aiming for an ideal implementation) -- C++ changing the contents of C headers in ways that are observable to the user is pretty gross. For example, writing a C header file that includes stddef.h and some inline functions may then compile quite differently whether included in a C TU or a C++ TU. e.g.,
where the expectation is that because we're including stddef.h, the only time the fallback is needed is with C standard libraries that don't yet support C23. It's a pretty contrived example, so don't read into it too much, it's more that I'm not certain of the usability of quietly changing standard interfaces in the C standard library. |
+1
Weak +1 (+1, but similar levels of unsuredness). On the llvm-libc side, we're running into pain with some of the compiler provided headers, too, particularly because they tend to themselves include additional headers (the xmm intrinsic headers including stdlib for example). I'm very tempted to resort to typedef __SIZE_TYPE__ size_t;
... and just be done with compiler resource headers. I'll suspect that cc @enh for visibility. |
Re: there being observable differences between a header in C vs C++ mode, that's already a thing. The definition for |
That's actually really bad. Both libc++ and libstdc++ assume that these are either not provided at all by the libc or are macros. This will inevitably lead to ambiguous overloads. |
I'm fine with changing our definition to better coordinate with libc++, though we should probably discuss that on someplace other than this patch. |
I do wonder if we could have the broader builtin headers discussion independent of this patch? Is everyone happy with this patch? We can keep talking about the builtin headers in here independent of merging right? |
FWIW, I did verify that it's very unlikely the changes in this PR will break existing code: https://sourcegraph.com/search?q=context:global+__need_unreachable+-file:.*clang.*&patternType=keyword&sm=0, so that's a good thing.
I guess I don't see these as independent topics; if we decide that C++ mode should not have observable differences in C headers, then the changes here are incorrect. I think we should have this discussion in a broader context (like Discourse) before moving forward with these changes. Also, I'd still like an explanation for this question:
because it may turn out we don't need these changes in the first place because the issue is elsewhere. |
Right now I just noticed that in a C++ test I was writing that stddef.h alone doesn't give me unreachable, but __needs_unreachable does. And that's probably wrong because unreachable belongs to <utility> in C++ and not stddef.h. The C++ standard has some frustrating divergence with the C standard as to what the c stdlib headers declare... |
I don't yet agree that it's wrong -- you define the macro saying you want
|
I was thinking that a lot of the low level Apple SDK C headers use the |
It's a danger, to be sure, but I don't think it warrants a surgical change here.
It's not inconsistent with llvm-project/clang/lib/Headers/stddef.h Line 54 in b15d27e
__need_nullptr_t based on it being C++ or C23.llvm-project/clang/lib/Headers/stddef.h Line 96 in b15d27e
__need_nullptr_t is defined.https://github.com/llvm/llvm-project/blob/main/clang/lib/Headers/__stddef_nullptr_t.h defines nullptr_t in both C and C++ mode.
|
It only defines it in C++ in specific MSC circumstances ( |
Ah, you're right, we do define it in C++ but only sometimes. That seems to be a rather ancient behavior: 158dec5 Regardless, for this patch, it's a hard call either way. GCC does not define On balance, I think we should not make changes here unless/until WG21 rebases on top of C23 because this seems to be an issue of the SDK not adhering to the existing contract for including this header with its special |
I've heard back from the compatibility study group and the sentiment there is to not expose We should probably take a tour through the headers Clang exposes and make sure we're paying attention to the conformance aspects of all the C headers when included in C++ mode. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, though we should have a release note about the change because we've been exposing the macro since Clang 17. I don't think this warrant a potentially breaking change notice, though, just a regular bugfix one.
I've spotted some problems: So I've filed #87668 to track this. |
I think we weren't exposing
( And then it's only exposed in Clang 18 if something sets the new Is that still worth mention in the release notes do you think? |
Oh duh, that's right, this requires someone to define the |
👍 thanks for bearing with me on this! |
Likewise! :-) |
Even if __need_unreachable is set, stddef.h should not declare unreachable() in C++ because it conflicts with the declaration in \<utility>. (cherry picked from commit df69a30)
Even if __need_unreachable is set, stddef.h should not declare unreachable() in C++ because it conflicts with the declaration in \<utility>. (cherry picked from commit df69a30)
Even if __need_unreachable is set, stddef.h should not declare unreachable() in C++ because it conflicts with the declaration in <utility>.