diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp index aea1a93ae1fa8..07677d655c5af 100644 --- a/clang/lib/AST/ODRHash.cpp +++ b/clang/lib/AST/ODRHash.cpp @@ -741,8 +741,55 @@ void ODRHash::AddEnumDecl(const EnumDecl *Enum) { if (Enum->isScoped()) AddBoolean(Enum->isScopedUsingClassTag()); - if (Enum->getIntegerTypeSourceInfo()) - AddQualType(Enum->getIntegerType()); + if (Enum->getIntegerTypeSourceInfo()) { + // FIMXE: This allows two enums with different spellings to have the same + // hash. + // + // // mod1.cppm + // module; + // extern "C" { + // typedef unsigned __int64 size_t; + // } + // namespace std { + // using :: size_t; + // } + // + // extern "C++" { + // namespace std { + // enum class align_val_t : std::size_t {}; + // } + // } + // + // export module mod1; + // export using std::align_val_t; + // + // // mod2.cppm + // module; + // extern "C" { + // typedef unsigned __int64 size_t; + // } + // + // extern "C++" { + // namespace std { + // enum class align_val_t : size_t {}; + // } + // } + // + // export module mod2; + // import mod1; + // export using std::align_val_t; + // + // The above example should be disallowed since it violates + // [basic.def.odr]p14: + // + // Each such definition shall consist of the same sequence of tokens + // + // The definitions of `std::align_val_t` in two module units have different + // spellings but we failed to give an error here. + // + // See https://github.com/llvm/llvm-project/issues/76638 for details. + AddQualType(Enum->getIntegerType().getCanonicalType()); + } // Filter out sub-Decls which will not be processed in order to get an // accurate count of Decl's. diff --git a/clang/test/Modules/pr76638.cppm b/clang/test/Modules/pr76638.cppm new file mode 100644 index 0000000000000..8cc807961421b --- /dev/null +++ b/clang/test/Modules/pr76638.cppm @@ -0,0 +1,69 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 %t/mod1.cppm -emit-module-interface -o %t/mod1.pcm +// RUN: %clang_cc1 -std=c++20 %t/mod2.cppm -fmodule-file=mod1=%t/mod1.pcm \ +// RUN: -fsyntax-only -verify +// +// RUN: %clang_cc1 -std=c++20 %t/mod3.cppm -emit-module-interface -o %t/mod3.pcm +// RUN: %clang_cc1 -std=c++20 %t/mod4.cppm -fmodule-file=mod3=%t/mod3.pcm \ +// RUN: -fsyntax-only -verify + +//--- size_t.h + +extern "C" { + typedef unsigned int size_t; +} + +//--- csize_t +namespace std { + using :: size_t; +} + +//--- align.h +namespace std { + enum class align_val_t : size_t {}; +} + +//--- mod1.cppm +module; +#include "size_t.h" +#include "align.h" +export module mod1; +export using std::align_val_t; + +//--- mod2.cppm +// expected-no-diagnostics +module; +#include "size_t.h" +#include "csize_t" +#include "align.h" +export module mod2; +import mod1; +export using std::align_val_t; + +//--- signed_size_t.h +// Test that we can still find the case if the underlying type is different +extern "C" { + typedef signed int size_t; +} + +//--- mod3.cppm +module; +#include "size_t.h" +#include "align.h" +export module mod3; +export using std::align_val_t; + +//--- mod4.cppm +module; +#include "signed_size_t.h" +#include "csize_t" +#include "align.h" +export module mod4; +import mod3; +export using std::align_val_t; + +// expected-error@align.h:* {{'std::align_val_t' has different definitions in different modules; defined here first difference is enum with specified type 'size_t' (aka 'int')}} +// expected-note@align.h:* {{but in 'mod3.' found enum with specified type 'size_t' (aka 'unsigned int')}}