-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[C++20] [Modules] Introduce -fskip-odr-check-in-gmf #79959
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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -457,6 +457,28 @@ Note that **currently** the compiler doesn't consider inconsistent macro definit | |
Currently Clang would accept the above example. But it may produce surprising results if the | ||
debugging code depends on consistent use of ``NDEBUG`` also in other translation units. | ||
|
||
Definitions consistency | ||
^^^^^^^^^^^^^^^^^^^^^^^ | ||
|
||
The C++ language defines that same declarations in different translation units should have | ||
the same definition, as known as ODR (One Definition Rule). Prior to modules, the translation | ||
units don't dependent on each other and the compiler itself don't and can't perform a strong | ||
ODR violation check. Sometimes it is the linker does some jobs related to ODR, where the | ||
higher level semantics are missing. With the introduction of modules, now the compiler have | ||
the chance to perform ODR violations with language semantics across translation units. | ||
|
||
However, in the practice we found the existing ODR checking mechanism may be too aggressive. | ||
In the many issue reports about ODR violation diagnostics, most of them are false positive | ||
ODR violations and the true positive ODR violations are rarely reported. Also MSVC don't | ||
perform ODR check for declarations in the global module fragment. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think there are many kinds of issues here and the text might not be conveying the right idea.
I read this paragraph as talking about 3 in particular, and giving the idea that the ODR, as specified, ends up barfing at too many harmless violations, and we are finding that it's more trouble than it's worth as we enforce it in more situations. Regarding MSVC, I don't think we normally would relax standards conformance so broadly in order to be consistent with another compiler. For example, see how we handle compatibility with MSVC regarding delayed template parsing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For 1, I don't understand why it is relevant here. I know what you're saying, but I just feel it is not related here. We're talking about ODR checker, right? For 2, I thought the term For MSVC, it is a simple supporting argument here. I mean, the document is for users, and what I want to say or express is, while all of us know and understand it is not good, but you (the users) don't need to be too panic since MSVC did the same thing. BTW, for the delayed template parsing, I remember one of the supporting reason to disable that in C++20 is that MSVC disable that in C++20 too. But this is not relevant here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That is surprising to me, because the issue you linked in the original commit, #78850, seems to be a case of 3) to me, mostly harmless but nonetheless an ODR violation. This is not just about the semantics of the resulting program itself, think about debug info: There will be two definitions of Even if the standard wording talks about the definitions consisting of the same series of tokens, this to me is a case of rule-as-written vs rule-as-intended. Ie the tokens not only have to be same textually, but they need to refer to the same entity.
See above, but I am also currently internally tracking two separate bugs which are a case of 1), and the ODR checker was just an innocent witness. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yeah, the original reproducer looks like a user's bug. But according to the comment, it should be fine since the function
Great insight. I feel this is meaningful. Maybe we need to reach out WG21 to confirm this. Can you access the mailing list of WG21?
Sorry. I still don't understand why it is related to the patch itself. I feel you're saying, when we compile pcm to obj, we would do some additional works, then we have divergence between compliing source to obj directly with a two phase compilation (source to pcm, pcm to obj). But I feel it is not related to the patch itself or the document here. (I am open to discuss it. Just want to clarify to make us clear about whay we're saying) And for the direction to disable ODR checks in GMF as I commented in #79240, I thought it as bad initially. But the explanation from MSVC developer moves me. Since the entities in the GMF are basically from headers. Then it wouldn't be a regression in some level. Also I saw many issue reports complaining the false positive ODR violation diagnostics. In fact, after I disabled the ODR checks in GMF, there 2 people reporting github issues saying they feel good and one people sent me a private email to say it solves his problems. So I feel this is the "correct" way to go while I understand it is not strictly correct. Also I feel your concern here is about deeper other problems. (e.g., what is ODR violation?) And I want to backport the series patches to 18.x. So I am wondering if we can approve this patch and I can start to backport it. Then we can discuss your concern in other places. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I am not a member yet, but I know a few people in there.
So a little background: The issue #78850 that was referenced in the original commit, which seems to be the main motivator for that change, was reported as two parts:
I have reason to believe, based on similarity to clang bug I am currently working on, that 2) is unrelated to 1). If you look at this from another angle, this reduction was performed by the user / reporter, and reducing a false-positive ODR violation repro is hard, because it's so easy you would introduce a real ODR-violation during a reduction step. So I feel we are taking a drastic step here based on potentially incorrect evidence. If we can confirm what I am saying, then don't you agree we would be losing the whole casus belli against the GMF ODR checker, and we could go back to square one, revert the whole thing, and take a better look at this? Even if that is not the only issue, my perception is that other supposed ODR violations could be simple issues like this.
I have never seen MSVC source code, so I don't know how relevant it would be for comparison, but I think clang has enough differences in how sema is applied to textual source vs PCM, that this would give me pause and I wouldn't be so sure. On the other hand, this does not seem to be a regression, since the whole C++20 modules thing is just coming up and we are talking about a new feature and new users.
See above. Would you mind waiting a little for my patch, for us to double check we are taking this step with solid footing? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Do you need me to send this? I feel this is the deepest problem in the discussion.
Is there an issue report?
To me, the issue is
If we can have a stable and correct ODR checker, we have no reasons to not enable it.
My experience is "yes, a lot of false positive ODR violations can be fixed by simple patch". The only problem is that there is too many. Or this may be the reason I feel the current ODR checker is not stable enough.
Or let's take a step back. How about always enabling the ODR checker for decls in the GMF but offering a driver flag "-fskip-odr-check-in-gmf"? Then the users can get better experience and offer more precise issue report. Also we don't need to be in the panic of losing standard conformance.
I think it is saying the users converting a repo from headers to modules. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Sure, we can move that part of the discussion over there.
Not on the llvm issue tracker yet, I am working on getting issue + patch up soon.
How are we tracking these issues, is there a special tag for them? We could take a better look, attack this systematically.
Right, but reducing these issues takes a lot of effort. On the other hand, it's possible they all reduce to a few simple cases.
Yeah, one concern is losing the bug reports. So I will go ahead and approve this, even though I feel this might have been too hasty, as long as we are running the clang test suite as before, then we can make progress in fixing all the problems and circle back to always enabling the checker.
Right, but that's not a regression. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I'll add you as CC if they agree to do so. I remember there is policy to forbid that. But I am not sure.
Got it. Not bad. But generally an issue can let us know what's going on and maybe we meet such issues too.
No. All of them are falling into the
(Maybe there are more) Note that not all of them fails due to bugs in ODR checker. Some of them fails due to bugs in other places and the ODR checker just fires it. Although the result is still false positive ODR violation diagnostics. And there are another camp about,
Same here.
Thanks. It is always easy to enable the check by default any time. |
||
|
||
So in order to get better user experience, save the time checking ODR and keep consistent | ||
behavior with MSVC, we disabled the ODR check for the declarations in the global module | ||
fragment by default. Users who want more strict check can still use the | ||
``-Xclang -fno-skip-odr-check-in-gmf`` flag to get the ODR check enabled. It is also | ||
encouraged to report issues if users find false positive ODR violations or false negative ODR | ||
violations with the flag enabled. | ||
mizvekov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
ABI Impacts | ||
----------- | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// RUN: %clang -std=c++20 -### -c %s 2>&1 | FileCheck %s | ||
// RUN: %clang -std=c++20 -fno-skip-odr-check-in-gmf -### -c %s 2>&1 \ | ||
// RUN: | FileCheck %s --check-prefix=UNUSED | ||
// RUN: %clang -std=c++20 -Xclang -fno-skip-odr-check-in-gmf -### -c %s 2>&1 \ | ||
// RUN: | FileCheck %s --check-prefix=NO-SKIP | ||
|
||
// CHECK: -fskip-odr-check-in-gmf | ||
// UNUSED: warning: argument unused during compilation: '-fno-skip-odr-check-in-gmf' | ||
// UNUSED-NOT: -fno-skip-odr-check-in-gmf | ||
// NO-SKIP: -fskip-odr-check-in-gmf{{.*}}-fno-skip-odr-check-in-gmf |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,12 @@ | |
// RUN: %clang_cc1 -std=c++20 %t/A.cppm -emit-module-interface -o %t/A.pcm | ||
// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -I%t -DDIFFERENT %t/B.cppm -verify | ||
// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -I%t %t/B.cppm -verify | ||
// | ||
// Testing the behavior of `-fskip-odr-check-in-gmf` | ||
// RUN: %clang_cc1 -std=c++20 -fskip-odr-check-in-gmf %t/A.cppm -emit-module-interface -o %t/A.pcm | ||
// RUN: %clang_cc1 -std=c++20 -fskip-odr-check-in-gmf -fprebuilt-module-path=%t -I%t \ | ||
// RUN: -DDIFFERENT -DSKIP_ODR_CHECK_IN_GMF %t/B.cppm -verify | ||
|
||
|
||
//--- foo.h | ||
#ifndef FOO_H | ||
|
@@ -70,6 +76,16 @@ module; | |
export module B; | ||
import A; | ||
|
||
#ifdef SKIP_ODR_CHECK_IN_GMF | ||
// [email protected]:* {{call to object of type '__fn' is ambiguous}} | ||
// expected-note@* 1+{{candidate function}} | ||
#elif defined(DIFFERENT) | ||
// [email protected]:41 {{'__fn::operator()' from module 'A.<global>' is not present in definition of '__fn' provided earlier}} | ||
// expected-note@* 1+{{declaration of 'operator()' does not match}} | ||
#else | ||
// expected-no-diagnostics | ||
#endif | ||
|
||
template <class T> | ||
struct U { | ||
auto operator+(U) { return 0; } | ||
|
@@ -87,10 +103,3 @@ void foo() { | |
|
||
__fn{}(U<int>(), U<int>()); | ||
} | ||
|
||
#ifdef DIFFERENT | ||
// [email protected]:* {{call to object of type '__fn' is ambiguous}} | ||
// expected-note@* 1+{{candidate function}} | ||
#else | ||
// expected-no-diagnostics | ||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,12 @@ | |
// RUN: %clang_cc1 -std=c++20 %t/mod4.cppm -fmodule-file=mod3=%t/mod3.pcm \ | ||
// RUN: -fsyntax-only -verify | ||
|
||
// Testing the behavior of `-fskip-odr-check-in-gmf` | ||
// RUN: %clang_cc1 -std=c++20 %t/mod3.cppm -fskip-odr-check-in-gmf \ | ||
// RUN: -emit-module-interface -o %t/mod3.pcm | ||
// RUN: %clang_cc1 -std=c++20 %t/mod4.cppm -fmodule-file=mod3=%t/mod3.pcm \ | ||
// RUN: -fskip-odr-check-in-gmf -DSKIP_ODR_CHECK_IN_GMF -fsyntax-only -verify | ||
|
||
//--- size_t.h | ||
|
||
extern "C" { | ||
|
@@ -57,13 +63,17 @@ export module mod3; | |
export using std::align_val_t; | ||
|
||
//--- mod4.cppm | ||
// This is actually an ODR violation. But given https://github.com/llvm/llvm-project/issues/79240, | ||
// we don't count it as an ODR violation now. | ||
// expected-no-diagnostics | ||
module; | ||
#include "signed_size_t.h" | ||
#include "csize_t" | ||
#include "align.h" | ||
export module mod4; | ||
import mod3; | ||
export using std::align_val_t; | ||
|
||
#ifdef SKIP_ODR_CHECK_IN_GMF | ||
// expected-no-diagnostics | ||
#else | ||
// [email protected]:* {{'std::align_val_t' has different definitions in different modules; defined here first difference is enum with specified type 'size_t' (aka 'int')}} | ||
// [email protected]:* {{but in 'mod3.<global>' found enum with specified type 'size_t' (aka 'unsigned int')}} | ||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
// RUN: rm -rf %t | ||
// RUN: mkdir -p %t | ||
// RUN: split-file %s %t | ||
// | ||
// Baseline testing to make sure we can detect the ODR violation from the CC1 invocation. | ||
// RUNX: %clang_cc1 -std=c++20 %t/a.cppm -emit-module-interface -o %t/a.pcm | ||
// RUNX: %clang_cc1 -std=c++20 %t/b.cppm -emit-module-interface -o %t/b.pcm | ||
// RUNX: %clang_cc1 -std=c++20 %t/test.cc -fprebuilt-module-path=%t -fsyntax-only -verify | ||
// | ||
// Testing that we can ignore the ODR violation from the driver invocation. | ||
// RUN: %clang -std=c++20 %t/a.cppm --precompile -o %t/a.pcm | ||
// RUN: %clang -std=c++20 %t/b.cppm --precompile -o %t/b.pcm | ||
// RUN: %clang -std=c++20 %t/test.cc -fprebuilt-module-path=%t -fsyntax-only -Xclang -verify \ | ||
// RUN: -DIGNORE_ODR_VIOLATION | ||
// | ||
// Testing that the driver can require to check the ODR violation. | ||
// RUN: %clang -std=c++20 -Xclang -fno-skip-odr-check-in-gmf %t/a.cppm --precompile -o %t/a.pcm | ||
// RUN: %clang -std=c++20 -Xclang -fno-skip-odr-check-in-gmf %t/b.cppm --precompile -o %t/b.pcm | ||
// RUN: %clang -std=c++20 -Xclang -fno-skip-odr-check-in-gmf %t/test.cc -fprebuilt-module-path=%t \ | ||
// RUN: -fsyntax-only -Xclang -verify | ||
|
||
//--- func1.h | ||
bool func(int x, int y) { | ||
return true; | ||
} | ||
|
||
//--- func2.h | ||
bool func(int x, int y) { | ||
return false; | ||
} | ||
|
||
//--- a.cppm | ||
module; | ||
#include "func1.h" | ||
export module a; | ||
export using ::func; | ||
|
||
//--- b.cppm | ||
module; | ||
#include "func2.h" | ||
export module b; | ||
export using ::func; | ||
|
||
//--- test.cc | ||
import a; | ||
import b; | ||
bool test() { | ||
return func(1, 2); | ||
} | ||
|
||
#ifdef IGNORE_ODR_VIOLATION | ||
// expected-no-diagnostics | ||
#else | ||
// [email protected]:1 {{'func' has different definitions in different modules;}} | ||
// [email protected]:1 {{but in 'a.<global>' found a different body}} | ||
#endif |
Uh oh!
There was an error while loading. Please reload this page.