[clang] [Clang][Sema] Fix the lambda call expression inside of a type alias declaration (PR #82310)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Mar 5 03:31:39 PST 2024
================
@@ -313,9 +313,75 @@ 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);
+ // 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)
+ 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.
----------------
cor3ntin wrote:
@erichkeane Opinion?
https://github.com/llvm/llvm-project/pull/82310
More information about the cfe-commits
mailing list