Skip to content

Commit d3a76a1

Browse files
committed
Slightly refactor & Fix GH82104
1 parent 62a00af commit d3a76a1

File tree

3 files changed

+155
-77
lines changed

3 files changed

+155
-77
lines changed

clang/lib/Sema/SemaTemplateInstantiate.cpp

Lines changed: 119 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,81 @@ struct Response {
8080
return R;
8181
}
8282
};
83+
84+
// Retrieve the primary template for a lambda call operator. It's
85+
// unfortunate that we only have the mappings of call operators rather
86+
// than lambda classes.
87+
const FunctionDecl *
88+
getPrimaryTemplateOfGenericLambda(const FunctionDecl *LambdaCallOperator) {
89+
while (true) {
90+
if (auto *FTD = dyn_cast_if_present<FunctionTemplateDecl>(
91+
LambdaCallOperator->getDescribedTemplate());
92+
FTD && FTD->getInstantiatedFromMemberTemplate()) {
93+
LambdaCallOperator =
94+
FTD->getInstantiatedFromMemberTemplate()->getTemplatedDecl();
95+
} else if (auto *Prev = cast<CXXMethodDecl>(LambdaCallOperator)
96+
->getInstantiatedFromMemberFunction())
97+
LambdaCallOperator = Prev;
98+
else
99+
break;
100+
}
101+
return LambdaCallOperator;
102+
}
103+
104+
struct EnclosingTypeAliasTemplateDetails {
105+
TypeAliasTemplateDecl *Template = nullptr;
106+
TypeAliasTemplateDecl *PrimaryTypeAliasDecl = nullptr;
107+
ArrayRef<TemplateArgument> AssociatedTemplateArguments;
108+
109+
explicit operator bool() noexcept { return Template; }
110+
};
111+
112+
// Find the enclosing type alias template Decl from CodeSynthesisContexts, as
113+
// well as its primary template and instantiating template arguments.
114+
EnclosingTypeAliasTemplateDetails
115+
getEnclosingTypeAliasTemplateDecl(Sema &SemaRef) {
116+
for (auto &CSC : llvm::reverse(SemaRef.CodeSynthesisContexts)) {
117+
if (CSC.Kind != Sema::CodeSynthesisContext::SynthesisKind::
118+
TypeAliasTemplateInstantiation)
119+
continue;
120+
EnclosingTypeAliasTemplateDetails Result;
121+
auto *TATD = cast<TypeAliasTemplateDecl>(CSC.Entity),
122+
*Next = TATD->getInstantiatedFromMemberTemplate();
123+
Result = {
124+
/*Template=*/TATD,
125+
/*PrimaryTypeAliasDecl=*/TATD,
126+
/*AssociatedTemplateArguments=*/CSC.template_arguments(),
127+
};
128+
while (Next) {
129+
Result.PrimaryTypeAliasDecl = Next;
130+
Next = Next->getInstantiatedFromMemberTemplate();
131+
}
132+
return Result;
133+
}
134+
return {};
135+
}
136+
137+
// Check if we are currently inside of a lambda expression that is
138+
// surrounded by a using alias declaration. e.g.
139+
// template <class> using type = decltype([](auto) { ^ }());
140+
// By checking if:
141+
// 1. The lambda expression and the using alias declaration share the
142+
// same declaration context.
143+
// 2. They have the same template depth.
144+
// We have to do so since a TypeAliasTemplateDecl (or a TypeAliasDecl) is never
145+
// a DeclContext, nor does it have an associated specialization Decl from which
146+
// we could collect these template arguments.
147+
bool isLambdaEnclosedByTypeAliasDecl(
148+
const FunctionDecl *PrimaryLambdaCallOperator,
149+
const TypeAliasTemplateDecl *PrimaryTypeAliasDecl) {
150+
return cast<CXXRecordDecl>(PrimaryLambdaCallOperator->getDeclContext())
151+
->getTemplateDepth() ==
152+
PrimaryTypeAliasDecl->getTemplateDepth() &&
153+
getLambdaAwareParentOfDeclContext(
154+
const_cast<FunctionDecl *>(PrimaryLambdaCallOperator)) ==
155+
PrimaryTypeAliasDecl->getDeclContext();
156+
}
157+
83158
// Add template arguments from a variable template instantiation.
84159
Response
85160
HandleVarTemplateSpec(const VarTemplateSpecializationDecl *VarTemplSpec,
@@ -176,7 +251,7 @@ HandleClassTemplateSpec(const ClassTemplateSpecializationDecl *ClassTemplSpec,
176251
return Response::UseNextDecl(ClassTemplSpec);
177252
}
178253

179-
Response HandleFunction(const FunctionDecl *Function,
254+
Response HandleFunction(Sema &SemaRef, const FunctionDecl *Function,
180255
MultiLevelTemplateArgumentList &Result,
181256
const FunctionDecl *Pattern, bool RelativeToPrimary,
182257
bool ForConstraintInstantiation) {
@@ -207,8 +282,23 @@ Response HandleFunction(const FunctionDecl *Function,
207282

208283
// If this function is a generic lambda specialization, we are done.
209284
if (!ForConstraintInstantiation &&
210-
isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function))
285+
isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function)) {
286+
// TypeAliasTemplateDecls should be taken into account, e.g.
287+
// when we're deducing the return type of a lambda.
288+
//
289+
// template <class> int Value = 0;
290+
// template <class T>
291+
// using T = decltype([]<int U = 0>() { return Value<T>; }());
292+
//
293+
if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(SemaRef)) {
294+
if (isLambdaEnclosedByTypeAliasDecl(
295+
/*PrimaryLambdaCallOperator=*/getPrimaryTemplateOfGenericLambda(
296+
Function),
297+
/*PrimaryTypeAliasDecl=*/TypeAlias.PrimaryTypeAliasDecl))
298+
return Response::UseNextDecl(Function);
299+
}
211300
return Response::Done();
301+
}
212302

213303
} else if (Function->getDescribedFunctionTemplate()) {
214304
assert(
@@ -312,74 +402,36 @@ Response HandleRecordDecl(Sema &SemaRef, const CXXRecordDecl *Rec,
312402
return Response::ChangeDecl(Rec->getLexicalDeclContext());
313403
}
314404

315-
// This is to make sure we pick up the VarTemplateSpecializationDecl that this
316-
// lambda is defined inside of.
405+
// This is to make sure we pick up the VarTemplateSpecializationDecl or the
406+
// TypeAliasTemplateDecl that this lambda is defined inside of.
317407
if (Rec->isLambda()) {
318408
if (const Decl *LCD = Rec->getLambdaContextDecl())
319409
return Response::ChangeDecl(LCD);
320-
// Attempt to retrieve the template arguments for a using alias declaration.
410+
// Retrieve the template arguments for a using alias declaration.
321411
// This is necessary for constraint checking, since we always keep
322412
// constraints relative to the primary template.
323-
if (ForConstraintInstantiation && !SemaRef.CodeSynthesisContexts.empty()) {
324-
for (auto &CSC : llvm::reverse(SemaRef.CodeSynthesisContexts)) {
325-
if (CSC.Kind != Sema::CodeSynthesisContext::SynthesisKind::
326-
TypeAliasTemplateInstantiation)
327-
continue;
328-
auto *TATD = cast<TypeAliasTemplateDecl>(CSC.Entity),
329-
*CurrentTATD = TATD;
330-
FunctionDecl *LambdaCallOperator = Rec->getLambdaCallOperator();
331-
// Retrieve the 'primary' template for a lambda call operator. It's
332-
// unfortunate that we only have the mappings of call operators rather
333-
// than lambda classes.
334-
while (true) {
335-
auto *FTD = dyn_cast_if_present<FunctionTemplateDecl>(
336-
LambdaCallOperator->getDescribedTemplate());
337-
if (FTD && FTD->getInstantiatedFromMemberTemplate()) {
338-
LambdaCallOperator =
339-
FTD->getInstantiatedFromMemberTemplate()->getTemplatedDecl();
340-
} else if (auto *Prev = cast<CXXMethodDecl>(LambdaCallOperator)
341-
->getInstantiatedFromMemberFunction())
342-
LambdaCallOperator = Prev;
343-
else
344-
break;
345-
}
346-
// Same applies for type alias Decl. We perform this to obtain the
347-
// "canonical" template parameter depths.
348-
while (TATD->getInstantiatedFromMemberTemplate())
349-
TATD = TATD->getInstantiatedFromMemberTemplate();
350-
// Tell if we're currently inside of a lambda expression that is
351-
// surrounded by a using alias declaration. e.g.
352-
// template <class> using type = decltype([](auto) { ^ }());
353-
// By checking if:
354-
// 1. The lambda expression and the using alias declaration share the
355-
// same declaration context.
356-
// 2. They have the same template depth.
357-
// Then we assume the template arguments from the using alias
358-
// declaration are essential for constraint instantiation. We have to do
359-
// so since a TypeAliasTemplateDecl (or a TypeAliasDecl) is never a
360-
// DeclContext, nor does it have an associated specialization Decl from
361-
// which we could collect these template arguments.
362-
if (cast<CXXRecordDecl>(LambdaCallOperator->getDeclContext())
363-
->getTemplateDepth() == TATD->getTemplateDepth() &&
364-
getLambdaAwareParentOfDeclContext(LambdaCallOperator) ==
365-
TATD->getDeclContext()) {
366-
Result.addOuterTemplateArguments(CurrentTATD,
367-
CSC.template_arguments(),
368-
/*Final=*/false);
369-
// Visit the parent of the current type alias declaration rather than
370-
// the lambda thereof. We have the following case:
371-
// struct S {
372-
// template <class> using T = decltype([]<Concept> {} ());
373-
// };
374-
// void foo() {
375-
// S::T var;
376-
// }
377-
// The instantiated lambda expression (which we're visiting at 'var')
378-
// has a function DeclContext 'foo' rather than the Record DeclContext
379-
// S. This seems to be an oversight that we may want to set a Sema
380-
// Context from the CXXScopeSpec before substituting into T to me.
381-
return Response::ChangeDecl(CurrentTATD->getDeclContext());
382-
}
413+
if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(SemaRef)) {
414+
const FunctionDecl *PrimaryLambdaCallOperator =
415+
getPrimaryTemplateOfGenericLambda(Rec->getLambdaCallOperator());
416+
if (isLambdaEnclosedByTypeAliasDecl(PrimaryLambdaCallOperator,
417+
TypeAlias.PrimaryTypeAliasDecl)) {
418+
Result.addOuterTemplateArguments(TypeAlias.Template,
419+
TypeAlias.AssociatedTemplateArguments,
420+
/*Final=*/false);
421+
// Visit the parent of the current type alias declaration rather than
422+
// the lambda thereof.
423+
// E.g., in the following example:
424+
// struct S {
425+
// template <class> using T = decltype([]<Concept> {} ());
426+
// };
427+
// void foo() {
428+
// S::T var;
429+
// }
430+
// The instantiated lambda expression (which we're visiting at 'var')
431+
// has a function DeclContext 'foo' rather than the Record DeclContext
432+
// S. This seems to be an oversight to me that we may want to set a
433+
// Sema Context from the CXXScopeSpec before substituting into T.
434+
return Response::ChangeDecl(TypeAlias.Template->getDeclContext());
383435
}
384436
}
385437
}
@@ -476,7 +528,7 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
476528
R = HandleClassTemplateSpec(ClassTemplSpec, Result,
477529
SkipForSpecialization);
478530
} else if (const auto *Function = dyn_cast<FunctionDecl>(CurDecl)) {
479-
R = HandleFunction(Function, Result, Pattern, RelativeToPrimary,
531+
R = HandleFunction(*this, Function, Result, Pattern, RelativeToPrimary,
480532
ForConstraintInstantiation);
481533
} else if (const auto *Rec = dyn_cast<CXXRecordDecl>(CurDecl)) {
482534
R = HandleRecordDecl(*this, Rec, Result, Context,
@@ -690,7 +742,7 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(
690742
: InstantiatingTemplate(
691743
SemaRef, Sema::CodeSynthesisContext::TypeAliasTemplateInstantiation,
692744
PointOfInstantiation, InstantiationRange, /*Entity=*/Template,
693-
nullptr, TemplateArgs) {}
745+
/*Template=*/nullptr, TemplateArgs) {}
694746

695747
Sema::InstantiatingTemplate::InstantiatingTemplate(
696748
Sema &SemaRef, SourceLocation PointOfInstantiation, TemplateDecl *Template,

clang/lib/Sema/TreeTransform.h

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13943,23 +13943,38 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
1394313943
SavedContext.pop();
1394413944

1394513945
// Recompute the dependency of the lambda so that we can defer the lambda call
13946-
// construction until after we have sufficient template arguments. For
13947-
// example, template <class> struct S {
13946+
// construction until after we have all the necessary template arguments. For
13947+
// example, given
13948+
//
13949+
// template <class> struct S {
1394813950
// template <class U>
1394913951
// using Type = decltype([](U){}(42.0));
1395013952
// };
1395113953
// void foo() {
1395213954
// using T = S<int>::Type<float>;
1395313955
// ^~~~~~
1395413956
// }
13955-
// We would end up here from instantiating the S<int> as we're ensuring the
13956-
// completeness. That would make us transform the lambda call expression
13957-
// despite the fact that we don't see the argument for U yet. We have a
13958-
// mechanism that circumvents the semantic checking if the CallExpr is
13959-
// dependent. We can harness that by recomputing the lambda dependency from
13960-
// the instantiation arguments. I'm putting it here rather than the above
13961-
// since we can see transformed lambda parameters in case that they're
13962-
// useful for calculation.
13957+
//
13958+
// We would end up here from instantiating S<int> when ensuring its
13959+
// completeness. That would transform the lambda call expression regardless of
13960+
// the absence of the corresponding argument for U.
13961+
//
13962+
// Going ahead with unsubstituted type U makes things worse: we would soon
13963+
// compare the argument type (which is float) against the parameter U
13964+
// somewhere in Sema::BuildCallExpr. Then we would quickly run into a bogus
13965+
// error suggesting unmatched types 'U' and 'float'!
13966+
//
13967+
// That said, everything will be fine if we defer that semantic checking.
13968+
// Fortunately, we have such a mechanism that bypasses it if the CallExpr is
13969+
// dependent. Since the CallExpr's dependency boils down to the lambda's
13970+
// dependency in this case, we can harness that by recomputing the dependency
13971+
// from the instantiation arguments.
13972+
//
13973+
// FIXME: Creating the type of a lambda requires us to have a dependency
13974+
// value, which happens before its substitution. We update its dependency
13975+
// *after* the substitution in case we can't decide the dependency
13976+
// so early, e.g. because we want to see if any of the *substituted*
13977+
// parameters are dependent.
1396313978
DependencyKind = getDerived().ComputeLambdaDependency(&LSICopy);
1396413979
Class->setLambdaDependencyKind(DependencyKind);
1396513980
// Clean up the type cache created previously. Then, we re-create a type for

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,15 @@ void bar() {
7979
using T13 = MeowMeow<char, int, long, unsigned>;
8080
}
8181

82+
namespace GH82104 {
83+
84+
template <typename, typename...> int Zero = 0;
85+
86+
template <typename T, typename...U>
87+
using T14 = decltype([]<int V = 0>() { return Zero<T, U...>; }());
88+
89+
template <typename T> using T15 = T14<T, T>;
90+
91+
} // namespace GH82104
92+
8293
} // namespace lambda_calls

0 commit comments

Comments
 (0)