-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
[C++20][Modules] deleted definitions should be allowed in header units #60079
Comments
@llvm/issue-subscribers-clang-modules |
Might be simpler to consider deleted definitions to be implicitly inline - it's more like how it works in practice. |
I think it's a pretty simple either implementation or spec bug/omission. I doubt anyone intended to disallow deleted definitions in header units. |
Yeah, it sounds good. In the language side, only the names have linkage instead of declarations. So from the perspective of language, the name of the deleted function can be external. But I guess it may be fine for the compiler to treat it as implicitly inline since the deleted definitions should never appear in object files.
According to @iains , some people of wg21 are discussing to remove such rules in future. For this issue report, I feel it makes sense to skip the rule. Although LLVM16 is going to be branched, we don't need to be hurry since it should be possible to backport the patch since this is a (severe) regression bug. (This section is not related to the issue report), Out of curiosity, I want to ask how do you use header units? What is the build system you're using? Currently, wg21 is still discussing how to support header units. It looks like cmake wouldn't support header units until they get a conclusion. I want to say that I feel the header units is not well tested and used a lot in practice now. So I am curious about your practice and using experience for header units. |
well, we anticipated that this could cause some code to flag an error. For clarification: there are some WG21 members who are saying they think this rule should be removed. I am not aware of any paper that actually proposes its removal (so that as far as I know, the rule is the same in C++23). I think that the
Otherwise - from an engineering perspective If a header contains a non-inline external definition, then it can only be used once (since multiple imports are an ODR violation), therefore actually it probably makes little sense to make it into a header unit, that is just extra build-time work - (it might as well be included textually in the single place it is used). |
The issue with the deleted function is that it is a definition - if it was a declaration, the rule would not fire. I suppose that we could implement @dwblaikie 's suggestion to make them implicitly inline. I wonder if there are other similar cases that will trip us up. I will file a question with core now - obviously, clang can elect to make a dialect as @ChuanqiXu9 has proposed in https://reviews.llvm.org/D141894 but let's see what answer we get first? |
Thanks for the clarification. That's what I understood. Maybe I didn't tell things clearly.
I think the behavior is correct. Since the name '_S_alignment' has not internal linkage. According to http://eel.is/c++draft/basic.link,
Year, since this is the defect of the language spec. It would be better to ask this in core. But clang16 is going to be branched. Even if we can backport patches before the formal release, the time is really limited. I really feel upset that we can't compile a hello world example for header units. So I think we should both accept the patch and ask questions in wg21. Otherwise, we have to mark header units as unusable in clang16, which seems like a big regression. |
Since Clang16 is going to be branched. I don't think we should perform the action now. It has wider impact.
Yeah, it should be fine to wait for few days. |
the response from core is: They already are, see https://eel.is/c++draft/dcl.fct.def.delete#4 "A deleted function is implicitly an inline function ([dcl.inline])." .. so that particular issue can be resolved (I will take a look at it today). |
Yeah, it looks like we did some bad for the deleted function in header units or modules. Since clang have already handle the case in |
@iains After I took a quick look, I found the reasons. llvm-project/clang/lib/Parse/Parser.cpp Lines 1392 to 1417 in 951cf65
We diagnose in the call of the line 1392. But the deleted function is marked as implicit inline at line 1414. So here is the problem. We should find other place to diagnose this. |
edit: edit2: |
OK. So I can look for an alternate place for the function diagnostic. Not sure what we should do about the other cases, it seems that the short-term solution would be a dialect and then some discussion in WG21 about the intent. |
The form |
So it looks like we can fix things properly. So the dialect patch would be abandoned. |
yeah, I have a patch in test for the deleted/defaulted functions. |
definitions in header units Address part of #60079. Since the the declaration of a non-inline static data member in its class definition is not a definition. The following form: ``` class A { public: static const int value = 43; }; ``` should be fine to appear in a header unit. From the perspective of implementation, it looks like we simply forgot to check if the variable is a definition... Reviewed By: iains Differential Revision: https://reviews.llvm.org/D141905
I am using CMake, however I have some custom functions set up that make modules and header units usable for Clang with custom commands. While by no means perfect, complete, or even well portable to other proejcts, it's sufficient to experiment until official support lands. The first is a header that includes all of the standard library. The reason for this are some functions (-objects) marked static, which are defined in one header and used in another. With both being header units, the function (-object) becomes unavailable due to internal linkage, so I put all of the standard library into one header unit for now. That header unit is re-exported by a named module Std, which I import throughout one project until import std; becomes available. The second is precompiling third party headers (such as boost). Even though they are only used at one place each, I found this useful to separate the build times for measuring how long a specific module takes to compile if all dependencies are already precompiled. The third is for headers that contain (almost) only macros. It's true however that there would be no need for a header unit in this case and it could as well stay an include. In general, I prefer using named modules to header units though. My overall experience with header units and modules has been great. Build times can go way down, it's easier to narrow down the location of bugs, and for tests cases it's possible to simply omit one import and replace it with a mock. That wouldn't be possible with transitive headers. The most trouble I had was transitioning from headers with a lot of type forward declarations to named modules, as you can't have a forward declaration in one module and it's definition in another. The related link errors aren't the most intuitive, but that's what I expect from new features. |
That is very encouraging. |
Thanks for the written-up! It is helpful. I didn't image that the named modules are helpful for testing. Interesting.
Yeah, it needs some refactoring. I am not sure if you know you can have a forward declaration in a partition module unit and define it in another partition module unit. In this way, we may avoid the link errors you mentioned. |
definitions in header units Address part of llvm/llvm-project#60079. Since the the declaration of a non-inline static data member in its class definition is not a definition. The following form: ``` class A { public: static const int value = 43; }; ``` should be fine to appear in a header unit. From the perspective of implementation, it looks like we simply forgot to check if the variable is a definition... Reviewed By: iains Differential Revision: https://reviews.llvm.org/D141905
Let me clarify that this works only for free functions found by ADL. For example defining operator == for Class in a submodule Class.Compare. You import that throughout the project, but in the test you import Class.CompareMock instead. Class.CompareMock may import but not export Class.Compare. As long as the mock operator == can be found by lookup (using declaration in the test file or global namespace) and the other one can't, this works great.
I have made use of partitions for exactly that reason. However there are 2 different uses of forward declarations. One is for cyclic dependencies in which case there is no way around partitions. This isn't a problem as files with cyclic dependencies are often close together on the filesystem and putting them in the same module is easy. Of course, resolving the cycles in the first place would be preferable. The other use case is to save an include in the header to shorten build time. This can happen with 2 files that are in very different parts of the project (or even 2 different projects). Putting those in the same module may just require putting most of the entire project in the same module due to dependency chains. That defeats one of the main purposes of modules. With modules however, there is no need for these kinds of forward declarations and they should just be imports. Finding just those forward declarations and replacing them by imports can be bothersome, but given the improved build time I think it's worth the effort. I think include-what-you-use with --no_fwd_decls can be helpful in replacing forward-declarations with includes before transitioning to modules, but it's not perfect either. |
Interesting.
Yeah, it would be best to solve the dependencies in the first place. Another solution may be the use of |
tryied the patch seems to work
|
with a more complexe scenario i get
|
That seems to be a different diagnostic - do you find it to be in some way related ? |
maybe it is not a header unit support problem but a bug of libc++ ? |
perhaps, but when I build ostream in isolation, this does not fire: gives only a warning about my changes are based on main edit - and the (start of the) output of
|
i just build a llvm from df34581 with ChianXqi p1689 and clang-scan-deps patches building the header only, the error is in the .cpp file when importing ostream and complex headers |
OK .. at present, this does not seem to be the same bug, is it possible for you to produce an isolated reproducer (and maybe file a separate bug) ? |
import <iostream>;
import <complex>;
using namespace std;
int main(int argc, char** argv)
{
cout << "hello world!" << endl;
return 0;
} |
I can repeat with my build, but cannot yet see any connection to the diagnostic that is the subject of this PR. |
so it is not, i've made an other issue #60358 |
No worries, I'm well aware this can happen when using unreleased builds. I've been using a build from January 8th in the meantime. The patch seems to work this time, no issues with either libc++ or libstdc++, so thank you. I hope this lands soon on apt.llvm.org. Regarding the other issue #60358 it seems to be the same issue as #58540. The best current workaround I could come up with was to include all of the standard library headers into one header std.hpp and using this one as the only header unit for the standard library. By force importing it with a header import_std.hpp and
into third party files, the include guards exported by std.hpp.pcm will disable all includes of the standard library and use the header unit instead. |
This addresses part of #60079 The test for external functions was not considering function templates. Differential Revision: https://reviews.llvm.org/D142704
This addresses part of llvm/llvm-project#60079 The test for external functions was not considering function templates. Differential Revision: https://reviews.llvm.org/D142704 (cherry picked from commit cdd44e2)
This addresses part of llvm/llvm-project#60079 The test for external functions was not considering function templates. Differential Revision: https://reviews.llvm.org/D142704
This addresses part of llvm/llvm-project#60079 The test for external functions was not considering function templates. Differential Revision: https://reviews.llvm.org/D142704 (cherry picked from commit cdd44e2)
No issues here. Tested libc++, libstdc++, C++20 and C++2b mode. |
This addresses part of llvm/llvm-project#60079 The test for external functions was not considering function templates. Differential Revision: https://reviews.llvm.org/D142704
definitions in header units Address part of llvm/llvm-project#60079. Since the the declaration of a non-inline static data member in its class definition is not a definition. The following form: ``` class A { public: static const int value = 43; }; ``` should be fine to appear in a header unit. From the perspective of implementation, it looks like we simply forgot to check if the variable is a definition... Reviewed By: iains Differential Revision: https://reviews.llvm.org/D141905
Address part of llvm/llvm-project#60079. Deleted and Defaulted functions are implicitly inline, but that state is not set at the point that we perform the diagnostic checks for externally- visible non-inline functions; check the function body type explicitly in the diagnostic. Differential Revision: https://reviews.llvm.org/D141908
…owed in header units Summary: These is no sense to report this cases as an error or add `inline` explicitly in this cases. If it is not required in normal headers. Similar to llvm#60079. Test Plan: check-clang Reviewers: @kaimfrai, @ChuanqiXu9 Subscribers: @iains, @EugeneZelenko, @dwblaikie, @Arthapz
…lowed in header units (llvm#98309) Summary: There is no sense to report these cases as an error or add `inline` explicitly in these cases, if it is not required in normal headers. Similar to llvm#60079. Test Plan: check-clang
This addresses part of llvm/llvm-project#60079 The test for external functions was not considering function templates. Differential Revision: https://reviews.llvm.org/D142704
This addresses part of llvm/llvm-project#60079 The test for external functions was not considering function templates. Differential Revision: https://reviews.llvm.org/D142704
Address part of llvm/llvm-project#60079. Deleted and Defaulted functions are implicitly inline, but that state is not set at the point that we perform the diagnostic checks for externally- visible non-inline functions; check the function body type explicitly in the diagnostic. Differential Revision: https://reviews.llvm.org/D141908
Since non-inline external definitions in header units were disallowed in commit/335668b, using standard header units has become impractical.
In particular, the example given at https://clang.llvm.org/docs/StandardCPlusPlusModules.html#header-units
no longer compiles with current Clang 16 (tried libc++ and libstdc++) while it did work before.
Errors include but are not limited to:
libstdc++
libc++
Unfortunately, this issue isn't limited to libstdc++ and libc++. Not only do other headers often include standard headers, but it's entirely resonable to have = delete; in header files. They are therefore also unusable as header units. Transitioning from headers to header units is thus limited to a small subset of headers, while Clang 15 allowed many more. While I think it's the correct default to error out in these cases, I would appreciate an explicit flag that skips over this rule, at least until P1502R1 (Standard library Header Units) or P2465R3 (std module) are implemented. For example:
Without such a flag I would find it very hard to make use of standard header units as they are implemented right now.
The text was updated successfully, but these errors were encountered: