Skip to content

Commit afd4188

Browse files
committed
Slightly refactor & Fix GH82104
1 parent 60b01b5 commit afd4188

File tree

3 files changed

+155
-77
lines changed

3 files changed

+155
-77
lines changed

clang/lib/Sema/SemaTemplateInstantiate.cpp

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

178-
Response HandleFunction(const FunctionDecl *Function,
253+
Response HandleFunction(Sema &SemaRef, const FunctionDecl *Function,
179254
MultiLevelTemplateArgumentList &Result,
180255
const FunctionDecl *Pattern, bool RelativeToPrimary,
181256
bool ForConstraintInstantiation) {
@@ -206,8 +281,23 @@ Response HandleFunction(const FunctionDecl *Function,
206281

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

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

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

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

clang/lib/Sema/TreeTransform.h

+25-10
Original file line numberDiff line numberDiff line change
@@ -13935,23 +13935,38 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
1393513935
SavedContext.pop();
1393613936

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

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

+11
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)