Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
The lambda call inside of a type alias
  • Loading branch information
zyn0217 committed Mar 9, 2024
commit 25f493da55e5cd7d46dda6fca6062aa56b6a3fd0
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,8 @@ Bug Fixes to C++ Support
when one of the function had more specialized templates.
Fixes (`#82509 <https://github.com/llvm/llvm-project/issues/82509>`_)
and (`#74494 <https://github.com/llvm/llvm-project/issues/74494>`_)
- Clang now supports direct lambda calls inside of a type alias template declarations.
This addresses (#GH70601), (#GH76674), (#GH79555), (#GH81145) and so on.

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/AST/DeclCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -1869,6 +1869,10 @@ class CXXRecordDecl : public RecordDecl {
DL.MethodTyInfo = TS;
}

void setLambdaDependencyKind(unsigned Kind) {
getLambdaData().DependencyKind = Kind;
}

void setLambdaIsGeneric(bool IsGeneric) {
assert(DefinitionData && DefinitionData->IsLambda &&
"setting lambda property of non-lambda class");
Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -10145,6 +10145,8 @@ class Sema final {

/// We are building deduction guides for a class.
BuildingDeductionGuides,

TypeAliasTemplateInstantiation,
} Kind;

/// Was the enclosing context a non-instantiation SFINAE context?
Expand Down Expand Up @@ -10234,6 +10236,12 @@ class Sema final {
FunctionDecl *Entity, ExceptionSpecification,
SourceRange InstantiationRange = SourceRange());

/// Note that we are instantiating a type alias template declaration.
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
TypeAliasTemplateDecl *Template,
ArrayRef<TemplateArgument> TemplateArgs,
SourceRange InstantiationRange = SourceRange());

/// Note that we are instantiating a default argument in a
/// template-id.
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Frontend/FrontendActions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,8 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback {
return "BuildingBuiltinDumpStructCall";
case CodeSynthesisContext::BuildingDeductionGuides:
return "BuildingDeductionGuides";
case Sema::CodeSynthesisContext::TypeAliasTemplateInstantiation:
return "TypeAliasTemplateInstantiation";
}
return "";
}
Expand Down
15 changes: 7 additions & 8 deletions clang/lib/Sema/SemaConcept.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -615,14 +615,13 @@ bool Sema::SetupConstraintScope(
// reference the original primary template.
// We walk up the instantiated template chain so that nested lambdas get
// handled properly.
for (FunctionTemplateDecl *FromMemTempl =
PrimaryTemplate->getInstantiatedFromMemberTemplate();
FromMemTempl;
FromMemTempl = FromMemTempl->getInstantiatedFromMemberTemplate()) {
if (addInstantiatedParametersToScope(FD, FromMemTempl->getTemplatedDecl(),
Scope, MLTAL))
return true;
}
FunctionTemplateDecl *FromMemTempl =
PrimaryTemplate->getInstantiatedFromMemberTemplate();
while (FromMemTempl && FromMemTempl->getInstantiatedFromMemberTemplate())
FromMemTempl = FromMemTempl->getInstantiatedFromMemberTemplate();
if (FromMemTempl && addInstantiatedParametersToScope(
FD, FromMemTempl->getTemplatedDecl(), Scope, MLTAL))
return true;

return false;
}
Expand Down
9 changes: 6 additions & 3 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4343,9 +4343,12 @@ QualType Sema::CheckTemplateIdType(TemplateName Name,
if (Inst.isInvalid())
return QualType();

CanonType = SubstType(Pattern->getUnderlyingType(),
TemplateArgLists, AliasTemplate->getLocation(),
AliasTemplate->getDeclName());
InstantiatingTemplate InstTemplate(
*this, Pattern->getTypeSourceInfo()->getTypeLoc().getBeginLoc(),
AliasTemplate, TemplateArgLists.getInnermost());
CanonType =
SubstType(Pattern->getUnderlyingType(), TemplateArgLists,
AliasTemplate->getLocation(), AliasTemplate->getDeclName());
if (CanonType.isNull()) {
// If this was enable_if and we failed to find the nested type
// within enable_if in a SFINAE context, dig out the specific
Expand Down
64 changes: 61 additions & 3 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,8 @@ Response HandleFunctionTemplateDecl(const FunctionTemplateDecl *FTD,
return Response::ChangeDecl(FTD->getLexicalDeclContext());
}

Response HandleRecordDecl(const CXXRecordDecl *Rec,
Response HandleRecordDecl(Sema &SemaRef,
const CXXRecordDecl *Rec,
MultiLevelTemplateArgumentList &Result,
ASTContext &Context,
bool ForConstraintInstantiation) {
Expand Down Expand Up @@ -314,9 +315,42 @@ Response HandleRecordDecl(const CXXRecordDecl *Rec,

// This is to make sure we pick up the VarTemplateSpecializationDecl that this
// lambda is defined inside of.
if (Rec->isLambda())
if (Rec->isLambda()) {
if (const Decl *LCD = Rec->getLambdaContextDecl())
return Response::ChangeDecl(LCD);
if (ForConstraintInstantiation && !SemaRef.CodeSynthesisContexts.empty()) {
for (auto &CSC : llvm::reverse(SemaRef.CodeSynthesisContexts)) {
if (CSC.Kind == Sema::CodeSynthesisContext::SynthesisKind::TypeAliasTemplateInstantiation) {
auto *TATD = cast<TypeAliasTemplateDecl>(CSC.Entity), *CurrentTATD = TATD;
FunctionDecl *LambdaCallOperator = Rec->getLambdaCallOperator();
while (true) {
auto *FTD = dyn_cast_if_present<FunctionTemplateDecl>(
LambdaCallOperator->getDescribedTemplate());
if (FTD && FTD->getInstantiatedFromMemberTemplate()) {
LambdaCallOperator =
FTD->getInstantiatedFromMemberTemplate()->getTemplatedDecl();
} else if (auto *Prev = cast<CXXMethodDecl>(LambdaCallOperator)
->getInstantiatedFromMemberFunction())
LambdaCallOperator = Prev;
else
break;
}
while (TATD->getInstantiatedFromMemberTemplate())
TATD = TATD->getInstantiatedFromMemberTemplate();
// Constraint template parameters have a deeper depth.
if (cast<CXXRecordDecl>(LambdaCallOperator->getDeclContext())
->getTemplateDepth() == TATD->getTemplateDepth() &&
getLambdaAwareParentOfDeclContext(LambdaCallOperator) ==
TATD->getDeclContext()) {
Result.addOuterTemplateArguments(CurrentTATD,
CSC.template_arguments(),
/*Final=*/false);
return Response::ChangeDecl(CurrentTATD->getDeclContext());
}
}
}
}
}

return Response::UseNextDecl(Rec);
}
Expand Down Expand Up @@ -413,7 +447,7 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
R = HandleFunction(Function, Result, Pattern, RelativeToPrimary,
ForConstraintInstantiation);
} else if (const auto *Rec = dyn_cast<CXXRecordDecl>(CurDecl)) {
R = HandleRecordDecl(Rec, Result, Context, ForConstraintInstantiation);
R = HandleRecordDecl(*this, Rec, Result, Context, ForConstraintInstantiation);
} else if (const auto *CSD =
dyn_cast<ImplicitConceptSpecializationDecl>(CurDecl)) {
R = HandleImplicitConceptSpecializationDecl(CSD, Result);
Expand Down Expand Up @@ -470,6 +504,7 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const {
case BuildingBuiltinDumpStructCall:
case LambdaExpressionSubstitution:
case BuildingDeductionGuides:
case TypeAliasTemplateInstantiation:
return false;

// This function should never be called when Kind's value is Memoization.
Expand Down Expand Up @@ -615,6 +650,15 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(
PointOfInstantiation, InstantiationRange, Param, Template,
TemplateArgs) {}

Sema::InstantiatingTemplate::InstantiatingTemplate(
Sema &SemaRef, SourceLocation PointOfInstantiation,
TypeAliasTemplateDecl *Template, ArrayRef<TemplateArgument> TemplateArgs,
SourceRange InstantiationRange)
: InstantiatingTemplate(
SemaRef, Sema::CodeSynthesisContext::TypeAliasTemplateInstantiation,
PointOfInstantiation, InstantiationRange, /*Entity=*/Template,
nullptr, TemplateArgs) {}

Sema::InstantiatingTemplate::InstantiatingTemplate(
Sema &SemaRef, SourceLocation PointOfInstantiation, TemplateDecl *Template,
NamedDecl *Param, ArrayRef<TemplateArgument> TemplateArgs,
Expand Down Expand Up @@ -1132,6 +1176,8 @@ void Sema::PrintInstantiationStack() {
Diags.Report(Active->PointOfInstantiation,
diag::note_building_deduction_guide_here);
break;
case CodeSynthesisContext::TypeAliasTemplateInstantiation:
break;
}
}
}
Expand Down Expand Up @@ -1209,6 +1255,7 @@ std::optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const {
break;

case CodeSynthesisContext::Memoization:
case CodeSynthesisContext::TypeAliasTemplateInstantiation:
break;
}

Expand Down Expand Up @@ -1533,6 +1580,17 @@ namespace {
SubstTemplateTypeParmPackTypeLoc TL,
bool SuppressObjCLifetime);

CXXRecordDecl::LambdaDependencyKind
ComputeLambdaDependency(LambdaScopeInfo *LSI) {
auto &CCS = SemaRef.CodeSynthesisContexts.back();
if (CCS.Kind == Sema::CodeSynthesisContext::TypeAliasTemplateInstantiation) {
unsigned TypeAliasDeclDepth = CCS.Entity->getTemplateDepth();
if (TypeAliasDeclDepth >= TemplateArgs.getNumSubstitutedLevels())
return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent;
}
return inherited::ComputeLambdaDependency(LSI);
}

ExprResult TransformLambdaExpr(LambdaExpr *E) {
LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
Sema::ConstraintEvalRAII<TemplateInstantiator> RAII(*this);
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1083,6 +1083,11 @@ TemplateDeclInstantiator::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) {
return nullptr;

TypeAliasDecl *Pattern = D->getTemplatedDecl();
Sema::InstantiatingTemplate InstTemplate(
SemaRef, Pattern->getTypeSourceInfo()->getTypeLoc().getBeginLoc(), D,
D->getTemplateDepth() >= TemplateArgs.getNumSubstitutedLevels()
? ArrayRef<TemplateArgument>()
: TemplateArgs.getInnermost());

TypeAliasTemplateDecl *PrevAliasTemplate = nullptr;
if (getPreviousDeclForInstantiation<TypedefNameDecl>(Pattern)) {
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,10 @@ class TreeTransform {
/// the body.
StmtResult SkipLambdaBody(LambdaExpr *E, Stmt *Body);

CXXRecordDecl::LambdaDependencyKind ComputeLambdaDependency(LambdaScopeInfo *LSI) {
return static_cast<CXXRecordDecl::LambdaDependencyKind>(LSI->Lambda->getLambdaDependencyKind());
}

QualType TransformReferenceType(TypeLocBuilder &TLB, ReferenceTypeLoc TL);

StmtResult TransformCompoundStmt(CompoundStmt *S, bool IsStmtExpr);
Expand Down Expand Up @@ -13936,6 +13940,11 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
/*IsInstantiation*/ true);
SavedContext.pop();

DependencyKind = getDerived().ComputeLambdaDependency(&LSICopy);
Class->setLambdaDependencyKind(DependencyKind);
Class->setTypeForDecl(nullptr);
getSema().Context.getTypeDeclType(Class);

return getSema().BuildLambdaExpr(E->getBeginLoc(), Body.get()->getEndLoc(),
&LSICopy);
}
Expand Down
82 changes: 82 additions & 0 deletions clang/test/SemaTemplate/alias-template-with-lambdas.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// RUN: %clang_cc1 -std=c++2c -fsyntax-only -verify %s
namespace lambda_calls {

template <class>
concept True = true;

template <class>
concept False = false; // #False

template <class T> struct S {
template <class... U> using type = decltype([](U...) {}(U()...));
template <class U> using type2 = decltype([](auto) {}(1));
template <class U> using type3 = decltype([](True auto) {}(1));
template <class>
using type4 = decltype([](auto... pack) { return sizeof...(pack); }(1, 2));

template <class U> using type5 = decltype([](False auto...) {}(1)); // #Type5

template <class U>
using type6 = decltype([]<True> {}.template operator()<char>());
template <class U>
using type7 = decltype([]<False> {}.template operator()<char>()); // #Type7

template <class U>
using type8 = decltype([]() // #Type8
requires(sizeof(U) == 32) // #Type8-requirement
{}());

template <class... U>
using type9 = decltype([]<True>(U...) {}.template operator()<char>(U()...));
// https://github.com/llvm/llvm-project/issues/76674
template <class U>
using type10 = decltype([]<class V> { return V(); }.template operator()<U>());

template <class U> using type11 = decltype([] { return U{}; });
};

template <class> using Meow = decltype([]<True> {}.template operator()<int>());

template <class... U>
using MeowMeow = decltype([]<True>(U...) {}.template operator()<char>(U()...));

// https://github.com/llvm/llvm-project/issues/70601
template <class> using U = decltype([]<True> {}.template operator()<int>());

U<int> foo();

void bar() {
using T = S<int>::type<int, int, int>;
using T2 = S<int>::type2<int>;
using T3 = S<int>::type3<char>;
using T4 = S<int>::type4<void>;
using T5 = S<int>::type5<void>; // #T5
// expected-error@#Type5 {{no matching function for call}}
// expected-note@#T5 {{type alias 'type5' requested here}}
// expected-note@#Type5 {{constraints not satisfied [with auto:1 = <int>]}}
// expected-note@#Type5 {{because 'int' does not satisfy 'False'}}
// expected-note@#False {{because 'false' evaluated to false}}

using T6 = S<int>::type6<void>;
using T7 = S<int>::type7<void>; // #T7
// expected-error@#Type7 {{no matching member function for call}}
// expected-note@#T7 {{type alias 'type7' requested here}}
// expected-note@#Type7 {{constraints not satisfied [with $0 = char]}}
// expected-note@#Type7 {{because 'char' does not satisfy 'False'}}
// expected-note@#False {{because 'false' evaluated to false}}

using T8 = S<int>::type8<char>; // #T8
// expected-error@#Type8 {{no matching function for call}}
// expected-note@#T8 {{type alias 'type8' requested here}}
// expected-note@#Type8 {{constraints not satisfied}}
// expected-note@#Type8-requirement {{because 'sizeof(char) == 32' (1 == 32) evaluated to false}}

using T9 = S<int>::type9<long, long, char>;
using T10 = S<int>::type10<int>;
using T11 = S<int>::type11<int>;
int x = T11()();
using T12 = Meow<int>;
using T13 = MeowMeow<char, int, long, unsigned>;
}

} // namespace lambda_calls