[clang] [Clang][Sema] Revisit the fix for the lambda within a type alias template decl (PR #89934)
Younan Zhang via cfe-commits
cfe-commits at lists.llvm.org
Wed Apr 24 19:41:47 PDT 2024
https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/89934
>From 92f8cfb255a549769c39327239c69edd6b2e947e Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Wed, 24 Apr 2024 20:54:58 +0800
Subject: [PATCH 1/3] [Clang][Sema] Revisit the lambda within a type alias
template decl
---
clang/lib/Sema/SemaTemplateInstantiate.cpp | 72 ++++++++++---------
.../alias-template-with-lambdas.cpp | 27 ++++++-
2 files changed, 64 insertions(+), 35 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 98d5c7cb3a8a80..572d8e4c820ee9 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -20,6 +20,7 @@
#include "clang/AST/Expr.h"
#include "clang/AST/ExprConcepts.h"
#include "clang/AST/PrettyDeclStackTrace.h"
+#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeLoc.h"
#include "clang/AST/TypeVisitor.h"
@@ -87,12 +88,19 @@ struct Response {
// than lambda classes.
const FunctionDecl *
getPrimaryTemplateOfGenericLambda(const FunctionDecl *LambdaCallOperator) {
+ if (!isLambdaCallOperator(LambdaCallOperator))
+ return LambdaCallOperator;
while (true) {
if (auto *FTD = dyn_cast_if_present<FunctionTemplateDecl>(
LambdaCallOperator->getDescribedTemplate());
FTD && FTD->getInstantiatedFromMemberTemplate()) {
LambdaCallOperator =
FTD->getInstantiatedFromMemberTemplate()->getTemplatedDecl();
+ } else if (LambdaCallOperator->getPrimaryTemplate()) {
+ // Cases where the lambda operator is instantiated in
+ // TemplateDeclInstantiator::VisitCXXMethodDecl.
+ LambdaCallOperator =
+ LambdaCallOperator->getPrimaryTemplate()->getTemplatedDecl();
} else if (auto *Prev = cast<CXXMethodDecl>(LambdaCallOperator)
->getInstantiatedFromMemberFunction())
LambdaCallOperator = Prev;
@@ -138,22 +146,30 @@ getEnclosingTypeAliasTemplateDecl(Sema &SemaRef) {
// Check if we are currently inside of a lambda expression that is
// surrounded by a using alias declaration. e.g.
// template <class> using type = decltype([](auto) { ^ }());
-// By checking if:
-// 1. The lambda expression and the using alias declaration share the
-// same declaration context.
-// 2. They have the same template depth.
// We have to do so since a TypeAliasTemplateDecl (or a TypeAliasDecl) is never
// a DeclContext, nor does it have an associated specialization Decl from which
// we could collect these template arguments.
bool isLambdaEnclosedByTypeAliasDecl(
- const FunctionDecl *PrimaryLambdaCallOperator,
+ const FunctionDecl *LambdaCallOperator,
const TypeAliasTemplateDecl *PrimaryTypeAliasDecl) {
- return cast<CXXRecordDecl>(PrimaryLambdaCallOperator->getDeclContext())
- ->getTemplateDepth() ==
- PrimaryTypeAliasDecl->getTemplateDepth() &&
- getLambdaAwareParentOfDeclContext(
- const_cast<FunctionDecl *>(PrimaryLambdaCallOperator)) ==
- PrimaryTypeAliasDecl->getDeclContext();
+ struct Visitor : RecursiveASTVisitor<Visitor> {
+ Visitor(const FunctionDecl *CallOperator) : CallOperator(CallOperator) {}
+ bool VisitLambdaExpr(const LambdaExpr *LE) {
+ // Return true to bail out of the traversal, implying the Decl contains
+ // the lambda.
+ return getPrimaryTemplateOfGenericLambda(LE->getCallOperator()) !=
+ CallOperator;
+ }
+ const FunctionDecl *CallOperator;
+ };
+
+ QualType Underlying =
+ PrimaryTypeAliasDecl->getTemplatedDecl()->getUnderlyingType();
+
+ if (auto *DT = dyn_cast<DecltypeType>(Underlying.getTypePtr()))
+ return !Visitor(getPrimaryTemplateOfGenericLambda(LambdaCallOperator))
+ .TraverseStmt(DT->getUnderlyingExpr());
+ return false;
}
// Add template arguments from a variable template instantiation.
@@ -283,23 +299,8 @@ Response HandleFunction(Sema &SemaRef, const FunctionDecl *Function,
// If this function is a generic lambda specialization, we are done.
if (!ForConstraintInstantiation &&
- isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function)) {
- // TypeAliasTemplateDecls should be taken into account, e.g.
- // when we're deducing the return type of a lambda.
- //
- // template <class> int Value = 0;
- // template <class T>
- // using T = decltype([]<int U = 0>() { return Value<T>; }());
- //
- if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(SemaRef)) {
- if (isLambdaEnclosedByTypeAliasDecl(
- /*PrimaryLambdaCallOperator=*/getPrimaryTemplateOfGenericLambda(
- Function),
- /*PrimaryTypeAliasDecl=*/TypeAlias.PrimaryTypeAliasDecl))
- return Response::UseNextDecl(Function);
- }
+ isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function))
return Response::Done();
- }
} else if (Function->getDescribedFunctionTemplate()) {
assert(
@@ -412,9 +413,7 @@ Response HandleRecordDecl(Sema &SemaRef, const CXXRecordDecl *Rec,
// This is necessary for constraint checking, since we always keep
// constraints relative to the primary template.
if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(SemaRef)) {
- const FunctionDecl *PrimaryLambdaCallOperator =
- getPrimaryTemplateOfGenericLambda(Rec->getLambdaCallOperator());
- if (isLambdaEnclosedByTypeAliasDecl(PrimaryLambdaCallOperator,
+ if (isLambdaEnclosedByTypeAliasDecl(Rec->getLambdaCallOperator(),
TypeAlias.PrimaryTypeAliasDecl)) {
Result.addOuterTemplateArguments(TypeAlias.Template,
TypeAlias.AssociatedTemplateArguments,
@@ -1670,12 +1669,17 @@ namespace {
CXXRecordDecl::LambdaDependencyKind
ComputeLambdaDependency(LambdaScopeInfo *LSI) {
- auto &CCS = SemaRef.CodeSynthesisContexts.back();
- if (CCS.Kind ==
- Sema::CodeSynthesisContext::TypeAliasTemplateInstantiation) {
- unsigned TypeAliasDeclDepth = CCS.Entity->getTemplateDepth();
+ if (auto TypeAlias =
+ TemplateInstArgsHelpers::getEnclosingTypeAliasTemplateDecl(
+ getSema());
+ TypeAlias && TemplateInstArgsHelpers::isLambdaEnclosedByTypeAliasDecl(
+ LSI->CallOperator, TypeAlias.PrimaryTypeAliasDecl)) {
+ unsigned TypeAliasDeclDepth = TypeAlias.Template->getTemplateDepth();
if (TypeAliasDeclDepth >= TemplateArgs.getNumSubstitutedLevels())
return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent;
+ for (const TemplateArgument &TA : TypeAlias.AssociatedTemplateArguments)
+ if (TA.isDependent())
+ return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent;
}
return inherited::ComputeLambdaDependency(LSI);
}
diff --git a/clang/test/SemaTemplate/alias-template-with-lambdas.cpp b/clang/test/SemaTemplate/alias-template-with-lambdas.cpp
index ff94031e4d86f1..bedbd4a6c07b4f 100644
--- a/clang/test/SemaTemplate/alias-template-with-lambdas.cpp
+++ b/clang/test/SemaTemplate/alias-template-with-lambdas.cpp
@@ -94,7 +94,9 @@ namespace GH82104 {
template <typename, typename...> int Zero = 0;
template <typename T, typename...U>
-using T14 = decltype([]<int V = 0>() { return Zero<T, U...>; }());
+using T14 = decltype([]<int V = 0>(auto Param) {
+ return Zero<T, U...> + V + (int)sizeof(Param);
+}("hello"));
template <typename T> using T15 = T14<T, T>;
@@ -102,4 +104,27 @@ static_assert(__is_same(T15<char>, int));
} // namespace GH82104
+namespace GH89853 {
+template <typename = void>
+static constexpr auto innocuous = []<int m> { return m; };
+
+template <auto Pred = innocuous<>>
+using broken = decltype(Pred.template operator()<42>());
+
+broken<> *boom;
+
+template <auto Pred =
+ []<const char c> {
+ (void)static_cast<char>(c);
+ }>
+using broken2 = decltype(Pred.template operator()<42>());
+
+broken2<> *boom2;
+
+template <auto Pred = []<const char m> { return m; }>
+using broken3 = decltype(Pred.template operator()<42>());
+
+broken3<> *boom3;
+}
+
} // namespace lambda_calls
>From 0598618aa06b5531a5f92ea07104ef1453fb07b5 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Wed, 24 Apr 2024 23:21:37 +0800
Subject: [PATCH 2/3] Just use TraverseType
---
clang/lib/Sema/SemaTemplateInstantiate.cpp | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 572d8e4c820ee9..ec7f5af1dc01c3 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -166,10 +166,8 @@ bool isLambdaEnclosedByTypeAliasDecl(
QualType Underlying =
PrimaryTypeAliasDecl->getTemplatedDecl()->getUnderlyingType();
- if (auto *DT = dyn_cast<DecltypeType>(Underlying.getTypePtr()))
- return !Visitor(getPrimaryTemplateOfGenericLambda(LambdaCallOperator))
- .TraverseStmt(DT->getUnderlyingExpr());
- return false;
+ return !Visitor(getPrimaryTemplateOfGenericLambda(LambdaCallOperator))
+ .TraverseType(Underlying);
}
// Add template arguments from a variable template instantiation.
>From c2342fedca28d21ebbc4b0884d7d6042c1e4ee88 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Thu, 25 Apr 2024 10:40:51 +0800
Subject: [PATCH 3/3] Add more tests
---
clang/lib/Sema/SemaTemplateInstantiate.cpp | 3 +-
.../alias-template-with-lambdas.cpp | 30 +++++++++++++++----
2 files changed, 27 insertions(+), 6 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index ec7f5af1dc01c3..8b4d841fab46cf 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -410,7 +410,8 @@ Response HandleRecordDecl(Sema &SemaRef, const CXXRecordDecl *Rec,
// Retrieve the template arguments for a using alias declaration.
// This is necessary for constraint checking, since we always keep
// constraints relative to the primary template.
- if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(SemaRef)) {
+ if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(SemaRef);
+ ForConstraintInstantiation && TypeAlias) {
if (isLambdaEnclosedByTypeAliasDecl(Rec->getLambdaCallOperator(),
TypeAlias.PrimaryTypeAliasDecl)) {
Result.addOuterTemplateArguments(TypeAlias.Template,
diff --git a/clang/test/SemaTemplate/alias-template-with-lambdas.cpp b/clang/test/SemaTemplate/alias-template-with-lambdas.cpp
index bedbd4a6c07b4f..032ad2cf6a5ad2 100644
--- a/clang/test/SemaTemplate/alias-template-with-lambdas.cpp
+++ b/clang/test/SemaTemplate/alias-template-with-lambdas.cpp
@@ -91,20 +91,30 @@ void bar() {
namespace GH82104 {
-template <typename, typename...> int Zero = 0;
+template <typename, typename... D> int Value = sizeof...(D);
-template <typename T, typename...U>
+template <typename T, typename... U>
using T14 = decltype([]<int V = 0>(auto Param) {
- return Zero<T, U...> + V + (int)sizeof(Param);
+ return Value<T, U...> + V + (int)sizeof(Param);
}("hello"));
template <typename T> using T15 = T14<T, T>;
static_assert(__is_same(T15<char>, int));
+// FIXME: This still crashes because we can't extract template arguments T and U
+// outside of the instantiation context of T16.
+#if 0
+template <typename T, typename... U>
+using T16 = decltype([](auto Param) requires (sizeof(Param) != 1 && sizeof...(U) > 0) {
+ return Value<T, U...> + sizeof(Param);
+});
+static_assert(T16<int, char, float>()(42) == 2 + sizeof(42));
+#endif
} // namespace GH82104
namespace GH89853 {
+
template <typename = void>
static constexpr auto innocuous = []<int m> { return m; };
@@ -114,17 +124,27 @@ using broken = decltype(Pred.template operator()<42>());
broken<> *boom;
template <auto Pred =
- []<const char c> {
+ []<char c> {
(void)static_cast<char>(c);
}>
using broken2 = decltype(Pred.template operator()<42>());
broken2<> *boom2;
-template <auto Pred = []<const char m> { return m; }>
+template <auto Pred = []<char m> { return m; }>
using broken3 = decltype(Pred.template operator()<42>());
broken3<> *boom3;
+
+static constexpr auto non_default = []<char c>(True auto) {
+ (void) static_cast<char>(c);
+};
+
+template<True auto Pred>
+using broken4 = decltype(Pred.template operator()<42>(Pred));
+
+broken4<non_default>* boom4;
+
}
} // namespace lambda_calls
More information about the cfe-commits
mailing list