Skip to content

Commit b412ec5

Browse files
authored
[Clang][Sema] Revisit the fix for the lambda within a type alias template decl (llvm#89934)
In the last patch llvm#82310, 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 llvm#89853. Moreover, our approach for llvm#82104 was sadly wrong. We tried to teach `DeduceReturnType` to consider alias template arguments; however, 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 llvm#89853 Fixes llvm#102760 Fixes llvm#105885
1 parent 87157ab commit b412ec5

File tree

2 files changed

+110
-38
lines changed

2 files changed

+110
-38
lines changed

clang/lib/Sema/SemaTemplateInstantiate.cpp

Lines changed: 38 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "clang/AST/Expr.h"
2121
#include "clang/AST/ExprConcepts.h"
2222
#include "clang/AST/PrettyDeclStackTrace.h"
23+
#include "clang/AST/RecursiveASTVisitor.h"
2324
#include "clang/AST/Type.h"
2425
#include "clang/AST/TypeLoc.h"
2526
#include "clang/AST/TypeVisitor.h"
@@ -88,12 +89,19 @@ struct Response {
8889
// than lambda classes.
8990
const FunctionDecl *
9091
getPrimaryTemplateOfGenericLambda(const FunctionDecl *LambdaCallOperator) {
92+
if (!isLambdaCallOperator(LambdaCallOperator))
93+
return LambdaCallOperator;
9194
while (true) {
9295
if (auto *FTD = dyn_cast_if_present<FunctionTemplateDecl>(
9396
LambdaCallOperator->getDescribedTemplate());
9497
FTD && FTD->getInstantiatedFromMemberTemplate()) {
9598
LambdaCallOperator =
9699
FTD->getInstantiatedFromMemberTemplate()->getTemplatedDecl();
100+
} else if (LambdaCallOperator->getPrimaryTemplate()) {
101+
// Cases where the lambda operator is instantiated in
102+
// TemplateDeclInstantiator::VisitCXXMethodDecl.
103+
LambdaCallOperator =
104+
LambdaCallOperator->getPrimaryTemplate()->getTemplatedDecl();
97105
} else if (auto *Prev = cast<CXXMethodDecl>(LambdaCallOperator)
98106
->getInstantiatedFromMemberFunction())
99107
LambdaCallOperator = Prev;
@@ -139,22 +147,28 @@ getEnclosingTypeAliasTemplateDecl(Sema &SemaRef) {
139147
// Check if we are currently inside of a lambda expression that is
140148
// surrounded by a using alias declaration. e.g.
141149
// template <class> using type = decltype([](auto) { ^ }());
142-
// By checking if:
143-
// 1. The lambda expression and the using alias declaration share the
144-
// same declaration context.
145-
// 2. They have the same template depth.
146150
// We have to do so since a TypeAliasTemplateDecl (or a TypeAliasDecl) is never
147151
// a DeclContext, nor does it have an associated specialization Decl from which
148152
// we could collect these template arguments.
149153
bool isLambdaEnclosedByTypeAliasDecl(
150-
const FunctionDecl *PrimaryLambdaCallOperator,
154+
const FunctionDecl *LambdaCallOperator,
151155
const TypeAliasTemplateDecl *PrimaryTypeAliasDecl) {
152-
return cast<CXXRecordDecl>(PrimaryLambdaCallOperator->getDeclContext())
153-
->getTemplateDepth() ==
154-
PrimaryTypeAliasDecl->getTemplateDepth() &&
155-
getLambdaAwareParentOfDeclContext(
156-
const_cast<FunctionDecl *>(PrimaryLambdaCallOperator)) ==
157-
PrimaryTypeAliasDecl->getDeclContext();
156+
struct Visitor : RecursiveASTVisitor<Visitor> {
157+
Visitor(const FunctionDecl *CallOperator) : CallOperator(CallOperator) {}
158+
bool VisitLambdaExpr(const LambdaExpr *LE) {
159+
// Return true to bail out of the traversal, implying the Decl contains
160+
// the lambda.
161+
return getPrimaryTemplateOfGenericLambda(LE->getCallOperator()) !=
162+
CallOperator;
163+
}
164+
const FunctionDecl *CallOperator;
165+
};
166+
167+
QualType Underlying =
168+
PrimaryTypeAliasDecl->getTemplatedDecl()->getUnderlyingType();
169+
170+
return !Visitor(getPrimaryTemplateOfGenericLambda(LambdaCallOperator))
171+
.TraverseType(Underlying);
158172
}
159173

160174
// Add template arguments from a variable template instantiation.
@@ -293,23 +307,8 @@ Response HandleFunction(Sema &SemaRef, const FunctionDecl *Function,
293307

294308
// If this function is a generic lambda specialization, we are done.
295309
if (!ForConstraintInstantiation &&
296-
isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function)) {
297-
// TypeAliasTemplateDecls should be taken into account, e.g.
298-
// when we're deducing the return type of a lambda.
299-
//
300-
// template <class> int Value = 0;
301-
// template <class T>
302-
// using T = decltype([]<int U = 0>() { return Value<T>; }());
303-
//
304-
if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(SemaRef)) {
305-
if (isLambdaEnclosedByTypeAliasDecl(
306-
/*PrimaryLambdaCallOperator=*/getPrimaryTemplateOfGenericLambda(
307-
Function),
308-
/*PrimaryTypeAliasDecl=*/TypeAlias.PrimaryTypeAliasDecl))
309-
return Response::UseNextDecl(Function);
310-
}
310+
isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function))
311311
return Response::Done();
312-
}
313312

314313
} else if (Function->getDescribedFunctionTemplate()) {
315314
assert(
@@ -421,10 +420,9 @@ Response HandleRecordDecl(Sema &SemaRef, const CXXRecordDecl *Rec,
421420
// Retrieve the template arguments for a using alias declaration.
422421
// This is necessary for constraint checking, since we always keep
423422
// constraints relative to the primary template.
424-
if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(SemaRef)) {
425-
const FunctionDecl *PrimaryLambdaCallOperator =
426-
getPrimaryTemplateOfGenericLambda(Rec->getLambdaCallOperator());
427-
if (isLambdaEnclosedByTypeAliasDecl(PrimaryLambdaCallOperator,
423+
if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(SemaRef);
424+
ForConstraintInstantiation && TypeAlias) {
425+
if (isLambdaEnclosedByTypeAliasDecl(Rec->getLambdaCallOperator(),
428426
TypeAlias.PrimaryTypeAliasDecl)) {
429427
Result.addOuterTemplateArguments(TypeAlias.Template,
430428
TypeAlias.AssociatedTemplateArguments,
@@ -1647,12 +1645,17 @@ namespace {
16471645

16481646
CXXRecordDecl::LambdaDependencyKind
16491647
ComputeLambdaDependency(LambdaScopeInfo *LSI) {
1650-
auto &CCS = SemaRef.CodeSynthesisContexts.back();
1651-
if (CCS.Kind ==
1652-
Sema::CodeSynthesisContext::TypeAliasTemplateInstantiation) {
1653-
unsigned TypeAliasDeclDepth = CCS.Entity->getTemplateDepth();
1648+
if (auto TypeAlias =
1649+
TemplateInstArgsHelpers::getEnclosingTypeAliasTemplateDecl(
1650+
getSema());
1651+
TypeAlias && TemplateInstArgsHelpers::isLambdaEnclosedByTypeAliasDecl(
1652+
LSI->CallOperator, TypeAlias.PrimaryTypeAliasDecl)) {
1653+
unsigned TypeAliasDeclDepth = TypeAlias.Template->getTemplateDepth();
16541654
if (TypeAliasDeclDepth >= TemplateArgs.getNumSubstitutedLevels())
16551655
return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent;
1656+
for (const TemplateArgument &TA : TypeAlias.AssociatedTemplateArguments)
1657+
if (TA.isDependent())
1658+
return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent;
16561659
}
16571660
return inherited::ComputeLambdaDependency(LSI);
16581661
}

clang/test/SemaTemplate/alias-template-with-lambdas.cpp

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,15 +91,84 @@ void bar() {
9191

9292
namespace GH82104 {
9393

94-
template <typename, typename...> int Zero = 0;
94+
template <typename, typename... D> constexpr int Value = sizeof...(D);
9595

96-
template <typename T, typename...U>
97-
using T14 = decltype([]<int V = 0>() { return Zero<T, U...>; }());
96+
template <typename T, typename... U>
97+
using T14 = decltype([]<int V = 0>(auto Param) {
98+
return Value<T, U...> + V + (int)sizeof(Param);
99+
}("hello"));
98100

99101
template <typename T> using T15 = T14<T, T>;
100102

101103
static_assert(__is_same(T15<char>, int));
102104

105+
// FIXME: This still crashes because we can't extract template arguments T and U
106+
// outside of the instantiation context of T16.
107+
#if 0
108+
template <typename T, typename... U>
109+
using T16 = decltype([](auto Param) requires (sizeof(Param) != 1 && sizeof...(U) > 0) {
110+
return Value<T, U...> + sizeof(Param);
111+
});
112+
static_assert(T16<int, char, float>()(42) == 2 + sizeof(42));
113+
#endif
103114
} // namespace GH82104
104115

116+
namespace GH89853 {
117+
118+
template <typename = void>
119+
static constexpr auto innocuous = []<int m> { return m; };
120+
121+
template <auto Pred = innocuous<>>
122+
using broken = decltype(Pred.template operator()<42>());
123+
124+
broken<> *boom;
125+
126+
template <auto Pred =
127+
[]<char c> {
128+
(void)static_cast<char>(c);
129+
}>
130+
using broken2 = decltype(Pred.template operator()<42>());
131+
132+
broken2<> *boom2;
133+
134+
template <auto Pred = []<char m> { return m; }>
135+
using broken3 = decltype(Pred.template operator()<42>());
136+
137+
broken3<> *boom3;
138+
139+
static constexpr auto non_default = []<char c>(True auto) {
140+
(void) static_cast<char>(c);
141+
};
142+
143+
template<True auto Pred>
144+
using broken4 = decltype(Pred.template operator()<42>(Pred));
145+
146+
broken4<non_default>* boom4;
147+
148+
} // namespace GH89853
149+
150+
namespace GH105885 {
151+
152+
template<int>
153+
using test = decltype([](auto...) {
154+
}());
155+
156+
static_assert(__is_same(test<0>, void));
157+
158+
} // namespace GH105885
159+
160+
namespace GH102760 {
161+
162+
auto make_tuple = []< class Tag, class... Captures>(Tag, Captures...) {
163+
return []< class _Fun >( _Fun) -> void requires requires { 0; }
164+
{};
165+
};
166+
167+
template < class, class... _As >
168+
using Result = decltype(make_tuple(0)(_As{}...));
169+
170+
using T = Result<int, int>;
171+
172+
} // namespace GH102760
173+
105174
} // namespace lambda_calls

0 commit comments

Comments
 (0)