Skip to content

Commit 843cc47

Browse files
authored
[Clang][Sema] Fix the lambda call expression inside of a type alias declaration (#82310)
This patch attempts to fix the lambda call expression inside of a type alias declaration from two aspects: 1. Defer the lambda call expression building until after we have sufficient template arguments. This avoids the overeager (and often wrong) semantic checking before the type alias instantiation. 2. Properly obtain template arguments involving a template type alias for constraint checking. It is unfortunate that 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. Thus, I added a new CodeSynthesisContext to record template arguments for alias declarations. Fixes #70601 Fixes #76674 Fixes #79555 Fixes #81145 Fixes #82104 Note that this doesn't involve the fix for #28461. That seems different, and I'd like to leave it as a follow-up.
1 parent ff1e72d commit 843cc47

File tree

10 files changed

+338
-12
lines changed

10 files changed

+338
-12
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,8 @@ Bug Fixes to C++ Support
480480
when one of the function had more specialized templates.
481481
Fixes (`#82509 <https://github.com/llvm/llvm-project/issues/82509>`_)
482482
and (`#74494 <https://github.com/llvm/llvm-project/issues/74494>`_)
483+
- Clang now supports direct lambda calls inside of a type alias template declarations.
484+
This addresses (#GH70601), (#GH76674), (#GH79555), (#GH81145) and (#GH82104).
483485
- Allow access to a public template alias declaration that refers to friend's
484486
private nested type. (#GH25708).
485487
- Fixed a crash in constant evaluation when trying to access a

clang/include/clang/AST/DeclCXX.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1869,6 +1869,10 @@ class CXXRecordDecl : public RecordDecl {
18691869
DL.MethodTyInfo = TS;
18701870
}
18711871

1872+
void setLambdaDependencyKind(unsigned Kind) {
1873+
getLambdaData().DependencyKind = Kind;
1874+
}
1875+
18721876
void setLambdaIsGeneric(bool IsGeneric) {
18731877
assert(DefinitionData && DefinitionData->IsLambda &&
18741878
"setting lambda property of non-lambda class");

clang/include/clang/Sema/Sema.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10170,6 +10170,9 @@ class Sema final {
1017010170

1017110171
/// We are building deduction guides for a class.
1017210172
BuildingDeductionGuides,
10173+
10174+
/// We are instantiating a type alias template declaration.
10175+
TypeAliasTemplateInstantiation,
1017310176
} Kind;
1017410177

1017510178
/// Was the enclosing context a non-instantiation SFINAE context?
@@ -10259,6 +10262,12 @@ class Sema final {
1025910262
FunctionDecl *Entity, ExceptionSpecification,
1026010263
SourceRange InstantiationRange = SourceRange());
1026110264

10265+
/// Note that we are instantiating a type alias template declaration.
10266+
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
10267+
TypeAliasTemplateDecl *Template,
10268+
ArrayRef<TemplateArgument> TemplateArgs,
10269+
SourceRange InstantiationRange = SourceRange());
10270+
1026210271
/// Note that we are instantiating a default argument in a
1026310272
/// template-id.
1026410273
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,

clang/lib/Frontend/FrontendActions.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,8 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback {
453453
return "BuildingBuiltinDumpStructCall";
454454
case CodeSynthesisContext::BuildingDeductionGuides:
455455
return "BuildingDeductionGuides";
456+
case Sema::CodeSynthesisContext::TypeAliasTemplateInstantiation:
457+
return "TypeAliasTemplateInstantiation";
456458
}
457459
return "";
458460
}

clang/lib/Sema/SemaConcept.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -615,10 +615,12 @@ bool Sema::SetupConstraintScope(
615615
// reference the original primary template.
616616
// We walk up the instantiated template chain so that nested lambdas get
617617
// handled properly.
618-
for (FunctionTemplateDecl *FromMemTempl =
619-
PrimaryTemplate->getInstantiatedFromMemberTemplate();
620-
FromMemTempl;
621-
FromMemTempl = FromMemTempl->getInstantiatedFromMemberTemplate()) {
618+
// We should only collect instantiated parameters from the primary template.
619+
// Otherwise, we may have mismatched template parameter depth!
620+
if (FunctionTemplateDecl *FromMemTempl =
621+
PrimaryTemplate->getInstantiatedFromMemberTemplate()) {
622+
while (FromMemTempl->getInstantiatedFromMemberTemplate())
623+
FromMemTempl = FromMemTempl->getInstantiatedFromMemberTemplate();
622624
if (addInstantiatedParametersToScope(FD, FromMemTempl->getTemplatedDecl(),
623625
Scope, MLTAL))
624626
return true;

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4383,6 +4383,11 @@ QualType Sema::CheckTemplateIdType(TemplateName Name,
43834383
if (Inst.isInvalid())
43844384
return QualType();
43854385

4386+
InstantiatingTemplate InstTemplate(
4387+
*this, /*PointOfInstantiation=*/AliasTemplate->getBeginLoc(),
4388+
/*Template=*/AliasTemplate,
4389+
/*TemplateArgs=*/TemplateArgLists.getInnermost());
4390+
43864391
std::optional<ContextRAII> SavedContext;
43874392
if (!AliasTemplate->getDeclContext()->isFileContext())
43884393
SavedContext.emplace(*this, AliasTemplate->getDeclContext());

clang/lib/Sema/SemaTemplateInstantiate.cpp

Lines changed: 152 additions & 8 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(
@@ -283,7 +373,7 @@ Response HandleFunctionTemplateDecl(const FunctionTemplateDecl *FTD,
283373
return Response::ChangeDecl(FTD->getLexicalDeclContext());
284374
}
285375

286-
Response HandleRecordDecl(const CXXRecordDecl *Rec,
376+
Response HandleRecordDecl(Sema &SemaRef, const CXXRecordDecl *Rec,
287377
MultiLevelTemplateArgumentList &Result,
288378
ASTContext &Context,
289379
bool ForConstraintInstantiation) {
@@ -312,11 +402,39 @@ Response HandleRecordDecl(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.
317-
if (Rec->isLambda())
405+
// This is to make sure we pick up the VarTemplateSpecializationDecl or the
406+
// TypeAliasTemplateDecl that this lambda is defined inside of.
407+
if (Rec->isLambda()) {
318408
if (const Decl *LCD = Rec->getLambdaContextDecl())
319409
return Response::ChangeDecl(LCD);
410+
// Retrieve the template arguments for a using alias declaration.
411+
// This is necessary for constraint checking, since we always keep
412+
// constraints relative to the primary template.
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());
435+
}
436+
}
437+
}
320438

321439
return Response::UseNextDecl(Rec);
322440
}
@@ -410,10 +528,11 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
410528
R = HandleClassTemplateSpec(ClassTemplSpec, Result,
411529
SkipForSpecialization);
412530
} else if (const auto *Function = dyn_cast<FunctionDecl>(CurDecl)) {
413-
R = HandleFunction(Function, Result, Pattern, RelativeToPrimary,
531+
R = HandleFunction(*this, Function, Result, Pattern, RelativeToPrimary,
414532
ForConstraintInstantiation);
415533
} else if (const auto *Rec = dyn_cast<CXXRecordDecl>(CurDecl)) {
416-
R = HandleRecordDecl(Rec, Result, Context, ForConstraintInstantiation);
534+
R = HandleRecordDecl(*this, Rec, Result, Context,
535+
ForConstraintInstantiation);
417536
} else if (const auto *CSD =
418537
dyn_cast<ImplicitConceptSpecializationDecl>(CurDecl)) {
419538
R = HandleImplicitConceptSpecializationDecl(CSD, Result);
@@ -470,6 +589,7 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const {
470589
case BuildingBuiltinDumpStructCall:
471590
case LambdaExpressionSubstitution:
472591
case BuildingDeductionGuides:
592+
case TypeAliasTemplateInstantiation:
473593
return false;
474594

475595
// This function should never be called when Kind's value is Memoization.
@@ -615,6 +735,15 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(
615735
PointOfInstantiation, InstantiationRange, Param, Template,
616736
TemplateArgs) {}
617737

738+
Sema::InstantiatingTemplate::InstantiatingTemplate(
739+
Sema &SemaRef, SourceLocation PointOfInstantiation,
740+
TypeAliasTemplateDecl *Template, ArrayRef<TemplateArgument> TemplateArgs,
741+
SourceRange InstantiationRange)
742+
: InstantiatingTemplate(
743+
SemaRef, Sema::CodeSynthesisContext::TypeAliasTemplateInstantiation,
744+
PointOfInstantiation, InstantiationRange, /*Entity=*/Template,
745+
/*Template=*/nullptr, TemplateArgs) {}
746+
618747
Sema::InstantiatingTemplate::InstantiatingTemplate(
619748
Sema &SemaRef, SourceLocation PointOfInstantiation, TemplateDecl *Template,
620749
NamedDecl *Param, ArrayRef<TemplateArgument> TemplateArgs,
@@ -1132,6 +1261,8 @@ void Sema::PrintInstantiationStack() {
11321261
Diags.Report(Active->PointOfInstantiation,
11331262
diag::note_building_deduction_guide_here);
11341263
break;
1264+
case CodeSynthesisContext::TypeAliasTemplateInstantiation:
1265+
break;
11351266
}
11361267
}
11371268
}
@@ -1209,6 +1340,7 @@ std::optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const {
12091340
break;
12101341

12111342
case CodeSynthesisContext::Memoization:
1343+
case CodeSynthesisContext::TypeAliasTemplateInstantiation:
12121344
break;
12131345
}
12141346

@@ -1534,6 +1666,18 @@ namespace {
15341666
SubstTemplateTypeParmPackTypeLoc TL,
15351667
bool SuppressObjCLifetime);
15361668

1669+
CXXRecordDecl::LambdaDependencyKind
1670+
ComputeLambdaDependency(LambdaScopeInfo *LSI) {
1671+
auto &CCS = SemaRef.CodeSynthesisContexts.back();
1672+
if (CCS.Kind ==
1673+
Sema::CodeSynthesisContext::TypeAliasTemplateInstantiation) {
1674+
unsigned TypeAliasDeclDepth = CCS.Entity->getTemplateDepth();
1675+
if (TypeAliasDeclDepth >= TemplateArgs.getNumSubstitutedLevels())
1676+
return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent;
1677+
}
1678+
return inherited::ComputeLambdaDependency(LSI);
1679+
}
1680+
15371681
ExprResult TransformLambdaExpr(LambdaExpr *E) {
15381682
LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
15391683
Sema::ConstraintEvalRAII<TemplateInstantiator> RAII(*this);

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,6 +1112,13 @@ TemplateDeclInstantiator::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) {
11121112
return nullptr;
11131113

11141114
TypeAliasDecl *Pattern = D->getTemplatedDecl();
1115+
Sema::InstantiatingTemplate InstTemplate(
1116+
SemaRef, D->getBeginLoc(), D,
1117+
D->getTemplateDepth() >= TemplateArgs.getNumLevels()
1118+
? ArrayRef<TemplateArgument>()
1119+
: (TemplateArgs.begin() + TemplateArgs.getNumLevels() - 1 -
1120+
D->getTemplateDepth())
1121+
->Args);
11151122

11161123
TypeAliasTemplateDecl *PrevAliasTemplate = nullptr;
11171124
if (getPreviousDeclForInstantiation<TypedefNameDecl>(Pattern)) {

clang/lib/Sema/TreeTransform.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,12 @@ class TreeTransform {
768768
/// the body.
769769
StmtResult SkipLambdaBody(LambdaExpr *E, Stmt *Body);
770770

771+
CXXRecordDecl::LambdaDependencyKind
772+
ComputeLambdaDependency(LambdaScopeInfo *LSI) {
773+
return static_cast<CXXRecordDecl::LambdaDependencyKind>(
774+
LSI->Lambda->getLambdaDependencyKind());
775+
}
776+
771777
QualType TransformReferenceType(TypeLocBuilder &TLB, ReferenceTypeLoc TL);
772778

773779
StmtResult TransformCompoundStmt(CompoundStmt *S, bool IsStmtExpr);
@@ -13992,6 +13998,46 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
1399213998
/*IsInstantiation*/ true);
1399313999
SavedContext.pop();
1399414000

14001+
// Recompute the dependency of the lambda so that we can defer the lambda call
14002+
// construction until after we have all the necessary template arguments. For
14003+
// example, given
14004+
//
14005+
// template <class> struct S {
14006+
// template <class U>
14007+
// using Type = decltype([](U){}(42.0));
14008+
// };
14009+
// void foo() {
14010+
// using T = S<int>::Type<float>;
14011+
// ^~~~~~
14012+
// }
14013+
//
14014+
// We would end up here from instantiating S<int> when ensuring its
14015+
// completeness. That would transform the lambda call expression regardless of
14016+
// the absence of the corresponding argument for U.
14017+
//
14018+
// Going ahead with unsubstituted type U makes things worse: we would soon
14019+
// compare the argument type (which is float) against the parameter U
14020+
// somewhere in Sema::BuildCallExpr. Then we would quickly run into a bogus
14021+
// error suggesting unmatched types 'U' and 'float'!
14022+
//
14023+
// That said, everything will be fine if we defer that semantic checking.
14024+
// Fortunately, we have such a mechanism that bypasses it if the CallExpr is
14025+
// dependent. Since the CallExpr's dependency boils down to the lambda's
14026+
// dependency in this case, we can harness that by recomputing the dependency
14027+
// from the instantiation arguments.
14028+
//
14029+
// FIXME: Creating the type of a lambda requires us to have a dependency
14030+
// value, which happens before its substitution. We update its dependency
14031+
// *after* the substitution in case we can't decide the dependency
14032+
// so early, e.g. because we want to see if any of the *substituted*
14033+
// parameters are dependent.
14034+
DependencyKind = getDerived().ComputeLambdaDependency(&LSICopy);
14035+
Class->setLambdaDependencyKind(DependencyKind);
14036+
// Clean up the type cache created previously. Then, we re-create a type for
14037+
// such Decl with the new DependencyKind.
14038+
Class->setTypeForDecl(nullptr);
14039+
getSema().Context.getTypeDeclType(Class);
14040+
1399514041
return getSema().BuildLambdaExpr(E->getBeginLoc(), Body.get()->getEndLoc(),
1399614042
&LSICopy);
1399714043
}

0 commit comments

Comments
 (0)