https://github.com/Nemi26 updated https://github.com/llvm/llvm-project/pull/232294
>From 99fc7d50fb321ad10528d37b18fd2e65e5400cd2 Mon Sep 17 00:00:00 2001 From: Nemi26 <[email protected]> Date: Thu, 15 Oct 2026 14:10:44 +0200 Subject: [PATCH] [clang][MS] Keep default-argument lambdas of function templates apart from the function's own lambdas A lambda in a default argument of a non-member function template gets no mangling number and no context declaration, but its instantiation lives in the function. The Microsoft mangler named it <lambda_N> from the translation unit's count of unnumbered lambdas, which starts at 0, while the function's own lambdas are numbered from 1 in the same scope. When the two numbers met, both closure types got the same mangled name, and calls to one call operator went to the other, a silent miscompile. Find the parameter whose default argument spans such a lambda and mangle it in the default-argument form <lambda_K_N>, as lambdas numbered in a default argument already are. This patch was written with the assistance of an AI tool (Claude Code, Claude Opus); the submitter reviewed it. Assisted-by: Claude Code (Claude Opus) --- clang/lib/AST/MicrosoftMangle.cpp | 28 ++++++++++++++++--- .../mangle-ms-lambda-template-default-arg.cpp | 16 +++++++++++ 2 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 clang/test/CodeGenCXX/mangle-ms-lambda-template-default-arg.cpp diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp index 74edee531e48a0..60f3d804c2bc35 100644 --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -86,6 +86,28 @@ struct msvc_hashing_ostream : public llvm::raw_svector_ostream { } }; +/// The parameter whose default argument holds the lambda, if any. An unnumbered +/// lambda has no context declaration, so it is found by its location. +static const ParmVarDecl *getLambdaDefaultArgParm(const CXXRecordDecl *Lambda) { + if (const auto *Parm = + dyn_cast_or_null<ParmVarDecl>(Lambda->getLambdaContextDecl())) + return Parm; + if (Lambda->getLambdaManglingNumber()) + return nullptr; + + const auto *Func = dyn_cast<FunctionDecl>(Lambda->getDeclContext()); + if (!Func) + return nullptr; + const SourceManager &SM = Lambda->getASTContext().getSourceManager(); + for (const ParmVarDecl *Parm : Func->parameters()) { + SourceRange Range = Parm->getDefaultArgRange(); + if (Range.isValid() && SM.isPointWithin(Lambda->getLocation(), + Range.getBegin(), Range.getEnd())) + return Parm; + } + return nullptr; +} + static const DeclContext * getLambdaDefaultArgumentDeclContext(const Decl *D) { if (const auto *RD = dyn_cast<CXXRecordDecl>(D)) @@ -250,10 +272,9 @@ class MicrosoftMangleContextImpl : public MicrosoftMangleContext { assert(Lambda->isLambda() && "RD must be a lambda!"); std::string Name("<lambda_"); - Decl *LambdaContextDecl = Lambda->getLambdaContextDecl(); unsigned LambdaManglingNumber = Lambda->getLambdaManglingNumber(); unsigned LambdaId; - const ParmVarDecl *Parm = dyn_cast_or_null<ParmVarDecl>(LambdaContextDecl); + const ParmVarDecl *Parm = getLambdaDefaultArgParm(Lambda); const FunctionDecl *Func = Parm ? dyn_cast<FunctionDecl>(Parm->getDeclContext()) : nullptr; @@ -1245,8 +1266,7 @@ void MicrosoftCXXNameMangler::mangleUnqualifiedName(GlobalDecl GD, Decl *LambdaContextDecl = Record->getLambdaContextDecl(); unsigned LambdaManglingNumber = Record->getLambdaManglingNumber(); unsigned LambdaId; - const ParmVarDecl *Parm = - dyn_cast_or_null<ParmVarDecl>(LambdaContextDecl); + const ParmVarDecl *Parm = getLambdaDefaultArgParm(Record); const FunctionDecl *Func = Parm ? dyn_cast<FunctionDecl>(Parm->getDeclContext()) : nullptr; diff --git a/clang/test/CodeGenCXX/mangle-ms-lambda-template-default-arg.cpp b/clang/test/CodeGenCXX/mangle-ms-lambda-template-default-arg.cpp new file mode 100644 index 00000000000000..16759ccdca3c04 --- /dev/null +++ b/clang/test/CodeGenCXX/mangle-ms-lambda-template-default-arg.cpp @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -std=c++20 -emit-llvm %s -o - -triple=x86_64-pc-windows-msvc | FileCheck %s + +// A default argument's lambda is named apart from the function's own lambdas. + +template <typename F> int call(F f) { return f(); } + +template <int N> int g(int (*f)() = [] { return 10; }) { + return f() + call([] { return N; }); +} + +int use() { return g<1>() + g<2>(); } + +// CHECK-DAG: define {{.*}} @"??R<lambda_1>@?0???$g@$00@@YAHP6AHXZ@Z@QEBA?A?<auto>@@XZ"( +// CHECK-DAG: define {{.*}} @"??R<lambda_1>@?0???$g@$01@@YAHP6AHXZ@Z@QEBA?A?<auto>@@XZ"( +// CHECK-DAG: define internal {{.*}} @"??R<lambda_1_{{[0-9]+}}>@?0???$g@$00@@YAHP6AHXZ@Z@QEBA?A?<auto>@@XZ"( +// CHECK-DAG: define internal {{.*}} @"??R<lambda_1_{{[0-9]+}}>@?0???$g@$01@@YAHP6AHXZ@Z@QEBA?A?<auto>@@XZ"( _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
