Repository: llvm/llvm-project
Pull request #134038: [Clang] Fix dependent local class instantiation bugs
Author: login: zyn0217
Labels: name: clang
description: Clang issues not falling into any other category
name: clang:frontend
description: Language frontend issues, e.g. anything involving "Sema"
Created: 2025-04-02T06:47:03Z | Merged: 2025-04-15T01:16:55Z

This patch fixes two long-standing bugs that prevent Clang from instantiating local class members inside a dependent context. These bugs were introduced in commits 21eb1af469c3 and 919df9d75a.

21eb1af469c3 introduced a concept called eligible methods such that it did an attempt to skip past ineligible method instantiation when instantiating class members. Unfortunately, this broke the instantiation chain for local classes - getTemplateInstantiationPattern() would fail to find the correct definition pattern if the class was defined within a partially transformed dependent context.

919df9d75a introduced a separate issue by incorrectly copying the DeclarationNameInfo during function definition instantiation from the template pattern, even though that DNI might contain a transformed TypeSourceInfo. Since that TSI was already updated when the declaration was instantiated, this led to inconsistencies. As a result, the final instantiated function could lose track of the transformed declarations, hence we crash: https://compiler-explorer.com/z/vjvoG76Tf.

This PR corrects them by

1. Removing the bypass logic for method instantiation. The eligible flag is independent of instantiation and can be updated properly afterward, so skipping instantiation is unnecessary.

2. Carefully handling TypeSourceInfo by creating a new instance that preserves the pattern's source location while using the already transformed type.

Fixes https://github.com/llvm/llvm-project/issues/59734
Fixes https://github.com/llvm/llvm-project/issues/132208



Diff:
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index e409f206f6eae..6107fd7667306 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -348,6 +348,7 @@ Bug Fixes to C++ Support
   by template argument deduction.
 - Clang is now better at instantiating the function definition after its use inside
   of a constexpr lambda. (#GH125747)
+- Fixed a local class member function instantiation bug inside dependent lambdas. (#GH59734), (#GH132208)
 - Clang no longer crashes when trying to unify the types of arrays with
   certain differences in qualifiers (this could happen during template argument
   deduction or when building a ternary operator). (#GH97005)
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 00dcadb41e8fb..5b502b494e410 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -4264,9 +4264,6 @@ Sema::InstantiateClassMembers(SourceLocation PointOfInstantiation,
       if (FunctionDecl *Pattern =
               Function->getInstantiatedFromMemberFunction()) {
 
-        if (Function->isIneligibleOrNotSelected())
-          continue;
-
         if (Function->getTrailingRequiresClause()) {
           ConstraintSatisfaction Satisfaction;
           if (CheckFunctionConstraints(Function, Satisfaction) ||
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 8aaaea0bcdd66..ab361c2a0f854 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -5590,7 +5590,61 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
   Function->setLocation(PatternDecl->getLocation());
   Function->setInnerLocStart(PatternDecl->getInnerLocStart());
   Function->setRangeEnd(PatternDecl->getEndLoc());
-  Function->setDeclarationNameLoc(PatternDecl->getNameInfo().getInfo());
+  // Let the instantiation use the Pattern's DeclarationNameLoc, due to the
+  // following awkwardness:
+  //
+  //   1. There are out-of-tree users of getNameInfo().getSourceRange(), who
+  //   expect the source range of the instantiated declaration to be set to
+  //   point to the definition.
+  //
+  //   2. That getNameInfo().getSourceRange() might return the TypeLocInfo's
+  //   location it tracked.
+  //
+  //   3. Function might come from an (implicit) declaration, while the pattern
+  //   comes from a definition. In these cases, we need the PatternDecl's source
+  //   location.
+  //
+  // To that end, we need to more or less tweak the DeclarationNameLoc. However,
+  // we can't blindly copy the DeclarationNameLoc from the PatternDecl to the
+  // function, since it contains associated TypeLocs that should have already
+  // been transformed. So, we rebuild the TypeLoc for that purpose. Technically,
+  // we should create a new function declaration and assign everything we need,
+  // but InstantiateFunctionDefinition updates the declaration in place.
+  auto NameLocPointsToPattern = [&] {
+    DeclarationNameInfo PatternName = PatternDecl->getNameInfo();
+    DeclarationNameLoc PatternNameLoc = PatternName.getInfo();
+    switch (PatternName.getName().getNameKind()) {
+    case DeclarationName::CXXConstructorName:
+    case DeclarationName::CXXDestructorName:
+    case DeclarationName::CXXConversionFunctionName:
+      break;
+    default:
+      // Cases where DeclarationNameLoc doesn't matter, as it merely contains a
+      // source range.
+      return PatternNameLoc;
+    }
+
+    TypeSourceInfo *TSI = Function->getNameInfo().getNamedTypeInfo();
+    // TSI might be null if the function is named by a constructor template id.
+    // E.g. S<T>() {} for class template S with a template parameter T.
+    if (!TSI) {
+      // We don't care about the DeclarationName of the instantiated function,
+      // but only the DeclarationNameLoc. So if the TypeLoc is absent, we do
+      // nothing.
+      return PatternNameLoc;
+    }
+
+    QualType InstT = TSI->getType();
+    // We want to use a TypeLoc that reflects the transformed type while
+    // preserving the source location from the pattern.
+    TypeLocBuilder TLB;
+    TLB.pushTrivial(
+        Context, InstT,
+        PatternNameLoc.getNamedTypeInfo()->getTypeLoc().getBeginLoc());
+    return DeclarationNameLoc::makeNamedTypeLoc(
+        TLB.getTypeSourceInfo(Context, InstT));
+  };
+  Function->setDeclarationNameLoc(NameLocPointsToPattern());
 
   EnterExpressionEvaluationContext EvalContext(
       *this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated);
diff --git a/clang/test/CodeGenCXX/local-class-instantiation.cpp b/clang/test/CodeGenCXX/local-class-instantiation.cpp
new file mode 100644
index 0000000000000..34103a1ee55ef
--- /dev/null
+++ b/clang/test/CodeGenCXX/local-class-instantiation.cpp
@@ -0,0 +1,64 @@
+// RUN: %clang_cc1 -std=c++17 %s -emit-llvm -triple %itanium_abi_triple -o - | FileCheck %s
+
+namespace LambdaContainingLocalClasses {
+
+template <typename F>
+void GH59734() {
+  [&](auto param) {
+    struct Guard {
+      Guard() {
+        // Check that we're able to create DeclRefExpr to param at this point.
+        static_assert(__is_same(decltype(param), int), "");
+      }
+      ~Guard() {
+        static_assert(__is_same(decltype(param), int), "");
+      }
+      operator decltype(param)() {
+        return decltype(param)();
+      }
+    };
+    Guard guard;
+    param = guard;
+  }(42);
+}
+
+// Guard::Guard():
+// CHECK-DAG: define {{.*}} @_ZZZN28LambdaContainingLocalClasses7GH59734IiEEvvENKUlT_E_clIiEEDaS1_EN5GuardC2Ev
+// Guard::operator int():
+// CHECK-DAG: define {{.*}} @_ZZZN28LambdaContainingLocalClasses7GH59734IiEEvvENKUlT_E_clIiEEDaS1_EN5GuardcviEv
+// Guard::~Guard():
+// CHECK-DAG: define {{.*}} @_ZZZN28LambdaContainingLocalClasses7GH59734IiEEvvENKUlT_E_clIiEEDaS1_EN5GuardD2Ev
+
+struct S {};
+
+template <class T = void>
+auto GH132208 = [](auto param) {
+  struct OnScopeExit {
+    OnScopeExit() {
+      static_assert(__is_same(decltype(param), S), "");
+    }
+    ~OnScopeExit() {
+      static_assert(__is_same(decltype(param), S), "");
+    }
+    operator decltype(param)() {
+      return decltype(param)();
+    }
+  } pending;
+
+  param = pending;
+};
+
+void bar() {
+  GH59734<int>();
+
+  GH132208<void>(S{});
+}
+
+// OnScopeExit::OnScopeExit():
+// CHECK-DAG: define {{.*}} @_ZZNK28LambdaContainingLocalClasses8GH132208IvEMUlT_E_clINS_1SEEEDaS2_EN11OnScopeExitC2Ev
+// OnScopeExit::operator S():
+// CHECK-DAG: define {{.*}} @_ZZNK28LambdaContainingLocalClasses8GH132208IvEMUlT_E_clINS_1SEEEDaS2_EN11OnScopeExitcvS5_Ev
+// OnScopeExit::~OnScopeExit():
+// CHECK-DAG: define {{.*}} @_ZZNK28LambdaContainingLocalClasses8GH132208IvEMUlT_E_clINS_1SEEEDaS2_EN11OnScopeExitD2Ev
+
+} // namespace LambdaContainingLocalClasses


Reviews:
user: login: erichkeane
body: This once again looks like an attempt at hacking around our lack of delayed lambda body instantiation... And this one has some pretty sizable negatives as far as I can see.  I'm not sure I really like this direction.
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-02T13:37:55Z
user: login: zyn0217
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T13:49:37Z
user: login: erichkeane
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-03T15:14:06Z
user: login: erichkeane
body: Woops!  Sorry, I thought I had already approved this with teh nit about the tests.  Sorry about that!
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-14T13:25:29Z
user: login: mizvekov
body: LGTM as well.
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-15T00:34:59Z

Review comments:
diff_hunk: @@ -4264,7 +4264,8 @@ Sema::InstantiateClassMembers(SourceLocation PointOfInstantiation,
       if (FunctionDecl *Pattern =
               Function->getInstantiatedFromMemberFunction()) {
 
-        if (Function->isIneligibleOrNotSelected())
+        if (!Instantiation->getDeclContext()->isDependentContext() &&
path: clang/lib/Sema/SemaTemplateInstantiate.cpp
user: login: erichkeane
body: Hmm... I don't think we want to go this far, right?    We still would like to aggressively instantiate these so we get diagnostics/etc, even if it is in a dependent context.  
created_at: 2025-04-02T13:37:07Z
author_association: COLLABORATOR
diff_hunk: @@ -4264,7 +4264,8 @@ Sema::InstantiateClassMembers(SourceLocation PointOfInstantiation,
       if (FunctionDecl *Pattern =
               Function->getInstantiatedFromMemberFunction()) {
 
-        if (Function->isIneligibleOrNotSelected())
+        if (!Instantiation->getDeclContext()->isDependentContext() &&
path: clang/lib/Sema/SemaTemplateInstantiate.cpp
user: login: zyn0217
body: It does instantiate the function declarations in the dependent context. In fact this fixes a bug introduced in clang 15: we skipped the instantiation of local classes here because they're not correctly marked "eligible" because the context the class lives in is dependent.

On the other hand I'm not quite clear if it is suitable to jump past any instantiations at all. it might be an incorrect optimization, but again I'm not quite familiar with the proposal that introduced this line.
created_at: 2025-04-02T13:49:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,6 +1,9 @@
 // RUN: %clang_cc1 -verify -std=c++11 %s
 // RUN: %clang_cc1 -verify -std=c++11 -fdelayed-template-parsing %s
 // RUN: %clang_cc1 -verify -std=c++20 -fsyntax-only %s
+// RUN: %clang_cc1 -std=c++17 %s -DCodeGen -emit-llvm -triple %itanium_abi_triple -o - | FileCheck %s -dump-input=always
+
+#ifndef CodeGen
path: clang/test/SemaTemplate/instantiate-local-class.cpp
user: login: erichkeane
body: Please don't do this.  Just add a separate test to the CodeGen directory.
created_at: 2025-04-03T15:14:05Z
author_association: COLLABORATOR

PR comments:
user: login: zyn0217
created_at: 2025-04-02T13:54:32Z
body: > This once again looks like an attempt at hacking around our lack of delayed lambda body instantiation... And this one has some pretty sizable negatives as far as I can see.  I'm not sure I really like this direction.

Yeah, admittedly this is another workaround of not having that mechanism. But the bug was a 15 regression, (which means we don't have to rely on that for it to work!) and due to some historic incorrect patches (which I'm fixing here) it ends up becoming hard to get the behavior correct as 14.
author_association: CONTRIBUTOR
user: login: erichkeane
created_at: 2025-04-02T13:57:50Z
body: > > This once again looks like an attempt at hacking around our lack of delayed lambda body instantiation... And this one has some pretty sizable negatives as far as I can see.  I'm not sure I really like this direction.
> 
> Yeah, admittedly this is another workaround of not having that mechanism. But the bug was a 15 regression, (which means we don't have to rely on that for it to work!) and due to some historic incorrect patches (which I'm fixing here) it ends up becoming hard to get the behavior correct as 14.

Oh, totally understood.  This is just a 'stuck between a rock and a hard place' sort of situation, and I'm pretty uncomfortable here again.

I wouldn't be able to convince you to start work on the delayed lambda body instantiation instead, would I ? :-P
author_association: COLLABORATOR
user: login: zyn0217
created_at: 2025-04-02T14:02:02Z
body: > I wouldn't be able to convince you to start work on the delayed lambda body instantiation instead, would I ? :-P

AFAIK @LYP951018 is already working on this region. Though I haven't test that patch with it yet 🥲

For Xref: https://github.com/llvm/llvm-project/pull/105953

author_association: CONTRIBUTOR
user: login: zyn0217
created_at: 2025-04-03T07:20:57Z
body: I talked to @erichkeane and updated the approach to make it feel less like a workaround and independent of lambda instantiation.

As noted, both cases worked prior to clang 15, which means they didn’t rely on deferred lambda body instantiation to compile correctly.

Regarding the lambda side of things - since @mizvekov is already working on a major ContextDecl refactoring in that area, I don’t think it's appropriate to initiate another large-scale change simultaneously. That would result in a lot of conflicts, as I can expect. It's also expected that much of the present issues will be alleviated once that gets landed and it's not too late for us to start working on the lambda deferral afterwards.

That said, this patch is aimed at fixing a regression and doesn't actually touch the lambda code itself (even though the bug involves lambdas), so it shouldn't become a burden that complicates future work on deferred lambda body instantiation. In fact, I think this should actually improve our test coverage overall.

There's little impact on the performance:

http://llvm-compile-time-tracker.com/compare.php?stat=instructions%3Au&from=fa5025b76034bcdd65d3a96eb29ae1edc18b876e&to=5aa9923638de93fedfbac1d8ea7f8677a88daaaf
author_association: CONTRIBUTOR
user: login: llvmbot
created_at: 2025-04-03T08:00:52Z
body: 
<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Younan Zhang (zyn0217)

<details>
<summary>Changes</summary>

This patch fixes two long-standing bugs that prevent Clang from instantiating local class members inside a dependent context. These bugs were introduced in commits 21eb1af469c3 and 919df9d75a.

21eb1af469c3 introduced a concept called eligible methods such that it did an attempt to skip past ineligible method instantiation when instantiating class members. Unfortunately, this broke the instantiation chain for local classes - getTemplateInstantiationPattern() would fail to find the correct definition pattern if the class was defined within a partially transformed dependent context.

919df9d75a introduced a separate issue by incorrectly copying the DeclarationNameInfo during function definition instantiation from the template pattern, even though that DNI might contain a transformed TypeSourceInfo. Since that TSI was already updated when the declaration was instantiated, this led to inconsistencies. As a result, the final instantiated function could lose track of the transformed declarations, hence we crash: https://compiler-explorer.com/z/vjvoG76Tf.

This PR corrects them by

1. Removing the bypass logic for method instantiation. The eligible flag is independent of instantiation and can be updated properly afterward, so skipping instantiation is unnecessary.

2. Carefully handling TypeSourceInfo by creating a new instance that preserves the pattern's source location while using the already transformed type.

Fixes https://github.com/llvm/llvm-project/issues/59734
Fixes https://github.com/llvm/llvm-project/issues/132208


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


4 Files Affected:

- (modified) clang/docs/ReleaseNotes.rst (+1) 
- (modified) clang/lib/Sema/SemaTemplateInstantiate.cpp (-3) 
- (modified) clang/lib/Sema/SemaTemplateInstantiateDecl.cpp (+56-1) 
- (modified) clang/test/SemaTemplate/instantiate-local-class.cpp (+73-1) 


``````````diff
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index e409f206f6eae..6107fd7667306 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -348,6 +348,7 @@ Bug Fixes to C++ Support
   by template argument deduction.
 - Clang is now better at instantiating the function definition after its use inside
   of a constexpr lambda. (#GH125747)
+- Fixed a local class member function instantiation bug inside dependent lambdas. (#GH59734), (#GH132208)
 - Clang no longer crashes when trying to unify the types of arrays with
   certain differences in qualifiers (this could happen during template argument
   deduction or when building a ternary operator). (#GH97005)
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 00dcadb41e8fb..5b502b494e410 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -4264,9 +4264,6 @@ Sema::InstantiateClassMembers(SourceLocation PointOfInstantiation,
       if (FunctionDecl *Pattern =
               Function->getInstantiatedFromMemberFunction()) {
 
-        if (Function->isIneligibleOrNotSelected())
-          continue;
-
         if (Function->getTrailingRequiresClause()) {
           ConstraintSatisfaction Satisfaction;
           if (CheckFunctionConstraints(Function, Satisfaction) ||
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 8aaaea0bcdd66..a8d23fb2be6d5 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -5590,7 +5590,62 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
   Function->setLocation(PatternDecl->getLocation());
   Function->setInnerLocStart(PatternDecl->getInnerLocStart());
   Function->setRangeEnd(PatternDecl->getEndLoc());
-  Function->setDeclarationNameLoc(PatternDecl->getNameInfo().getInfo());
+  // Let the instantiation use the Pattern's DeclarationNameLoc, due to the
+  // following awkwardness:
+  //   1. There are out-of-tree users of getNameInfo().getSourceRange(), who
+  //   expect the source range of the instantiated declaration to be set to
+  //   point the definition.
+  //
+  //   2. That getNameInfo().getSourceRange() might return the TypeLocInfo's
+  //   location it tracked.
+  //
+  //   3. Function might come from an (implicit) declaration, while the pattern
+  //   comes from a definition. In these cases, we need the PatternDecl's source
+  //   location.
+  //
+  // To that end, we need to more or less tweak the DeclarationNameLoc. However,
+  // we can't blindly copy the DeclarationNameLoc from the PatternDecl to the
+  // function, since it contains associated TypeLocs that should have already
+  // been transformed. So, we rebuild the TypeLoc for that purpose. Technically,
+  // we should create a new function declaration and assign everything we need,
+  // but InstantiateFunctionDefinition updates the declaration in place.
+  auto CorrectNameLoc = [&] {
+    DeclarationNameInfo PatternName = PatternDecl->getNameInfo();
+    DeclarationNameLoc PatternNameLoc = PatternName.getInfo();
+    switch (PatternName.getName().getNameKind()) {
+    case DeclarationName::CXXConstructorName:
+    case DeclarationName::CXXDestructorName:
+    case DeclarationName::CXXConversionFunctionName:
+      break;
+    default:
+      // Cases where DeclarationNameLoc doesn't matter, as it merely contains a
+      // source range.
+      Function->setDeclarationNameLoc(PatternNameLoc);
+      return;
+    }
+
+    TypeSourceInfo *TSI = Function->getNameInfo().getNamedTypeInfo();
+    // !!! When could TSI be null?
+    if (!TSI) {
+      // We don't care about the DeclarationName of the instantiated function,
+      // but only the DeclarationNameLoc. So if the TypeLoc is absent, we do
+      // nothing.
+      Function->setDeclarationNameLoc(PatternNameLoc);
+      return;
+    }
+
+    QualType InstT = TSI->getType();
+    // We want to use a TypeLoc that reflects the transformed type while
+    // preserving the source location from the pattern.
+    TypeLocBuilder TLB;
+    TLB.pushTrivial(
+        Context, InstT,
+        PatternNameLoc.getNamedTypeInfo()->getTypeLoc().getBeginLoc());
+    Function->setDeclarationNameLoc(DeclarationNameLoc::makeNamedTypeLoc(
+        TLB.getTypeSourceInfo(Context, InstT)));
+  };
+
+  CorrectNameLoc();
 
   EnterExpressionEvaluationContext EvalContext(
       *this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated);
diff --git a/clang/test/SemaTemplate/instantiate-local-class.cpp b/clang/test/SemaTemplate/instantiate-local-class.cpp
index 298233739900f..bd6ef84a2bc0a 100644
--- a/clang/test/SemaTemplate/instantiate-local-class.cpp
+++ b/clang/test/SemaTemplate/instantiate-local-class.cpp
@@ -1,6 +1,9 @@
 // RUN: %clang_cc1 -verify -std=c++11 %s
 // RUN: %clang_cc1 -verify -std=c++11 -fdelayed-template-parsing %s
 // RUN: %clang_cc1 -verify -std=c++20 -fsyntax-only %s
+// RUN: %clang_cc1 -std=c++17 %s -DCodeGen -emit-llvm -triple %itanium_abi_triple -o - | FileCheck %s -dump-input=always
+
+#ifndef CodeGen
 
 template<typename T>
 void f0() {
@@ -60,7 +63,7 @@ namespace PR8801 {
     class X;
     typedef int (X::*pmf_type)();
     class X : public T { };
-    
+
     pmf_type pmf = &T::foo;
   }
 
@@ -535,3 +538,72 @@ void foo() {
 } // namespace local_recursive_lambda
 
 #endif
+
+#endif // CodeGen
+
+#ifdef CodeGen
+
+namespace LambdaContainingLocalClasses {
+
+template <typename F>
+void GH59734() {
+  [&](auto param) {
+    struct Guard {
+      Guard() {
+        // Check that we're able to create DeclRefExpr to param at this point.
+        static_assert(__is_same(decltype(param), int), "");
+      }
+      ~Guard() {
+        static_assert(__is_same(decltype(param), int), "");
+      }
+      operator decltype(param)() {
+        return decltype(param)();
+      }
+    };
+    Guard guard;
+    param = guard;
+  }(42);
+}
+
+// Guard::Guard():
+// CHECK-DAG: define {{.*}} @_ZZZN28LambdaContainingLocalClasses7GH59734IiEEvvENKUlT_E_clIiEEDaS1_EN5GuardC2Ev
+// Guard::operator int():
+// CHECK-DAG: define {{.*}} @_ZZZN28LambdaContainingLocalClasses7GH59734IiEEvvENKUlT_E_clIiEEDaS1_EN5GuardcviEv
+// Guard::~Guard():
+// CHECK-DAG: define {{.*}} @_ZZZN28LambdaContainingLocalClasses7GH59734IiEEvvENKUlT_E_clIiEEDaS1_EN5GuardD2Ev
+
+struct S {};
+
+template <class T = void>
+auto GH132208 = [](auto param) {
+  struct OnScopeExit {
+    OnScopeExit() {
+      static_assert(__is_same(decltype(param), S), "");
+    }
+    ~OnScopeExit() {
+      static_assert(__is_same(decltype(param), S), "");
+    }
+    operator decltype(param)() {
+      return decltype(param)();
+    }
+  } pending;
+
+  param = pending;
+};
+
+void bar() {
+  GH59734<int>();
+
+  GH132208<void>(S{});
+}
+
+// OnScopeExit::OnScopeExit():
+// CHECK-DAG: define {{.*}} @_ZZNK28LambdaContainingLocalClasses8GH132208IvEMUlT_E_clINS_1SEEEDaS2_EN11OnScopeExitC2Ev
+// OnScopeExit::operator S():
+// CHECK-DAG: define {{.*}} @_ZZNK28LambdaContainingLocalClasses8GH132208IvEMUlT_E_clINS_1SEEEDaS2_EN11OnScopeExitcvS5_Ev
+// OnScopeExit::~OnScopeExit():
+// CHECK-DAG: define {{.*}} @_ZZNK28LambdaContainingLocalClasses8GH132208IvEMUlT_E_clINS_1SEEEDaS2_EN11OnScopeExitD2Ev
+
+} // namespace LambdaContainingLocalClasses
+
+#endif

``````````

</details>

author_association: MEMBER
user: login: zyn0217
created_at: 2025-04-14T06:51:23Z
body: Review ping
author_association: CONTRIBUTOR
user: login: mysterymath
created_at: 2025-04-15T18:19:13Z
body: This looks to be the likely culprit for a clang crash in the Fuchsia build:

```
[21173/168418](63) CXX host_x64/obj/src/developer/debug/zxdb/client/libclient.map_setting_store.cc.o
FAILED: [code=1] host_x64/obj/src/developer/debug/zxdb/client/libclient.map_setting_store.cc.o
../../prebuilt/third_party/clang/custom/bin/clang++ -MD -MF host_x64/obj/src/developer/debug/zxdb/client/libclient.map_setting_store.cc.o.d -D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS -D_LIBCPP_REMOVE_TRANSITIVE_INCLUDES -DCURL_NO_OLDIES -DLLVM_USING_OLD_PREBUILT -DRAPIDJSON_HAS_STDSTRING -DRAPIDJSON_HAS_CXX11_RANGE_FOR -DRAPIDJSON_HAS_CXX11_RVALUE_REFS -DRAPIDJSON_HAS_CXX11_TYPETRAITS -DRAPIDJSON...
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace, preprocessed source, and associated run script.
Stack dump:
0.	Program arguments: ../../prebuilt/third_party/clang/custom/bin/clang++ -MD -MF host_x64/obj/src/developer/debug/zxdb/client/libclient.map_setting_store.cc.o.d -D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS -D_LIBCPP_REMOVE_TRANSITIVE_INCLUDES -DCURL_NO_OLDIES -DLLVM_USING_OLD_PREBUILT -DRAPIDJSON_HAS_STDSTRING -DRAPIDJSON_HAS_CXX11_RANGE_FOR -DRAPIDJSON_HAS_CXX11_RVALUE_REFS -DRAPIDJSON_HAS_CXX11_...
1.	<eof> parser at end of file
2.	../../src/lib/fxl/observer_list.h:193:54: instantiating function definition 'fxl::ObserverListBase<zxdb::SettingStoreObserver>::Iter<const fxl::ObserverListBase<zxdb::SettingStoreObserver>>::~Iter'
#0 0x000055e06ddeea18 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (../../prebuilt/third_party/clang/custom/bin/clang+++0x9164a18)
clang++: error: clang frontend command failed with exit code 139 (use -v to see invocation)
Fuchsia clang version 21.0.0git (https://llvm.googlesource.com/llvm-project 1cf9f764ac41fb3492e10c78640dd50e616388db)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: ../../prebuilt/third_party/clang/custom/bin
Build config: +assertions
clang++: note: diagnostic msg:
********************

PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT:
Preprocessed source(s) and associated run script(s) are located at:
clang++: note: diagnostic msg: clang-crashreports/map_setting_store-0b592c.cpp
clang++: note: diagnostic msg: clang-crashreports/map_setting_store-0b592c.sh
clang++: note: diagnostic msg:

********************
```

Does it seem likely this PR is the culprit? If so, can this be fixed forward or reverted?

https://luci-milo.appspot.com/ui/p/fuchsia/builders/ci/clang_toolchain.ci.core.x64-debug/b8717549930672410881/overview
author_association: CONTRIBUTOR
user: login: mizvekov
created_at: 2025-04-15T18:26:44Z
body: Can you send reproducer and / or stack trace?

FYI you can use `CLANG_CRASH_DIAGNOSTICS_DIR` environment variable to make clang put these automatically generated reproducers up in a directory which will have its contents exported as artifacts.
author_association: CONTRIBUTOR
user: login: mysterymath
created_at: 2025-04-15T20:45:51Z
body: Here's a link to a reproducer tar; I was able to verify that the issue reproduces locally using our toolchain from CI with the tarball.

https://storage.googleapis.com/fuchsia-artifacts/builds/8717549930672410881/build-debug/clang-crashreports/map_setting_store-0b592c.tar
author_association: CONTRIBUTOR
user: login: mizvekov
created_at: 2025-04-15T22:31:21Z
body: Yeah, I confirm this crashes clang, and reverting this commit does fix the crash.

For reference, this is the stack trace:
```
1.	<eof> parser at end of file
2.	../../src/lib/fxl/observer_list.h:193:54: instantiating function definition 'fxl::ObserverListBase<zxdb::SettingStoreObserver>::Iter<const fxl::ObserverListBase<zxdb::SettingStoreObserver>>::~Iter'
 #0 0x0000000104d687b8 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/Users/mizvekov/Repos/llvm/6/build/dbg/llvm/bin/clang-21+0x104a787b8)
 #1 0x0000000104d68cdc PrintStackTraceSignalHandler(void*) (/Users/mizvekov/Repos/llvm/6/build/dbg/llvm/bin/clang-21+0x104a78cdc)
 #2 0x0000000104d66b9c llvm::sys::RunSignalHandlers() (/Users/mizvekov/Repos/llvm/6/build/dbg/llvm/bin/clang-21+0x104a76b9c)
 #3 0x0000000104d695a8 SignalHandler(int, __siginfo*, void*) (/Users/mizvekov/Repos/llvm/6/build/dbg/llvm/bin/clang-21+0x104a795a8)
 #4 0x0000000184d77624 (/usr/lib/system/libsystem_platform.dylib+0x1804ab624)
 #5 0x0000000109f3dcac clang::Sema::InstantiateFunctionDefinition(clang::SourceLocation, clang::FunctionDecl*, bool, bool, bool)::$_4::operator()() const (/Users/mizvekov/Repos/llvm/6/build/dbg/llvm/bin/clang-21+0x109c4dcac)
 #6 0x0000000109f3dcac clang::Sema::InstantiateFunctionDefinition(clang::SourceLocation, clang::FunctionDecl*, bool, bool, bool)::$_4::operator()() const (/Users/mizvekov/Repos/llvm/6/build/dbg/llvm/bin/clang-21+0x109c4dcac)
 #7 0x0000000109f3d3d4 clang::Sema::InstantiateFunctionDefinition(clang::SourceLocation, clang::FunctionDecl*, bool, bool, bool) (/Users/mizvekov/Repos/llvm/6/build/dbg/llvm/bin/clang-21+0x109c4d3d4)
 #8 0x0000000109f407a0 clang::Sema::PerformPendingInstantiations(bool, bool) (/Users/mizvekov/Repos/llvm/6/build/dbg/llvm/bin/clang-21+0x109c507a0)
 #9 0x0000000108f23178 clang::Sema::ActOnEndOfTranslationUnitFragment(clang::Sema::TUFragmentKind) (/Users/mizvekov/Repos/llvm/6/build/dbg/llvm/bin/clang-21+0x108c33178)
#10 0x0000000108f23788 clang::Sema::ActOnEndOfTranslationUnit() (/Users/mizvekov/Repos/llvm/6/build/dbg/llvm/bin/clang-21+0x108c33788)
#11 0x0000000108e236e0 clang::Parser::ParseTopLevelDecl(clang::OpaquePtr<clang::DeclGroupRef>&, clang::Sema::ModuleImportState&) (/Users/mizvekov/Repos/llvm/6/build/dbg/llvm/bin/clang-21+0x108b336e0)
#12 0x0000000108cfb610 clang::ParseAST(clang::Sema&, bool, bool) (/Users/mizvekov/Repos/llvm/6/build/dbg/llvm/bin/clang-21+0x108a0b610)
#13 0x00000001066eeb34 clang::ASTFrontendAction::ExecuteAction() (/Users/mizvekov/Repos/llvm/6/build/dbg/llvm/bin/clang-21+0x1063feb34)
#14 0x0000000105bf3564 clang::CodeGenAction::ExecuteAction() (/Users/mizvekov/Repos/llvm/6/build/dbg/llvm/bin/clang-21+0x105903564)
#15 0x00000001066ee358 clang::FrontendAction::Execute() (/Users/mizvekov/Repos/llvm/6/build/dbg/llvm/bin/clang-21+0x1063fe358)
#16 0x00000001065cdb8c clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) (/Users/mizvekov/Repos/llvm/6/build/dbg/llvm/bin/clang-21+0x1062ddb8c)
#17 0x0000000106960d30 clang::ExecuteCompilerInvocation(clang::CompilerInstance*) (/Users/mizvekov/Repos/llvm/6/build/dbg/llvm/bin/clang-21+0x106670d30)
#18 0x0000000100301a40 cc1_main(llvm::ArrayRef<char const*>, char const*, void*) (/Users/mizvekov/Repos/llvm/6/build/dbg/llvm/bin/clang-21+0x100011a40)
#19 0x00000001002f36e0 ExecuteCC1Tool(llvm::SmallVectorImpl<char const*>&, llvm::ToolContext const&) (/Users/mizvekov/Repos/llvm/6/build/dbg/llvm/bin/clang-21+0x1000036e0)
#20 0x00000001002f2404 clang_main(int, char**, llvm::ToolContext const&) (/Users/mizvekov/Repos/llvm/6/build/dbg/llvm/bin/clang-21+0x100002404)
#21 0x000000010032bb48 main (/Users/mizvekov/Repos/llvm/6/build/dbg/llvm/bin/clang-21+0x10003bb48)
#22 0x000000018499eb4c
```

@zyn0217 FYI
author_association: CONTRIBUTOR
user: login: zyn0217
created_at: 2025-04-16T02:08:49Z
body: Thanks for the revert.

I added an assertion to `NameLocPointsToPattern` after line 5637 and it fired because `PatternName.getNamedTypeInfo` is null. This is strange because the instantiated function does have a TypeSourceInfo, which is guarded by line 5630.

I need a reduced version to figure out what happened.
author_association: CONTRIBUTOR