Author: Chuanqi Xu
Date: 2026-08-27T09:19:09Z
New Revision: 3ec748cea37b991d6f557310b1230d6a9011e064

URL: 
https://github.com/llvm/llvm-project/commit/3ec748cea37b991d6f557310b1230d6a9011e064
DIFF: 
https://github.com/llvm/llvm-project/commit/3ec748cea37b991d6f557310b1230d6a9011e064.diff

LOG: [C++20] [Modules] Preserve placement new and operator for ADL in Reduced 
BMI (#219145)

Close https://github.com/llvm/llvm-project/issues/218246

The root cause of the issue is, the placement new and operator for ADL
is not directly referenced and they are removed in reduced BMI. This is
not correct. This patch fixes it.

Added: 
    clang/test/Modules/reduced-bmi-dependent-lookup.cpp

Modified: 
    clang/lib/Serialization/ASTWriterStmt.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/Serialization/ASTWriterStmt.cpp 
b/clang/lib/Serialization/ASTWriterStmt.cpp
index 782fecdbd0c80..513c832c38ffd 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -1140,6 +1140,44 @@ void ASTStmtWriter::VisitBinaryOperator(BinaryOperator 
*E) {
       E->getObjectKind() == OK_Ordinary)
     AbbrevToUse = Writer.getBinaryOperatorAbbrev();
 
+  // When emitting reduced BMI, some necessary operators may be removed for 
ADL.
+  // Here we tries to save such operators.
+  if (Writer.isGeneratingReducedBMI() &&
+      // Assign doesn't take part in ADL.
+      E->getOpcode() != BO_Assign &&
+      (E->getLHS()->isTypeDependent() || E->getRHS()->isTypeDependent())) {
+    OverloadedOperatorKind Op =
+        BinaryOperator::getOverloadedOperator(E->getOpcode());
+
+    // [module.global.frag] performs a synthetic lookup in which each
+    // type-dependent operand has no associated namespaces or entities.
+    DeclarationName Name =
+        Record.getASTContext().DeclarationNames.getCXXOperatorName(Op);
+
+    auto PreserveAssociatedCandidates = [&](Expr *Operand) {
+      const auto *RT = Operand->getType()->getAs<RecordType>();
+      if (!RT)
+        return;
+
+      // Find the associated namespace and perform a synthetic lookup in it.
+      DeclContext *DC = RT->getDecl()->getDeclContext();
+      while (DC && !DC->isFileContext())
+        DC = DC->getParent();
+      if (auto *NS = dyn_cast_or_null<NamespaceDecl>(DC))
+        for (NamedDecl *D : NS->noload_lookup(Name))
+          Writer.GetDeclRef(D);
+    };
+
+    if (!E->getLHS()->isTypeDependent())
+      PreserveAssociatedCandidates(E->getLHS());
+    if (!E->getRHS()->isTypeDependent())
+      PreserveAssociatedCandidates(E->getRHS());
+
+    for (NamedDecl *D :
+         Record.getASTContext().getTranslationUnitDecl()->noload_lookup(Name))
+      Writer.GetDeclRef(D);
+  }
+
   Code = serialization::EXPR_BINARY_OPERATOR;
 }
 
@@ -2045,6 +2083,24 @@ void ASTStmtWriter::VisitCXXNewExpr(CXXNewExpr *E) {
 
   Record.AddDeclRef(E->getOperatorNew());
   Record.AddDeclRef(E->getOperatorDelete());
+
+  // Preserve the global candidates that the lookup at instantiation can find;
+  // otherwise a reduced BMI can elide them because the dependent CXXNewExpr 
has
+  // no direct reference to an allocation function.
+  if (Writer.isGeneratingReducedBMI() && !E->getOperatorNew()) {
+    auto PreserveGlobalCandidates = [&](OverloadedOperatorKind Kind) {
+      DeclarationName Name =
+          Record.getASTContext().DeclarationNames.getCXXOperatorName(Kind);
+      for (NamedDecl *Found :
+           
Record.getASTContext().getTranslationUnitDecl()->noload_lookup(Name))
+        if (!Found->isImplicit())
+          Writer.GetDeclRef(Found);
+    };
+
+    PreserveGlobalCandidates(E->isArray() ? OO_Array_New : OO_New);
+    PreserveGlobalCandidates(E->isArray() ? OO_Array_Delete : OO_Delete);
+  }
+
   Record.AddTypeSourceInfo(E->getAllocatedTypeSourceInfo());
   if (E->isParenTypeId())
     Record.AddSourceRange(E->getTypeIdParens());

diff  --git a/clang/test/Modules/reduced-bmi-dependent-lookup.cpp 
b/clang/test/Modules/reduced-bmi-dependent-lookup.cpp
new file mode 100644
index 0000000000000..4fd70b851db3d
--- /dev/null
+++ b/clang/test/Modules/reduced-bmi-dependent-lookup.cpp
@@ -0,0 +1,57 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 %t/M.cppm -emit-module-interface -o %t/M.pcm
+// RUN: %clang_cc1 -std=c++20 %t/use.cpp -fprebuilt-module-path=%t \
+// RUN:   -fsyntax-only -verify
+//
+// RUN: %clang_cc1 -std=c++20 %t/M.cppm -emit-obj -fmodules-reduced-bmi \
+// RUN:   -fmodule-output=%t/M.pcm -o %t/M.o
+// RUN: %clang_cc1 -std=c++20 %t/use.cpp -fprebuilt-module-path=%t \
+// RUN:   -fsyntax-only -verify
+
+//--- support.h
+using size_t = decltype(sizeof(0));
+
+struct placement_tag {};
+inline void *operator new(size_t, void *p) { return p; }
+inline void *operator new(size_t, void *p, placement_tag) { return p; }
+
+namespace ranges {
+struct reverse_fn {};
+inline constexpr reverse_fn reverse;
+
+template <class Range>
+bool operator|(Range &&, reverse_fn) {
+  return true;
+}
+} // namespace ranges
+
+//--- M.cppm
+module;
+#include "support.h"
+export module M;
+
+export template <class T>
+struct box {
+  alignas(T) unsigned char storage[sizeof(T)];
+
+  void construct(T value) { ::new (static_cast<void *>(storage)) T(value); }
+
+  void construct_tagged(T value) {
+    ::new (static_cast<void *>(storage), placement_tag{}) T(value);
+  }
+
+  bool reverse() { return *this | ranges::reverse; }
+};
+
+//--- use.cpp
+// expected-no-diagnostics
+import M;
+
+void use() {
+  box<int> b;
+  b.construct(42);
+  b.construct_tagged(43);
+  (void)b.reverse();
+}


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to