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
Prev Previous commit
Next Next commit
Format & Comments
  • Loading branch information
zyn0217 committed Mar 9, 2024
commit 303fd1bc662f2dbb4a63cc28744a1e3c92b7a34a
1 change: 1 addition & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -10146,6 +10146,7 @@ class Sema final {
/// We are building deduction guides for a class.
BuildingDeductionGuides,

/// We are instantiating a type alias template declaration.
TypeAliasTemplateInstantiation,
} Kind;

Expand Down
19 changes: 12 additions & 7 deletions clang/lib/Sema/SemaConcept.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -615,13 +615,18 @@ bool Sema::SetupConstraintScope(
// reference the original primary template.
// We walk up the instantiated template chain so that nested lambdas get
// handled properly.
FunctionTemplateDecl *FromMemTempl =
PrimaryTemplate->getInstantiatedFromMemberTemplate();
while (FromMemTempl && FromMemTempl->getInstantiatedFromMemberTemplate())
FromMemTempl = FromMemTempl->getInstantiatedFromMemberTemplate();
if (FromMemTempl && addInstantiatedParametersToScope(
FD, FromMemTempl->getTemplatedDecl(), Scope, MLTAL))
return true;
// Note that we shall not collect instantiated parameters from
// 'intermediate' transformed function templates but the primary template
// for which we have built up the template arguments relative to. Otherwise,
// we may have mismatched template parameter depth!
if (FunctionTemplateDecl *FromMemTempl =
PrimaryTemplate->getInstantiatedFromMemberTemplate()) {
while (FromMemTempl->getInstantiatedFromMemberTemplate())
FromMemTempl = FromMemTempl->getInstantiatedFromMemberTemplate();
if (addInstantiatedParametersToScope(FD, FromMemTempl->getTemplatedDecl(),
Scope, MLTAL))
return true;
}

return false;
}
Expand Down
5 changes: 3 additions & 2 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4344,8 +4344,9 @@ QualType Sema::CheckTemplateIdType(TemplateName Name,
return QualType();

InstantiatingTemplate InstTemplate(
*this, Pattern->getTypeSourceInfo()->getTypeLoc().getBeginLoc(),
AliasTemplate, TemplateArgLists.getInnermost());
*this, /*PointOfInstantiation=*/AliasTemplate->getBeginLoc(),
/*Template=*/AliasTemplate,
/*TemplateArgs=*/TemplateArgLists.getInnermost());
CanonType =
SubstType(Pattern->getUnderlyingType(), TemplateArgLists,
AliasTemplate->getLocation(), AliasTemplate->getDeclName());
Expand Down
96 changes: 65 additions & 31 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,8 +283,7 @@ Response HandleFunctionTemplateDecl(const FunctionTemplateDecl *FTD,
return Response::ChangeDecl(FTD->getLexicalDeclContext());
}

Response HandleRecordDecl(Sema &SemaRef,
const CXXRecordDecl *Rec,
Response HandleRecordDecl(Sema &SemaRef, const CXXRecordDecl *Rec,
MultiLevelTemplateArgumentList &Result,
ASTContext &Context,
bool ForConstraintInstantiation) {
Expand Down Expand Up @@ -318,35 +317,68 @@ Response HandleRecordDecl(Sema &SemaRef,
if (Rec->isLambda()) {
if (const Decl *LCD = Rec->getLambdaContextDecl())
return Response::ChangeDecl(LCD);
// Attempt to retrieve the template arguments for a using alias declaration.
// This is necessary for constraint checking, since we always keep
// constraints relative to the primary template.
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());
}
if (CSC.Kind != Sema::CodeSynthesisContext::SynthesisKind::
TypeAliasTemplateInstantiation)
continue;
auto *TATD = cast<TypeAliasTemplateDecl>(CSC.Entity),
*CurrentTATD = TATD;
FunctionDecl *LambdaCallOperator = Rec->getLambdaCallOperator();
// Retrieve the 'primary' template for a lambda call operator. It's
// unfortunate that we only have the mappings of call operators rather
// than lambda classes.
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;
}
// Same applies for type alias Decl. We perform this to obtain the
// "canonical" template parameter depths.
while (TATD->getInstantiatedFromMemberTemplate())
TATD = TATD->getInstantiatedFromMemberTemplate();
// Tell if we're currently inside of a lambda expression that is
// surrounded by a using alias declaration. e.g.
// template <class> using type = decltype([](auto) { ^ }());
// By checking if:
// 1. The lambda expression and the using alias declaration share the
// same declaration context.
// 2. They have the same template depth.
// Then we assume the template arguments from the using alias
// declaration are essential for constraint instantiation. We have to do
// so since 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.
if (cast<CXXRecordDecl>(LambdaCallOperator->getDeclContext())
->getTemplateDepth() == TATD->getTemplateDepth() &&
getLambdaAwareParentOfDeclContext(LambdaCallOperator) ==
TATD->getDeclContext()) {
Result.addOuterTemplateArguments(CurrentTATD,
CSC.template_arguments(),
/*Final=*/false);
// Visit the parent of the current type alias declaration rather than
// the lambda thereof. We have the following case:
// struct S {
// template <class> using T = decltype([]<Concept> {} ());
// };
// void foo() {
// S::T var;
// }
// The instantiated lambda expression (which we're visiting at 'var')
// has a function DeclContext 'foo' rather than the Record DeclContext
// S. This seems to be an oversight that we may want to set a Sema
// Context from the CXXScopeSpec before substituting into T to me.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@erichkeane Opinion?

return Response::ChangeDecl(CurrentTATD->getDeclContext());
}
}
}
Expand Down Expand Up @@ -447,7 +479,8 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
R = HandleFunction(Function, Result, Pattern, RelativeToPrimary,
ForConstraintInstantiation);
} else if (const auto *Rec = dyn_cast<CXXRecordDecl>(CurDecl)) {
R = HandleRecordDecl(*this, 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 @@ -1583,7 +1616,8 @@ namespace {
CXXRecordDecl::LambdaDependencyKind
ComputeLambdaDependency(LambdaScopeInfo *LSI) {
auto &CCS = SemaRef.CodeSynthesisContexts.back();
if (CCS.Kind == Sema::CodeSynthesisContext::TypeAliasTemplateInstantiation) {
if (CCS.Kind ==
Sema::CodeSynthesisContext::TypeAliasTemplateInstantiation) {
unsigned TypeAliasDeclDepth = CCS.Entity->getTemplateDepth();
if (TypeAliasDeclDepth >= TemplateArgs.getNumSubstitutedLevels())
return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent;
Expand Down
26 changes: 24 additions & 2 deletions clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -767,8 +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());
CXXRecordDecl::LambdaDependencyKind
ComputeLambdaDependency(LambdaScopeInfo *LSI) {
return static_cast<CXXRecordDecl::LambdaDependencyKind>(
LSI->Lambda->getLambdaDependencyKind());
}

QualType TransformReferenceType(TypeLocBuilder &TLB, ReferenceTypeLoc TL);
Expand Down Expand Up @@ -13940,8 +13942,28 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
/*IsInstantiation*/ true);
SavedContext.pop();

// Recompute the dependency of the lambda so that we can defer the lambda call
// construction until after we have sufficient template arguments. For
// example, template <class> struct S {
// template <class U>
// using Type = decltype([](U){}(42.0));
// };
// void foo() {
// using T = S<int>::Type<float>;
// ^~~~~~
// }
// We would end up here from instantiating the S<int> as we're ensuring the
// completeness. That would make us transform the lambda call expression
// despite the fact that we don't see the argument for U yet. We have a
// mechanism that circumvents the semantic checking if the CallExpr is
// dependent. We can harness that by recomputing the lambda dependency from
// the instantiation arguments. I'm putting it here rather than the above
// since we can see transformed lambda parameters in case that they're
// useful for calculation.
DependencyKind = getDerived().ComputeLambdaDependency(&LSICopy);
Class->setLambdaDependencyKind(DependencyKind);
// Clean up the type cache created previously. Then, we re-create a type for
// such Decl with the new DependencyKind.
Class->setTypeForDecl(nullptr);
getSema().Context.getTypeDeclType(Class);

Expand Down