llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Nemi26

<details>
<summary>Changes</summary>

Under the Microsoft ABI, a lambda in a default argument of a non-member 
function template can get the same mangled name as a lambda in the function's 
body. When that happens, calls to one call operator go to the other. It's a 
silent miscompile:

```cpp
template &lt;typename F&gt; struct Lazy { F f; int get() { int v = 0; f(v); 
return v; } };
template &lt;typename B&gt; int shoot(B &amp;&amp;b) { Lazy&lt;B &amp;&gt; 
l{b}; return l.get(); }
template &lt;int N, typename E = void (*)(int &amp;)&gt;
int track(E &amp;&amp;e = [](int &amp;) {}) {
  return shoot([&amp;](int &amp;v) { v = N; e(v); });
}
int main() { return track&lt;1&gt;() * 10 + track&lt;2&gt;(); } // 12; 10 for 
x86_64-pc-windows-msvc
```

The default argument's lambda has no mangling number and no context 
declaration, because `getCurrentMangleNumberContext` leaves default arguments 
of non-member function templates unnumbered. Its instantiation still lives in 
the function. The Microsoft mangler then names it `&lt;lambda_N&gt;`, taking N 
from the translation unit's count of unnumbered lambdas, which starts at 0. The 
function's own lambdas are numbered from 1 in the same scope. In 
`track&lt;2&gt;`, both closure types become 
`&lt;lambda_1&gt;@?0???$track@$01...`. Itanium is unaffected, because it spells 
unnumbered lambdas `$_N`.

This patch finds the parameter whose default argument spans such a lambda and 
mangles the lambda in the default-argument form `&lt;lambda_K_N&gt;`, which 
lambdas numbered in a default argument (class members) already use. The fix 
stays in the mangler. Numbering these lambdas in Sema would also change 
Itanium's mangling.

Tested with the new `mangle-ms-lambda-template-default-arg.cpp`, which fails 
without the patch. `clang/test/CodeGenCXX` and `clang/test/DebugInfo/CXX` pass, 
except 8 tests that need tools my build doesn't have (`llvm-dis`, `llvm-link`, 
`llvm-profdata`).

This patch was written with the assistance of an AI tool (Claude Code, Claude 
Opus); I reviewed it.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---
Full diff: https://github.com/llvm/llvm-project/pull/232294.diff


2 Files Affected:

- (modified) clang/lib/AST/MicrosoftMangle.cpp (+24-4) 
- (added) clang/test/CodeGenCXX/mangle-ms-lambda-template-default-arg.cpp (+16) 


``````````diff
diff --git a/clang/lib/AST/MicrosoftMangle.cpp 
b/clang/lib/AST/MicrosoftMangle.cpp
index 74edee531e48a..60f3d804c2bc3 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 0000000000000..16759ccdca3c0
--- /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"(

``````````

</details>


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

Reply via email to