Skip to content
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] Can't use same legacy header in different modules #60027

Closed
SasisaDev opened this issue Jan 14, 2023 · 20 comments
Closed

[C++20][Modules] Can't use same legacy header in different modules #60027

SasisaDev opened this issue Jan 14, 2023 · 20 comments
Labels
clang:modules C++20 modules and Clang Header Modules

Comments

@SasisaDev
Copy link

SasisaDev commented Jan 14, 2023

module;

#include <concepts>

export module Services:Service;
module;

#include <iostream>

export module Log;

Results in this error

D:\VisualStudio\VC\Tools\MSVC\14.34.31933\include\vcruntime_new.h:27:16: error: 'std::align_val_t' has different definitions in different modules; definition in module 'Log.<global>' first difference is enum with specified type 'size_t' (aka 'unsigned long long')
    enum class align_val_t : size_t {};
    ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~
D:\VisualStudio\VC\Tools\MSVC\14.34.31933\include\vcruntime_new.h:27:16: note: but in 'Services:Service.<global>' found enum with specified type 'size_t' (aka 'unsigned long long')
    enum class align_val_t : size_t {};
    ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~
1 error generated.

Both headers at one point include vcruntime_new.h header.
No using namespace std; used.
Obviously, definitions are not different, they're just not removed by clang when building tree.
I used these commands to build

[PCM] clang++ -std=c++20 -fmodules  -g -O0  -Wall  -DPLATFORM_WINDOWS 
-fprebuilt-module-path=D:\UnixProjects\General\Bismuth\BismuthEngine\Intermediate\Modules  
D:\UnixProjects\General\Bismuth\BismuthEngine\Intermediate\Modules\Engine.lib 
D:\UnixProjects\General\Bismuth\BismuthEngine\Intermediate\Modules\Services.lib 
D:\UnixProjects\General\Bismuth\BismuthEngine\Intermediate\Modules\Engine.lib 
D:\UnixProjects\General\Bismuth\BismuthEngine\Intermediate\Modules\Services.lib 
--precompile D:\UnixProjects\General\Bismuth\BismuthEngine\Source\Engine\Application\Application.cppm
-o D:\UnixProjects\General\Bismuth\BismuthEngine\Intermediate\Modules\Application.pcm 

[LIB] clang++ -std=c++20 -fmodules  -g -O0  -Wall  -DPLATFORM_WINDOWS 
-fprebuilt-module-path=D:\UnixProjects\General\Bismuth\BismuthEngine\Intermediate\Modules  
D:\UnixProjects\General\Bismuth\BismuthEngine\Intermediate\Modules\Engine.lib 
D:\UnixProjects\General\Bismuth\BismuthEngine\Intermediate\Modules\Services.lib 
D:\UnixProjects\General\Bismuth\BismuthEngine\Intermediate\Modules\Engine.lib 
D:\UnixProjects\General\Bismuth\BismuthEngine\Intermediate\Modules\Services.lib 
-c  D:\UnixProjects\General\Bismuth\BismuthEngine\Source\Engine\Application\Application.cppm 
-o D:\UnixProjects\General\Bismuth\BismuthEngine\Intermediate\Modules\Application_temp\Application_interface.obj

[LINKER] lld-link  /lib D:\UnixProjects\General\Bismuth\BismuthEngine\Intermediate\Modules\Engine.lib 
D:\UnixProjects\General\Bismuth\BismuthEngine\Intermediate\Modules\Services.lib     
/out:D:\UnixProjects\General\Bismuth\BismuthEngine\Intermediate\Modules\Application.lib
@EugeneZelenko EugeneZelenko added clang:modules C++20 modules and Clang Header Modules and removed new issue labels Jan 14, 2023
@llvmbot
Copy link
Member

llvmbot commented Jan 14, 2023

@llvm/issue-subscribers-clang-modules

@ChuanqiXu9
Copy link
Member

hi, the reproducer looks odd to me since that we don't likely meet the problems if we don't import Log to Services:Service or import them to the same TU. Could you try to provide the full reproducer and the corresponding comamnds line.

@davidstone
Copy link
Contributor

I have reproduced this issue with libstdc++, so I suspect it's the same problem here. I'll describe my libstdc++ issue:

bits/align.h includes stdint.h, which provides the type ::uintptr_t. If cstdint is included before that, then there is also the type ::std::uintptr_t. The function is defined in namespace std, but references uintptr_t (no qualifications or scope resolution). This means that some translation units refer to ::std::uintptr_t and some refer to ::uintptr_t. Even though ::std::uintptr_t is defined as namespace std { using ::uintptr_t; }, this is treated as a violation of the ODR. I don't know if that is an actual violation. Here is the fully reduced test case from libstdc++:

module;

using uintptr_t = unsigned long;

namespace std {

using ::uintptr_t;

void align() {
  uintptr_t x;
}

} // namespace std

export module a;
module;

using uintptr_t = unsigned long;

namespace std {

void align() {
  uintptr_t x;
}

} // namespace std

export module b;
export module c;

import a;
import b;

Compile with

clang++ -std=c++20 -x c++-module a.cpp --precompile -o a.pcm
clang++ -std=c++20 -x c++-module b.cpp --precompile -o b.pcm
clang++ -std=c++20 -x c++-module c.cpp --precompile -o c.pcm -fmodule-file=a.pcm -fmodule-file=b.pcm

Outputs

In file included from c.cpp:1:
b.cpp:7:6: error: 'std::align' has different definitions in different modules; definition in module 'b.<global>' first difference is function body
void align() {
~~~~~^~~~~~~~~
a.cpp:9:6: note: but in 'a.<global>' found a different body
void align() {
~~~~~^~~~~~~~~
1 error generated.

I'm assuming Visual Studio's headers have a similar problem, so to workaround you can include the cwhatever header first, if you can figure out which symbol is being referenced. Then you'll always get the ::std:: version.

@ChuanqiXu9
Copy link
Member

ChuanqiXu9 commented Feb 2, 2023

Thanks for the minimal reproducer! It helps a lot. I think this is not a real ODR violation. Since http://eel.is/c++draft/basic.def.odr#14.5 said the names inside the functions should refer to the same entity after the lookup. And ::std::uintptr_t and ::uintptr_t should have the same type. So they should be the same entity. So this is not a ODR violation and this should be a compiler bug.

@masx200
Copy link

masx200 commented May 17, 2023

[ 37%]: compiling.module.test leetcode_treenode_cpp.freeTreeNode
"C:\\Program Files\\LLVM\\bin\\clang" -c -x c++-module -fmodule-output=build\.gens\leetcode-treenode-cpp\windows\x64\test\rules\modules\cache\ec4a91a6\leetcode_treenode_cpp.freeTreeNode.pcm -fmodule-file=leetcode_treenode_cpp.TreeNode=build\.gens\leetcode-treenode-cpp\windows\x64\test\rules\modules\cache\ec4a91a6\leetcode_treenode_cpp.TreeNode.pcm -Qunused-arguments -m64 -std=c++20 -D__TEST__ -fexceptions -fcxx-exceptions -isystem C:\Users\Administrator\Documents\vcpkg-master\installed\x64-windows-static\include -fmodules-cache-path=build\.gens\leetcode-treenode-cpp\windows\x64\test\rules\modules\cache -fmodule-file=leetcode_treenode_cpp.TreeNode=build\.gens\leetcode-treenode-cpp\windows\x64\test\rules\modules\cache\ec4a91a6\leetcode_treenode_cpp.TreeNode.pcm -o build\.objs\leetcode-treenode-cpp\windows\x64\test\freeTreeNode.ixx.obj freeTreeNode.ixx
error: @programdir\modules\private\async\runjobs.lua:256: @programdir\rules\c++\modules\modules_support\clang.lua:270: @programdir\modules\core\tools\gcc.lua:721: In file included from traversalTreeNode.ixx:2:
In file included from C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\vector:11:
In file included from C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\xmemory:14:
In file included from C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\new:11:
In file included from C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\exception:12:
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\type_traits:2186:19: error: 'std::hash' has different definitions in different modules; first difference is defined here found method '_Do_hash' with no body
    static size_t _Do_hash(const _Kty& _Keyval) noexcept {
    ~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\type_traits:2186:19: note: but in 'leetcode_treenode_cpp.TreeNode.<global>' found method '_Do_hash' with body
    static size_t _Do_hash(const _Kty& _Keyval) noexcept {
    ~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from traversalTreeNode.ixx:2:
In file included from C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\vector:11:
In file included from C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\xmemory:14:
In file included from C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\new:11:
In file included from C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\exception:12:
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\type_traits:2167:23: error: 'std::_Conditionally_enabled_hash' has different definitions in different modules; first difference is defined here found method 'operator()' with no body
    _NODISCARD size_t operator()(const _Kty& _Keyval) const
               ~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\type_traits:2167:23: note: but in 'leetcode_treenode_cpp.TreeNode.<global>' found method 'operator()' with body
    _NODISCARD size_t operator()(const _Kty& _Keyval) const
               ~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2 errors generated.

@ChuanqiXu9
Copy link
Member

[ 37%]: compiling.module.test leetcode_treenode_cpp.freeTreeNode
"C:\Program Files\LLVM\bin\clang" -c -x c++-module -fmodule-output=build.gens\leetcode-treenode-cpp\windows\x64\test\rules\modules\cache\ec4a91a6\leetcode_treenode_cpp.freeTreeNode.pcm -fmodule-file=leetcode_treenode_cpp.TreeNode=build.gens\leetcode-treenode-cpp\windows\x64\test\rules\modules\cache\ec4a91a6\leetcode_treenode_cpp.TreeNode.pcm -Qunused-arguments -m64 -std=c++20 -D__TEST__ -fexceptions -fcxx-exceptions -isystem C:\Users\Administrator\Documents\vcpkg-master\installed\x64-windows-static\include -fmodules-cache-path=build.gens\leetcode-treenode-cpp\windows\x64\test\rules\modules\cache -fmodule-file=leetcode_treenode_cpp.TreeNode=build.gens\leetcode-treenode-cpp\windows\x64\test\rules\modules\cache\ec4a91a6\leetcode_treenode_cpp.TreeNode.pcm -o build.objs\leetcode-treenode-cpp\windows\x64\test\freeTreeNode.ixx.obj freeTreeNode.ixx
error: @programdir\modules\private\async\runjobs.lua:256: @programdir\rules\c++\modules\modules_support\clang.lua:270: @programdir\modules\core\tools\gcc.lua:721: In file included from traversalTreeNode.ixx:2:
In file included from C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\vector:11:
In file included from C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\xmemory:14:
In file included from C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\new:11:
In file included from C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\exception:12:
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\type_traits:2186:19: error: 'std::hash' has different definitions in different modules; first difference is defined here found method '_Do_hash' with no body
static size_t _Do_hash(const _Kty& _Keyval) noexcept {
~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\type_traits:2186:19: note: but in 'leetcode_treenode_cpp.TreeNode.' found method '_Do_hash' with body
static size_t _Do_hash(const _Kty& _Keyval) noexcept {
~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from traversalTreeNode.ixx:2:
In file included from C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\vector:11:
In file included from C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\xmemory:14:
In file included from C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\new:11:
In file included from C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\exception:12:
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\type_traits:2167:23: error: 'std::_Conditionally_enabled_hash' has different definitions in different modules; first difference is defined here found method 'operator()' with no body
_NODISCARD size_t operator()(const _Kty& _Keyval) const
~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\include\type_traits:2167:23: note: but in 'leetcode_treenode_cpp.TreeNode.' found method 'operator()' with body
_NODISCARD size_t operator()(const _Kty& _Keyval) const
~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2 errors generated.

This should fall in the category of #61807.

@csimon-bambecksystems
Copy link

@masx200 @ChuanqiXu9 I have one more reproducer for you... unfortunately it's the modules "Hello World" example direct from LLVM's documentation (https://clang.llvm.org/docs/StandardCPlusPlusModules.html).

// M.cppm
export module M;
export import :interface_part;
import :impl_part;
export void Hello();

// interface_part.cppm
export module M:interface_part;
export void World();

// impl_part.cppm
module;
#include <iostream>
#include <string>
module M:impl_part;
import :interface_part;

std::string W = "World.";
void World() {
  std::cout << W << std::endl;
}

// Impl.cpp
module;
#include <iostream>
module M;
void Hello() {
  std::cout << "Hello ";
}

// User.cpp
import M;
int main() {
  Hello();
  World();
  return 0;
}

Now I build the example on my Windows PC with the following commands:

clang++ -std=c++20 interface_part.cppm --precompile -o M-interface_part.pcm
clang++ -std=c++20 impl_part.cppm --precompile -fprebuilt-module-path='.' -o M-impl_part.pcm
clang++ -std=c++20 M.cppm --precompile -fprebuilt-module-path='.' -o M.pcm
clang++ -std=c++20 Impl.cpp -fprebuilt-module-path='.' -fmodule-file=M='M.pcm' -c -o Impl.obj # Fails

The compilation of Impl.cpp fails with this error:

C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.37.32822\include\xstring:2993:10: error:
      'std::basic_string' has different definitions in different modules; first difference is defined here found method
      '_Memcpy_val_from' with no body
 2993 |     void _Memcpy_val_from(const basic_string& _Right) noexcept {
      |     ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.37.32822\include\xstring:2993:10: note:
      but in 'M:impl_part.<global>' found method '_Memcpy_val_from' with body
 2993 |     void _Memcpy_val_from(const basic_string& _Right) noexcept {
      |     ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 2994 |         _STL_INTERNAL_CHECK(_Can_memcpy_val);
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 2995 |         const auto _My_data_mem =
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~
 2996 |             reinterpret_cast<unsigned char*>(_STD addressof(_Mypair._Myval2)) + _Memcpy_val_offset;
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 2997 |         const auto _Right_data_mem =
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 2998 |             reinterpret_cast<const unsigned char*>(_STD addressof(_Right._Mypair._Myval2)) + _Memcpy_val_offset;
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 2999 |         _CSTD memcpy(_My_data_mem, _Right_data_mem, _Memcpy_val_size);
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 3000 |     }
      |     ~

My clang is v17.0.1.

@ChuanqiXu9
Copy link
Member

The root problems of such issues are the compiler failed to recognize the sameness of declarations in different translation unit. And this is not a single problem but a catagory of problems. And we probably can only fix them one by one unless we'd like to invent a new BMI format. That said, your issue may still exist after the reproducer of the issue itself got fixed.

@csimon-bambecksystems So I believe your report worths a standalone issue. Also since I don't have a windows developing environment, it would be much more helpful if you can reduce it so that it doesn't depends on MSSTL. We can achieve this by preprocess the source files and delete things from it. It is pretty touch and time consuming though..

@dwblaikie
Copy link
Collaborator

The root problems of such issues are the compiler failed to recognize the sameness of declarations in different translation unit. And this is not a single problem but a catagory of problems. And we probably can only fix them one by one unless we'd like to invent a new BMI format. That said, your issue may still exist after the reproducer of the issue itself got fixed.

I'm pretty confused by this. For Clang Header Modules we already have a fairly robust merging solution.

So some of that infrastructure isn't working where it should be working in C++20 modules?

@csimon-bambecksystems
Copy link

csimon-bambecksystems commented Oct 10, 2023

So I believe your report worths a standalone issue.

Consider reopening #61068?

We can achieve this by preprocess the source files and delete things from it.

So in order to get that done, I'd propose this plan of action:

  1. Make a decision which issue this is to be tracked under (the present one, or my C++ modules example fails on LLVM 15.0.7 for 64-bit Windows - delayed template parsing is not supported/broken with C++ modules #61068 which was recently closed as a duplicate of the present one, or a new one). Then,
  2. For me to go to the decided-upon issue and upload the preprocessor output, would seem a logical next step for the benefit of those who do not have access to Visual Studio, as well as a starting point for reducing to the minimal example. At the same time, however,
  3. I believe we ought also to go and ping somebody who does use/understand the Microsoft development environment. Their knowledge would be useful before we just dive headfirst into Microsoft standard headers in search of mysterious issues, am I right? Unfortunately I do not know how, in the LLVM project, we go about finding people with specific expertise, except by leaving attention-seeking comments on issues like this.

@csimon-bambecksystems
Copy link

Just one more comment...

The root problems of such issues are the compiler failed to recognize the sameness of declarations in different translation unit.

No, I don't think it is, in my case. Look again at what the diagnostic says. On one pass, clang sees a function "with no body". This is incorrect, and you can see it in the diagnostic itself: there is an opening brace { right there, that is to say, the function does have a body. So, just on the surface, it seems as if clang is not correct to stop before the { and find a function "with no body." The root question is, why does clang think this function (this time) does not have a body? Because on another pass, clang reads the very same code over again, same file/same lines, and - this is the crucial part - this time it finds a function body, like it is supposed to - even spits out the entire body in the diagnostic. Then, clang compares the two passes and correctly decides that a function with no body is not identical to a function with a body.

So there is the root. What happened (that one time, but not the other time) to the function body?

Granted there are some mysterious interactions here with standard C++ modules, and with Microsoft. But my gut says the true problems isn't with those. What do you all think?

@ChuanqiXu9
Copy link
Member

The root problems of such issues are the compiler failed to recognize the sameness of declarations in different translation unit. And this is not a single problem but a catagory of problems. And we probably can only fix them one by one unless we'd like to invent a new BMI format. That said, your issue may still exist after the reproducer of the issue itself got fixed.

I'm pretty confused by this. For Clang Header Modules we already have a fairly robust merging solution.

So some of that infrastructure isn't working where it should be working in C++20 modules?

I am not super sure but I fixed multiple similar issues for named modules. One problem may be that the named modules don't emit macros. So with header modules, when we see duplicated header includes and we're allowed to skip them (by pragma once and guard-if). But with named modules, we have to handle them explicitly. So we would meet different corner cases...

@ChuanqiXu9
Copy link
Member

So I believe your report worths a standalone issue.

Consider reopening #61068?

Done.

We can achieve this by preprocess the source files and delete things from it.

So in order to get that done, I'd propose this plan of action:

  1. Make a decision which issue this is to be tracked under (the present one, or my C++ modules example fails on LLVM 15.0.7 for 64-bit Windows #61068 which was recently closed as a duplicate of the present one, or a new one). Then,

Let's track the issue in that page. I believe these are 2 different issues.

  1. For me to go to the decided-upon issue and upload the preprocessor output, would seem a logical next step for the benefit of those who do not have access to Visual Studio, as well as a starting point for reducing to the minimal example. At the same time, however,
  2. I believe we ought also to go and ping somebody who does use/understand the Microsoft development environment. Their knowledge would be useful before we just dive headfirst into Microsoft standard headers in search of mysterious issues, am I right? Unfortunately I do not know how, in the LLVM project, we go about finding people with specific expertise, except by leaving attention-seeking comments on issues like this.

Sadly we don't know such people now.

Just one more comment...

The root problems of such issues are the compiler failed to recognize the sameness of declarations in different translation unit.

No, I don't think it is, in my case. Look again at what the diagnostic says. On one pass, clang sees a function "with no body". This is incorrect, and you can see it in the diagnostic itself: there is an opening brace { right there, that is to say, the function does have a body. So, just on the surface, it seems as if clang is not correct to stop before the { and find a function "with no body." The root question is, why does clang think this function (this time) does not have a body? Because on another pass, clang reads the very same code over again, same file/same lines, and - this is the crucial part - this time it finds a function body, like it is supposed to - even spits out the entire body in the diagnostic. Then, clang compares the two passes and correctly decides that a function with no body is not identical to a function with a body.

So there is the root. What happened (that one time, but not the other time) to the function body?

Granted there are some mysterious interactions here with standard C++ modules, and with Microsoft. But my gut says the true problems isn't with those. What do you all think?

Yeah, I admit it is pretty confusing and disappointing. One problem with the analysis (to me) is that it is based on a parser's perspective. But the problem may not live in the parser's side but in the serializer's/ODR checker's side. And from my experience, the answer may simply be that we didn't serialize/check ODR properly for some wried grammar.

@dwblaikie
Copy link
Collaborator

The root problems of such issues are the compiler failed to recognize the sameness of declarations in different translation unit. And this is not a single problem but a catagory of problems. And we probably can only fix them one by one unless we'd like to invent a new BMI format. That said, your issue may still exist after the reproducer of the issue itself got fixed.

I'm pretty confused by this. For Clang Header Modules we already have a fairly robust merging solution.
So some of that infrastructure isn't working where it should be working in C++20 modules?

I am not super sure but I fixed multiple similar issues for named modules. One problem may be that the named modules don't emit macros. So with header modules, when we see duplicated header includes and we're allowed to skip them (by pragma once and guard-if). But with named modules, we have to handle them explicitly. So we would meet different corner cases...

Except we can still end up in a case where two modules bring in the same content (because they didn't see each others macros or pragmas) and we have to merge them. Take two modules that both textually include a header, then both those modules (clang header modules) are included/imported into a translation unit.

All possible merging is required there, so far as I know. So any time we see a failure to merge/deduplicate in C++20 modules - we should check if it that merging situation already arises (& is handled correctly) in Clang Header Modules and how it's accounted for there. We may be missing some generalization/implementation sharing from there. We don't want to go tracking down/re-fixing all those issues in C++20 modules if we can help it...

Do you have a link to one such previous example, perhaps? Then we could compare/contrast the problem and solution to the Clang Header Modules situation and see if there's a generalization we could benefit from.

@ChuanqiXu9
Copy link
Member

The root problems of such issues are the compiler failed to recognize the sameness of declarations in different translation unit. And this is not a single problem but a catagory of problems. And we probably can only fix them one by one unless we'd like to invent a new BMI format. That said, your issue may still exist after the reproducer of the issue itself got fixed.

I'm pretty confused by this. For Clang Header Modules we already have a fairly robust merging solution.
So some of that infrastructure isn't working where it should be working in C++20 modules?

I am not super sure but I fixed multiple similar issues for named modules. One problem may be that the named modules don't emit macros. So with header modules, when we see duplicated header includes and we're allowed to skip them (by pragma once and guard-if). But with named modules, we have to handle them explicitly. So we would meet different corner cases...

Except we can still end up in a case where two modules bring in the same content (because they didn't see each others macros or pragmas) and we have to merge them. Take two modules that both textually include a header, then both those modules (clang header modules) are included/imported into a translation unit.

All possible merging is required there, so far as I know. So any time we see a failure to merge/deduplicate in C++20 modules - we should check if it that merging situation already arises (& is handled correctly) in Clang Header Modules and how it's accounted for there. We may be missing some generalization/implementation sharing from there. We don't want to go tracking down/re-fixing all those issues in C++20 modules if we can help it...

Do you have a link to one such previous example, perhaps? Then we could compare/contrast the problem and solution to the Clang Header Modules situation and see if there's a generalization we could benefit from.

Yeah, generally when we fix such issues reported in C++20 modules, we don't add a test in clang header modules. And vice versa. Luckily, the exceptional examples in my memory exists:

Then now I feel it may be true that So any time we see a failure to merge/deduplicate in C++20 modules - we should check if it that merging situation already arises (& is handled correctly) in Clang Header Modules. And the point may be that Clang Header Modules doesn't have a pretty pretty pretty strong robust merging solution. The root problem may be C++ is too complex.

And from the implementer's perspective to the above issues, I can't find a common point in the above fixes. This is the reason why I think such issue is not a single issue but a catagory of issues. And we can only fix them case by case.

@koplas
Copy link

koplas commented Oct 13, 2023

I have reproduced this issue with libstdc++, so I suspect it's the same problem here. I'll describe my libstdc++ issue:

bits/align.h includes stdint.h, which provides the type ::uintptr_t. If cstdint is included before that, then there is also the type ::std::uintptr_t. The function is defined in namespace std, but references uintptr_t (no qualifications or scope resolution). This means that some translation units refer to ::std::uintptr_t and some refer to ::uintptr_t. Even though ::std::uintptr_t is defined as namespace std { using ::uintptr_t; }, this is treated as a violation of the ODR. I don't know if that is an actual violation. Here is the fully reduced test case from libstdc++:

module;

using uintptr_t = unsigned long;

namespace std {

using ::uintptr_t;

void align() {
  uintptr_t x;
}

} // namespace std

export module a;
module;

using uintptr_t = unsigned long;

namespace std {

void align() {
  uintptr_t x;
}

} // namespace std

export module b;
export module c;

import a;
import b;

Compile with

clang++ -std=c++20 -x c++-module a.cpp --precompile -o a.pcm
clang++ -std=c++20 -x c++-module b.cpp --precompile -o b.pcm
clang++ -std=c++20 -x c++-module c.cpp --precompile -o c.pcm -fmodule-file=a.pcm -fmodule-file=b.pcm

Outputs

In file included from c.cpp:1:
b.cpp:7:6: error: 'std::align' has different definitions in different modules; definition in module 'b.<global>' first difference is function body
void align() {
~~~~~^~~~~~~~~
a.cpp:9:6: note: but in 'a.<global>' found a different body
void align() {
~~~~~^~~~~~~~~
1 error generated.

I'm assuming Visual Studio's headers have a similar problem, so to workaround you can include the cwhatever header first, if you can figure out which symbol is being referenced. Then you'll always get the ::std:: version.

Is this already fixed? This reproducer works for me with clang 18 (b3d4549).

EDIT: This fails with clang 16.0.6. @SasisaDev could you test if this issue persists in HEAD?

@dwblaikie
Copy link
Collaborator

The root problems of such issues are the compiler failed to recognize the sameness of declarations in different translation unit. And this is not a single problem but a catagory of problems. And we probably can only fix them one by one unless we'd like to invent a new BMI format. That said, your issue may still exist after the reproducer of the issue itself got fixed.

I'm pretty confused by this. For Clang Header Modules we already have a fairly robust merging solution.
So some of that infrastructure isn't working where it should be working in C++20 modules?

I am not super sure but I fixed multiple similar issues for named modules. One problem may be that the named modules don't emit macros. So with header modules, when we see duplicated header includes and we're allowed to skip them (by pragma once and guard-if). But with named modules, we have to handle them explicitly. So we would meet different corner cases...

Except we can still end up in a case where two modules bring in the same content (because they didn't see each others macros or pragmas) and we have to merge them. Take two modules that both textually include a header, then both those modules (clang header modules) are included/imported into a translation unit.
All possible merging is required there, so far as I know. So any time we see a failure to merge/deduplicate in C++20 modules - we should check if it that merging situation already arises (& is handled correctly) in Clang Header Modules and how it's accounted for there. We may be missing some generalization/implementation sharing from there. We don't want to go tracking down/re-fixing all those issues in C++20 modules if we can help it...
Do you have a link to one such previous example, perhaps? Then we could compare/contrast the problem and solution to the Clang Header Modules situation and see if there's a generalization we could benefit from.

Yeah, generally when we fix such issues reported in C++20 modules, we don't add a test in clang header modules. And vice versa. Luckily, the exceptional examples in my memory exists:

Then now I feel it may be true that So any time we see a failure to merge/deduplicate in C++20 modules - we should check if it that merging situation already arises (& is handled correctly) in Clang Header Modules. And the point may be that Clang Header Modules doesn't have a pretty pretty pretty strong robust merging solution. The root problem may be C++ is too complex.

And from the implementer's perspective to the above issues, I can't find a common point in the above fixes. This is the reason why I think such issue is not a single issue but a catagory of issues. And we can only fix them case by case.

ah, fair enough. Yeah, @zygoloid did do a lot of fixing of ODR stuff when we were rolling out modules at Google - and the things you've noted in the above are features we don't/haven't used at Google yet (mostly concepts) so haven't been robustly tested in clang header modules deployments, most likely.

So, yes, for "newer" feature areas, I agree keeping bugs separate/expecting there to be many distinct fixes required seems fair. (the one about a basic function complaining it has a different body, etc - that seems like something I'd have thought would be covered by existing clang header modules support, so is a bit surprising to me)

@ChuanqiXu9
Copy link
Member

I have reproduced this issue with libstdc++, so I suspect it's the same problem here. I'll describe my libstdc++ issue:
bits/align.h includes stdint.h, which provides the type ::uintptr_t. If cstdint is included before that, then there is also the type ::std::uintptr_t. The function is defined in namespace std, but references uintptr_t (no qualifications or scope resolution). This means that some translation units refer to ::std::uintptr_t and some refer to ::uintptr_t. Even though ::std::uintptr_t is defined as namespace std { using ::uintptr_t; }, this is treated as a violation of the ODR. I don't know if that is an actual violation. Here is the fully reduced test case from libstdc++:

module;

using uintptr_t = unsigned long;

namespace std {

using ::uintptr_t;

void align() {
  uintptr_t x;
}

} // namespace std

export module a;
module;

using uintptr_t = unsigned long;

namespace std {

void align() {
  uintptr_t x;
}

} // namespace std

export module b;
export module c;

import a;
import b;

Compile with

clang++ -std=c++20 -x c++-module a.cpp --precompile -o a.pcm
clang++ -std=c++20 -x c++-module b.cpp --precompile -o b.pcm
clang++ -std=c++20 -x c++-module c.cpp --precompile -o c.pcm -fmodule-file=a.pcm -fmodule-file=b.pcm

Outputs

In file included from c.cpp:1:
b.cpp:7:6: error: 'std::align' has different definitions in different modules; definition in module 'b.<global>' first difference is function body
void align() {
~~~~~^~~~~~~~~
a.cpp:9:6: note: but in 'a.<global>' found a different body
void align() {
~~~~~^~~~~~~~~
1 error generated.

I'm assuming Visual Studio's headers have a similar problem, so to workaround you can include the cwhatever header first, if you can figure out which symbol is being referenced. Then you'll always get the ::std:: version.

Is this already fixed? This reproducer works for me with clang 18 (b3d4549).

EDIT: This fails with clang 16.0.6. @SasisaDev could you test if this issue persists in HEAD?

Are you using Windows or LInux? If you're using Windows, I think we can close this one now.

@koplas
Copy link

koplas commented Oct 16, 2023

Are you using Windows or LInux? If you're using Windows, I think we can close this one now.

I can confirm that the reproducer from @davidstone compiles without issues on Windows with clang version 17.0.0 (https://github.com/llvm/llvm-project.git 0d3eee3)

@ChuanqiXu9
Copy link
Member

Are you using Windows or LInux? If you're using Windows, I think we can close this one now.

I can confirm that the reproducer from @davidstone compiles without issues on Windows with clang version 17.0.0 (https://github.com/llvm/llvm-project.git 0d3eee3)

Got it. In this case, I think we can close the issue and we can reopen this if @SasisaDev find this doesn't solve the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:modules C++20 modules and Clang Header Modules
Projects
None yet
Development

No branches or pull requests

9 participants