Skip to content

[Clang] Don't assert on substituted-but-yet-expanded packs for nested lambdas #112896

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Oct 22, 2024

Conversation

zyn0217
Copy link
Contributor

@zyn0217 zyn0217 commented Oct 18, 2024

Nested lambdas could refer to outer packs that would be expanded by a larger CXXFoldExpr, in which case that reference could happen to be a full expression containing intermediate types/expressions, e.g. SubstTemplateTypeParmPackType/FunctionParmPackExpr. They are designated as "UnexpandedPack" dependencies but don't introduce new packs anyway.

This also handles a missed case for VarDecls, where the flag of ContainsUnexpandedPack was not propagated up to the surrounding lambda.

Fixes #112352

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Oct 18, 2024
@llvmbot
Copy link
Member

llvmbot commented Oct 18, 2024

@llvm/pr-subscribers-clang

Author: Younan Zhang (zyn0217)

Changes

Nested lambdas could refer to outer packs that would be expanded by a larger CXXFoldExpr, in which case that reference could happen to be a full expression containing intermediate types/expressions, e.g. SubstTemplateTypeParmPackType/FunctionParmPackExpr. They are designated as "UnexpandedPack" dependencies but don't introduce new packs anyway.

Fixes #112352


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

3 Files Affected:

  • (modified) clang/docs/ReleaseNotes.rst (+1-1)
  • (modified) clang/lib/Sema/SemaTemplateVariadic.cpp (+34-15)
  • (modified) clang/test/SemaCXX/lambda-pack-expansion.cpp (+40)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index b430b2b0ee3187..d071c480f36dc7 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -486,7 +486,7 @@ Bug Fixes to C++ Support
 - Clang no longer tries to capture non-odr used default arguments of template parameters of generic lambdas (#GH107048)
 - Fixed a bug where defaulted comparison operators would remove ``const`` from base classes. (#GH102588)
 - Fix a crash when using ``source_location`` in the trailing return type of a lambda expression. (#GH67134)
-- A follow-up fix was added for (#GH61460), as the previous fix was not entirely correct. (#GH86361)
+- A follow-up fix was added for (#GH61460), as the previous fix was not entirely correct. (#GH86361), (#GH112352)
 - Fixed a crash in the typo correction of an invalid CTAD guide. (#GH107887)
 - Fixed a crash when clang tries to subtitute parameter pack while retaining the parameter
   pack. (#GH63819), (#GH107560)
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 19bd4547665835..151b328872bd75 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -41,7 +41,7 @@ namespace {
     unsigned DepthLimit = (unsigned)-1;
 
 #ifndef NDEBUG
-    bool ContainsFunctionParmPackExpr = false;
+    bool ContainsIntermediatePacks = false;
 #endif
 
     void addUnexpanded(NamedDecl *ND, SourceLocation Loc = SourceLocation()) {
@@ -114,6 +114,11 @@ namespace {
           addUnexpanded(TTP);
       }
 
+#ifndef NDEBUG
+      ContainsIntermediatePacks |=
+          (bool)Template.getAsSubstTemplateTemplateParmPack();
+#endif
+
       return inherited::TraverseTemplateName(Template);
     }
 
@@ -297,13 +302,28 @@ namespace {
 
 #ifndef NDEBUG
     bool TraverseFunctionParmPackExpr(FunctionParmPackExpr *) {
-      ContainsFunctionParmPackExpr = true;
+      ContainsIntermediatePacks = true;
+      return true;
+    }
+
+    bool TraverseSubstNonTypeTemplateParmPackExpr(
+        SubstNonTypeTemplateParmPackExpr *) {
+      ContainsIntermediatePacks = true;
+      return true;
+    }
+
+    bool VisitSubstTemplateTypeParmPackType(SubstTemplateTypeParmPackType *) {
+      ContainsIntermediatePacks = true;
       return true;
     }
 
-    bool containsFunctionParmPackExpr() const {
-      return ContainsFunctionParmPackExpr;
+    bool
+    VisitSubstTemplateTypeParmPackTypeLoc(SubstTemplateTypeParmPackTypeLoc) {
+      ContainsIntermediatePacks = true;
+      return true;
     }
+
+    bool containsIntermediatePacks() const { return ContainsIntermediatePacks; }
 #endif
   };
 }
@@ -439,21 +459,20 @@ bool Sema::DiagnoseUnexpandedParameterPack(Expr *E,
   if (!E->containsUnexpandedParameterPack())
     return false;
 
-  // FunctionParmPackExprs are special:
-  //
-  // 1) they're used to model DeclRefExprs to packs that have been expanded but
-  // had that expansion held off in the process of transformation.
-  //
-  // 2) they always have the unexpanded dependencies but don't introduce new
-  // unexpanded packs.
-  //
-  // We might encounter a FunctionParmPackExpr being a full expression, which a
-  // larger CXXFoldExpr would expand.
   SmallVector<UnexpandedParameterPack, 2> Unexpanded;
   CollectUnexpandedParameterPacksVisitor Visitor(Unexpanded);
   Visitor.TraverseStmt(E);
-  assert((!Unexpanded.empty() || Visitor.containsFunctionParmPackExpr()) &&
+#ifndef NDEBUG
+  // The expression might contain a type/subexpression that has been substituted
+  // but has the expansion held off, e.g. a FunctionParmPackExpr which a larger
+  // CXXFoldExpr would expand. It's only possible when expanding a lambda as a
+  // pattern of a fold expression, so don't fire on an empty result in that
+  // case.
+  bool LambdaReferencingOuterPacks =
+      getEnclosingLambdaOrBlock() && Visitor.containsIntermediatePacks();
+  assert((!Unexpanded.empty() || LambdaReferencingOuterPacks) &&
          "Unable to find unexpanded parameter packs");
+#endif
   return DiagnoseUnexpandedParameterPacks(E->getBeginLoc(), UPPC, Unexpanded);
 }
 
diff --git a/clang/test/SemaCXX/lambda-pack-expansion.cpp b/clang/test/SemaCXX/lambda-pack-expansion.cpp
index 0e60ecd8756600..7f7dd3bb095d57 100644
--- a/clang/test/SemaCXX/lambda-pack-expansion.cpp
+++ b/clang/test/SemaCXX/lambda-pack-expansion.cpp
@@ -94,3 +94,43 @@ template <typename... Ts> void g2(Ts... p1s) {
 void f1() { g(); }
 
 } // namespace GH61460
+
+namespace GH112352 {
+
+template <class>
+constexpr bool foo = false;
+
+template <int>
+constexpr bool bar = false;
+
+template <template<class> class>
+constexpr bool baz = false;
+
+struct S {
+  template <typename... Types, int... Values> void foldExpr1() {
+    (void)[]<int... Is> {
+      ([] {
+        Is;
+        foo<Types>;
+        bar<Values>;
+      } &&
+       ...);
+    };
+  }
+
+  template <template<class> class... TTPs> void foldExpr2() {
+    (void)[]<int... Is> {
+      ([] {
+        Is;
+        baz<TTPs>;
+      } && ...);
+    };
+  }
+};
+
+void use() {
+  S().foldExpr1();
+  S().foldExpr2();
+}
+
+} // namespace GH112352

Comment on lines +8388 to +8400
if (LSI) {
if (auto *TD = dyn_cast<TypeDecl>(Transformed))
LSI->ContainsUnexpandedParameterPack |=
getSema()
.getASTContext()
.getTypeDeclType(TD)
.getCanonicalType()
->containsUnexpandedParameterPack();

if (auto *VD = dyn_cast<VarDecl>(Transformed))
LSI->ContainsUnexpandedParameterPack |=
VD->getType()->containsUnexpandedParameterPack();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The more I see that sort of things, the more @zygoloid 's idea of tracking unexpanded packs lexically seems compelling.

we probably should have have a separate function for "does this declaration contains an unexpanded pack"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if I understand "tracking unexpanded packs lexically". Do you have a link to that context? It sounds like we need a new bit to track that in Decls, despite that flag being accessible through their QualTypes' dependencies for the status quo. In the latter regard, it looks like we just need a shortcut method for it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha, this is mostly a "gosh, this is getting unwieldy, i wonder if there is a better way", rather than a request for a change. I was referring to this #9395 (comment)

Copy link
Contributor

@cor3ntin cor3ntin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@zyn0217 zyn0217 merged commit d15559d into llvm:main Oct 22, 2024
7 of 9 checks passed
@zyn0217 zyn0217 deleted the issue-112352 branch October 22, 2024 08:51
aarongable pushed a commit to chromium/chromium that referenced this pull request Oct 22, 2024
This reverts commit 80dcfe1.

Reason for revert:
The Clang issue is reported to have been fixed in
llvm/llvm-project#112896

Original change's description:
> Don't compile gvariant_ref_unittest.cc with ToT clang
>
> gvariant_ref_unittest.cc exposes a bug in clang built with asserts (llvm/llvm-project#112352) which may not be fixed soon. This breaks all of the clang ToT bots which build and use clang with asserts. Disable compiling this file under llvm_force_head_revision, which our clang ToT bots set.
>
> Bug: 373478542
> Change-Id: Id56cbc66c455a9480c5ceafe4fb9f0fc6e1bba02
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5941915
> Commit-Queue: Arthur Eubanks <[email protected]>
> Commit-Queue: Erik Jensen <[email protected]>
> Auto-Submit: Arthur Eubanks <[email protected]>
> Reviewed-by: Erik Jensen <[email protected]>
> Cr-Commit-Position: refs/heads/main@{#1370344}

Bug: 373478542
Change-Id: I270b0e13128c528bb5e1fe883c561d18a002551f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5954005
Reviewed-by: Erik Jensen <[email protected]>
Auto-Submit: Hans Wennborg <[email protected]>
Commit-Queue: Erik Jensen <[email protected]>
Reviewed-by: Arthur Eubanks <[email protected]>
Bot-Commit: Rubber Stamper <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1372195}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Clang assertion failure: "Unable to find unexpanded parameter packs"
3 participants