[clang] [clang][Modules] Support ODR merging of `UsingEnumDecl` (PR #207071)
Ivo Popov via cfe-commits
cfe-commits at lists.llvm.org
Wed Jul 1 12:52:36 PDT 2026
https://github.com/ipopov created https://github.com/llvm/llvm-project/pull/207071
Fixes https://github.com/llvm/llvm-project/issues/207066.
When compiling with C++20 modules, Clang performs ODR merging of class definitions across different modules. During this process, `ASTContext::isSameEntity` is used to check if member declarations match between the definitions.
However, `UsingEnumDecl` (representing C++20 `using enum` declarations) was not handled in `ASTContext::isSameEntity`, causing it to default to returning `false`. Consequently, identical class definitions containing a `using enum` statement failed to merge, resulting in spurious ODR mismatch errors such as: `error: 'MyStruct::MyEnum' from module 'ModuleB' is not present in definition of 'MyStruct' in module 'ModuleA'`.
This patch implements merging support for `UsingEnumDecl` in `ASTContext::isSameEntity` by comparing the nested-name-specifier qualifiers and the underlying `EnumDecl` target.
MARKDOWN=true
>From cb84c53bdd2696f538945f3fed914a1adb029f32 Mon Sep 17 00:00:00 2001
From: ipopov <ipopov at google.com>
Date: Wed, 1 Jul 2026 12:48:10 -0700
Subject: [PATCH] [clang][Modules] Support ODR merging of `UsingEnumDecl`
When compiling with C++20 modules, Clang performs ODR merging of class
definitions across different modules. During this process,
`ASTContext::isSameEntity` is used to check if member declarations match
between the definitions.
However, `UsingEnumDecl` (representing C++20 `using enum` declarations)
was not handled in `ASTContext::isSameEntity`, causing it to default to
returning `false`. Consequently, identical class definitions containing
a `using enum` statement failed to merge, resulting in spurious ODR
mismatch errors such as: `error: 'MyStruct::MyEnum' from module
'ModuleB' is not present in definition of 'MyStruct' in module
'ModuleA'`.
This patch implements merging support for `UsingEnumDecl` in
`ASTContext::isSameEntity` by comparing the nested-name-specifier
qualifiers and the underlying `EnumDecl` target.
Fixes https://github.com/llvm/llvm-project/issues/207066.
MARKDOWN=true
---
clang/lib/AST/ASTContext.cpp | 6 +++
.../modules-using-enum-class-scope.cppm | 53 +++++++++++++++++++
2 files changed, 59 insertions(+)
create mode 100644 clang/test/Modules/modules-using-enum-class-scope.cppm
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index abf0cd5e18c2b..ccdbb82ccc59f 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -7948,6 +7948,12 @@ bool ASTContext::isSameEntity(const NamedDecl *X, const NamedDecl *Y) const {
return NAX->getNamespace()->Equals(NAY->getNamespace());
}
+ if (const auto *UX = dyn_cast<UsingEnumDecl>(X)) {
+ const auto *UY = cast<UsingEnumDecl>(Y);
+ return isSameQualifier(UX->getQualifier(), UY->getQualifier()) &&
+ declaresSameEntity(UX->getEnumDecl(), UY->getEnumDecl());
+ }
+
return false;
}
diff --git a/clang/test/Modules/modules-using-enum-class-scope.cppm b/clang/test/Modules/modules-using-enum-class-scope.cppm
new file mode 100644
index 0000000000000..ca2463b2e74ae
--- /dev/null
+++ b/clang/test/Modules/modules-using-enum-class-scope.cppm
@@ -0,0 +1,53 @@
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+// RUN: cd %t
+//
+// RUN: %clang_cc1 -std=c++20 -fmodules -fimplicit-module-maps -x c++ -I%t \
+// RUN: -emit-module -fmodule-name=ModuleA -o %t/ModuleA.pcm \
+// RUN: %t/module.modulemap
+//
+// RUN: %clang_cc1 -std=c++20 -fmodules -fimplicit-module-maps -x c++ -I%t \
+// RUN: -emit-module -fmodule-name=ModuleB -o %t/ModuleB.pcm \
+// RUN: %t/module.modulemap
+//
+// RUN: %clang_cc1 -std=c++20 -fmodules -fimplicit-module-maps -x c++ -I%t \
+// RUN: -fmodule-file=%t/ModuleA.pcm \
+// RUN: -fmodule-file=%t/ModuleB.pcm \
+// RUN: -verify %t/main.cpp
+
+//--- common.h
+#pragma once
+struct MyStruct {
+ enum class MyEnum { A };
+ using enum MyEnum;
+};
+
+//--- a.h
+#pragma once
+#include "common.h"
+
+//--- b.h
+#pragma once
+#include "common.h"
+
+//--- module.modulemap
+module ModuleA {
+ header "a.h"
+ export *
+}
+
+module ModuleB {
+ header "b.h"
+ export *
+}
+
+//--- main.cpp
+#include "a.h"
+#include "b.h"
+
+inline void use() {
+ auto x = MyStruct::A;
+}
+
+// expected-no-diagnostics
More information about the cfe-commits
mailing list