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
