Skip to content

Commit 15cb29a

Browse files
zyn0217llvmbot
authored andcommitted
[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 (cherry picked from commit b412ec5)
1 parent 5f744ee commit 15cb29a

File tree

2 files changed

+110
-38
lines changed

2 files changed

+110
-38
lines changed

clang/lib/Sema/SemaTemplateInstantiate.cpp

+38-35
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"
@@ -87,12 +88,19 @@ struct Response {
8788
// than lambda classes.
8889
const FunctionDecl *
8990
getPrimaryTemplateOfGenericLambda(const FunctionDecl *LambdaCallOperator) {
91+
if (!isLambdaCallOperator(LambdaCallOperator))
92+
return LambdaCallOperator;
9093
while (true) {
9194
if (auto *FTD = dyn_cast_if_present<FunctionTemplateDecl>(
9295
LambdaCallOperator->getDescribedTemplate());
9396
FTD && FTD->getInstantiatedFromMemberTemplate()) {
9497
LambdaCallOperator =
9598
FTD->getInstantiatedFromMemberTemplate()->getTemplatedDecl();
99+
} else if (LambdaCallOperator->getPrimaryTemplate()) {
100+
// Cases where the lambda operator is instantiated in
101+
// TemplateDeclInstantiator::VisitCXXMethodDecl.
102+
LambdaCallOperator =
103+
LambdaCallOperator->getPrimaryTemplate()->getTemplatedDecl();
96104
} else if (auto *Prev = cast<CXXMethodDecl>(LambdaCallOperator)
97105
->getInstantiatedFromMemberFunction())
98106
LambdaCallOperator = Prev;
@@ -138,22 +146,28 @@ getEnclosingTypeAliasTemplateDecl(Sema &SemaRef) {
138146
// Check if we are currently inside of a lambda expression that is
139147
// surrounded by a using alias declaration. e.g.
140148
// template <class> using type = decltype([](auto) { ^ }());
141-
// By checking if:
142-
// 1. The lambda expression and the using alias declaration share the
143-
// same declaration context.
144-
// 2. They have the same template depth.
145149
// We have to do so since a TypeAliasTemplateDecl (or a TypeAliasDecl) is never
146150
// a DeclContext, nor does it have an associated specialization Decl from which
147151
// we could collect these template arguments.
148152
bool isLambdaEnclosedByTypeAliasDecl(
149-
const FunctionDecl *PrimaryLambdaCallOperator,
153+
const FunctionDecl *LambdaCallOperator,
150154
const TypeAliasTemplateDecl *PrimaryTypeAliasDecl) {
151-
return cast<CXXRecordDecl>(PrimaryLambdaCallOperator->getDeclContext())
152-
->getTemplateDepth() ==
153-
PrimaryTypeAliasDecl->getTemplateDepth() &&
154-
getLambdaAwareParentOfDeclContext(
155-
const_cast<FunctionDecl *>(PrimaryLambdaCallOperator)) ==
156-
PrimaryTypeAliasDecl->getDeclContext();
155+
struct Visitor : RecursiveASTVisitor<Visitor> {
156+
Visitor(const FunctionDecl *CallOperator) : CallOperator(CallOperator) {}
157+
bool VisitLambdaExpr(const LambdaExpr *LE) {
158+
// Return true to bail out of the traversal, implying the Decl contains
159+
// the lambda.
160+
return getPrimaryTemplateOfGenericLambda(LE->getCallOperator()) !=
161+
CallOperator;
162+
}
163+
const FunctionDecl *CallOperator;
164+
};
165+
166+
QualType Underlying =
167+
PrimaryTypeAliasDecl->getTemplatedDecl()->getUnderlyingType();
168+
169+
return !Visitor(getPrimaryTemplateOfGenericLambda(LambdaCallOperator))
170+
.TraverseType(Underlying);
157171
}
158172

159173
// Add template arguments from a variable template instantiation.
@@ -290,23 +304,8 @@ Response HandleFunction(Sema &SemaRef, const FunctionDecl *Function,
290304

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

311310
} else if (Function->getDescribedFunctionTemplate()) {
312311
assert(
@@ -418,10 +417,9 @@ Response HandleRecordDecl(Sema &SemaRef, const CXXRecordDecl *Rec,
418417
// Retrieve the template arguments for a using alias declaration.
419418
// This is necessary for constraint checking, since we always keep
420419
// constraints relative to the primary template.
421-
if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(SemaRef)) {
422-
const FunctionDecl *PrimaryLambdaCallOperator =
423-
getPrimaryTemplateOfGenericLambda(Rec->getLambdaCallOperator());
424-
if (isLambdaEnclosedByTypeAliasDecl(PrimaryLambdaCallOperator,
420+
if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(SemaRef);
421+
ForConstraintInstantiation && TypeAlias) {
422+
if (isLambdaEnclosedByTypeAliasDecl(Rec->getLambdaCallOperator(),
425423
TypeAlias.PrimaryTypeAliasDecl)) {
426424
Result.addOuterTemplateArguments(TypeAlias.Template,
427425
TypeAlias.AssociatedTemplateArguments,
@@ -1642,12 +1640,17 @@ namespace {
16421640

16431641
CXXRecordDecl::LambdaDependencyKind
16441642
ComputeLambdaDependency(LambdaScopeInfo *LSI) {
1645-
auto &CCS = SemaRef.CodeSynthesisContexts.back();
1646-
if (CCS.Kind ==
1647-
Sema::CodeSynthesisContext::TypeAliasTemplateInstantiation) {
1648-
unsigned TypeAliasDeclDepth = CCS.Entity->getTemplateDepth();
1643+
if (auto TypeAlias =
1644+
TemplateInstArgsHelpers::getEnclosingTypeAliasTemplateDecl(
1645+
getSema());
1646+
TypeAlias && TemplateInstArgsHelpers::isLambdaEnclosedByTypeAliasDecl(
1647+
LSI->CallOperator, TypeAlias.PrimaryTypeAliasDecl)) {
1648+
unsigned TypeAliasDeclDepth = TypeAlias.Template->getTemplateDepth();
16491649
if (TypeAliasDeclDepth >= TemplateArgs.getNumSubstitutedLevels())
16501650
return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent;
1651+
for (const TemplateArgument &TA : TypeAlias.AssociatedTemplateArguments)
1652+
if (TA.isDependent())
1653+
return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent;
16511654
}
16521655
return inherited::ComputeLambdaDependency(LSI);
16531656
}

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

+72-3
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)