[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 07:25:55 PDT 2024
https://github.com/zyn0217 created https://github.com/llvm/llvm-project/pull/89934
Previously commited as https://github.com/llvm/llvm-project/issues/82310.
In the last patch, we used template depths to tell if such alias decls contain lambdas, which is wrong because the lambda can also appear as a part of the default argument, and that would make `getTemplateInstantiationArgs` provide extra template arguments in undesired contexts. This leads to issue #89853.
Moreover, our approach for https://github.com/llvm/llvm-project/issues/82104 was sadly wrong. We tried to teach DeduceReturnType to consider alias template arguments. Still, giving these arguments in the context where they should have been substituted in a TransformCallExpr call is never correct.
This patch addresses such problems by using a `RecursiveASTVisitor` to check if the lambda is contained by an alias `Decl`, as well as twiddling the lambda dependencies - we should also build a dependent lambda expression if the surrounding alias template arguments were dependent.
Fixes https://github.com/llvm/llvm-project/issues/89853
>From 670c4a42234da65f082e9828f4c8125d3527093c 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] [Clang][Sema] Rehash the lambda within a type alias template
decl
Fixes https://github.com/llvm/llvm-project/issues/89853
---
clang/lib/Sema/SemaTemplateInstantiate.cpp | 66 +++++++++----------
.../alias-template-with-lambdas.cpp | 25 ++++++-
2 files changed, 56 insertions(+), 35 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 98d5c7cb3a8a80..c9aa0525d3875c 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,17 @@ 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()) {
+ LambdaCallOperator =
+ LambdaCallOperator->getPrimaryTemplate()->getTemplatedDecl();
} else if (auto *Prev = cast<CXXMethodDecl>(LambdaCallOperator)
->getInstantiatedFromMemberFunction())
LambdaCallOperator = Prev;
@@ -138,22 +144,26 @@ 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) {
+ auto *G = getPrimaryTemplateOfGenericLambda(LE->getCallOperator());
+ return G != 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 +293,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 +407,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 +1663,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 (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..b7a2f74e0530ef 100644
--- a/clang/test/SemaTemplate/alias-template-with-lambdas.cpp
+++ b/clang/test/SemaTemplate/alias-template-with-lambdas.cpp
@@ -94,7 +94,7 @@ 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 +102,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
More information about the cfe-commits
mailing list