https://github.com/ChuanqiXu9 created https://github.com/llvm/llvm-project/pull/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. >From 63ad6091cdeaf182341ee971b88c8d106a0a908e Mon Sep 17 00:00:00 2001 From: Chuanqi Xu <[email protected]> Date: Thu, 27 Aug 2026 16:38:28 +0800 Subject: [PATCH] [C++20] [Modules] Preserve placement new and operator for ADL in Reduced BMI 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. --- clang/lib/Serialization/ASTWriterStmt.cpp | 56 ++++++++++++++++++ .../Modules/reduced-bmi-dependent-lookup.cpp | 57 +++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 clang/test/Modules/reduced-bmi-dependent-lookup.cpp 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
