https://github.com/0x59616e updated https://github.com/llvm/llvm-project/pull/65193:
>From a65cb213b6ea24a04e170a7cc210ed9d2d00a9ac Mon Sep 17 00:00:00 2001 From: Sheng <ox596...@gmail.com> Date: Wed, 30 Aug 2023 11:44:23 +0800 Subject: [PATCH] [clang][Sema] Fix a bug when instantiating a lambda with requires clause Instantiating a lambda at a scope different from its definition scope will paralyze clang if the trailing require clause refers to local variables of that definition scope. This patch fixes this by re-adding the local variables to `LocalInstantiationScope`. Fixes #64462 --- clang/include/clang/Sema/Sema.h | 5 +- clang/lib/Sema/SemaConcept.cpp | 136 ++++++++++++++++++++++++-------- clang/test/SemaCXX/pr64462.cpp | 20 +++++ 3 files changed, 125 insertions(+), 36 deletions(-) create mode 100644 clang/test/SemaCXX/pr64462.cpp diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 1980571e6656f9..d33af3d113b90c 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -7394,7 +7394,8 @@ class Sema final { /// function. bool SetupConstraintScope( FunctionDecl *FD, std::optional<ArrayRef<TemplateArgument>> TemplateArgs, - MultiLevelTemplateArgumentList MLTAL, LocalInstantiationScope &Scope); + MultiLevelTemplateArgumentList MLTAL, LocalInstantiationScope &Scope, + const bool shouldAddDeclsFromParentScope); /// Used during constraint checking, sets up the constraint template argument /// lists, and calls SetupConstraintScope to set up the @@ -7402,7 +7403,7 @@ class Sema final { std::optional<MultiLevelTemplateArgumentList> SetupConstraintCheckingTemplateArgumentsAndScope( FunctionDecl *FD, std::optional<ArrayRef<TemplateArgument>> TemplateArgs, - LocalInstantiationScope &Scope); + LocalInstantiationScope &Scope, const bool shouldAddDeclsFromParentScope); private: // The current stack of constraint satisfactions, so we can exit-early. diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index fa3dadf68229ee..a15324f2424265 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -567,9 +567,65 @@ bool Sema::addInstantiatedCapturesToScope( return false; } +static void addDeclsFromParentScope(Sema &S, FunctionDecl *FD, + FunctionDecl *Pattern, + LocalInstantiationScope &Scope) { + LambdaScopeInfo *LSI = nullptr; + if (!S.getFunctionScopes().empty()) + LSI = dyn_cast<LambdaScopeInfo>(S.getFunctionScopes().back()); + + auto captureVarIfNeeded = [&](VarDecl *VD) { + if (!LSI) + return; + + LSI->addCapture(VD, /*isBlock=*/false, /*isByref=*/false, + /*isNested=*/false, VD->getBeginLoc(), SourceLocation(), + VD->getType(), /*Invalid=*/false); + }; + + FD = dyn_cast<FunctionDecl>(FD->getParent()->getParent()); + Pattern = dyn_cast<FunctionDecl>(Pattern->getParent()->getParent()); + + if (!FD || !Pattern) + return; + + for (unsigned I = 0; I < Pattern->getNumParams(); ++I) { + ParmVarDecl *PVD = Pattern->getParamDecl(I); + if (!PVD->isParameterPack()) { + Scope.InstantiatedLocal(PVD, FD->getParamDecl(I)); + captureVarIfNeeded(FD->getParamDecl(I)); + continue; + } + + Scope.MakeInstantiatedLocalArgPack(PVD); + + for (ParmVarDecl *Inst : FD->parameters().drop_front(I)) { + Scope.InstantiatedLocalPackArg(PVD, Inst); + captureVarIfNeeded(Inst); + } + } + + for (auto *decl : Pattern->decls()) { + if (!isa<VarDecl>(decl) || isa<ParmVarDecl>(decl)) + continue; + + IdentifierInfo *II = cast<NamedDecl>(decl)->getIdentifier(); + auto it = llvm::find_if(FD->decls(), [&](Decl *inst) { + VarDecl *VD = dyn_cast<VarDecl>(inst); + return VD && VD->isLocalVarDecl() && VD->getIdentifier() == II; + }); + + assert(it != FD->decls().end() && "Cannot find the instantiated variable."); + + Scope.InstantiatedLocal(decl, *it); + captureVarIfNeeded(cast<VarDecl>(*it)); + } +} + bool Sema::SetupConstraintScope( FunctionDecl *FD, std::optional<ArrayRef<TemplateArgument>> TemplateArgs, - MultiLevelTemplateArgumentList MLTAL, LocalInstantiationScope &Scope) { + MultiLevelTemplateArgumentList MLTAL, LocalInstantiationScope &Scope, + const bool shouldAddDeclsFromParentScope) { if (FD->isTemplateInstantiation() && FD->getPrimaryTemplate()) { FunctionTemplateDecl *PrimaryTemplate = FD->getPrimaryTemplate(); InstantiatingTemplate Inst( @@ -601,10 +657,14 @@ bool Sema::SetupConstraintScope( Scope, MLTAL)) return true; // Make sure the captures are also added to the instantiation scope. - if (isLambdaCallOperator(FD) && - addInstantiatedCapturesToScope(FD, FromMemTempl->getTemplatedDecl(), - Scope, MLTAL)) - return true; + if (isLambdaCallOperator(FD)) { + if (addInstantiatedCapturesToScope(FD, FromMemTempl->getTemplatedDecl(), + Scope, MLTAL)) + return true; + if (shouldAddDeclsFromParentScope) + addDeclsFromParentScope(*this, FD, FromMemTempl->getTemplatedDecl(), + Scope); + } } return false; @@ -631,9 +691,13 @@ bool Sema::SetupConstraintScope( return true; // Make sure the captures are also added to the instantiation scope. - if (isLambdaCallOperator(FD) && - addInstantiatedCapturesToScope(FD, InstantiatedFrom, Scope, MLTAL)) - return true; + if (isLambdaCallOperator(FD)) { + if (addInstantiatedCapturesToScope(FD, InstantiatedFrom, Scope, MLTAL)) + return true; + + if (shouldAddDeclsFromParentScope) + addDeclsFromParentScope(*this, FD, InstantiatedFrom, Scope); + } } return false; @@ -644,7 +708,7 @@ bool Sema::SetupConstraintScope( std::optional<MultiLevelTemplateArgumentList> Sema::SetupConstraintCheckingTemplateArgumentsAndScope( FunctionDecl *FD, std::optional<ArrayRef<TemplateArgument>> TemplateArgs, - LocalInstantiationScope &Scope) { + LocalInstantiationScope &Scope, const bool shouldAddDeclsFromParentScope) { MultiLevelTemplateArgumentList MLTAL; // Collect the list of template arguments relative to the 'primary' template. @@ -655,7 +719,8 @@ Sema::SetupConstraintCheckingTemplateArgumentsAndScope( /*RelativeToPrimary=*/true, /*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true); - if (SetupConstraintScope(FD, TemplateArgs, MLTAL, Scope)) + if (SetupConstraintScope(FD, TemplateArgs, MLTAL, Scope, + shouldAddDeclsFromParentScope)) return std::nullopt; return MLTAL; @@ -694,23 +759,10 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD, CtxToSave = CtxToSave->getNonTransparentContext(); } + const bool shouldAddDeclsFromParentScope = !CtxToSave->Encloses(CurContext); ContextRAII SavedContext{*this, CtxToSave}; LocalInstantiationScope Scope(*this, !ForOverloadResolution || isLambdaCallOperator(FD)); - std::optional<MultiLevelTemplateArgumentList> MLTAL = - SetupConstraintCheckingTemplateArgumentsAndScope( - const_cast<FunctionDecl *>(FD), {}, Scope); - - if (!MLTAL) - return true; - - Qualifiers ThisQuals; - CXXRecordDecl *Record = nullptr; - if (auto *Method = dyn_cast<CXXMethodDecl>(FD)) { - ThisQuals = Method->getMethodQualifiers(); - Record = const_cast<CXXRecordDecl *>(Method->getParent()); - } - CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr); // When checking the constraints of a lambda, we need to restore a // LambdaScopeInfo populated with correct capture information so that the type @@ -727,6 +779,22 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD, FuncScope.disable(); } + std::optional<MultiLevelTemplateArgumentList> MLTAL = + SetupConstraintCheckingTemplateArgumentsAndScope( + const_cast<FunctionDecl *>(FD), {}, Scope, + shouldAddDeclsFromParentScope); + + if (!MLTAL) + return true; + + Qualifiers ThisQuals; + CXXRecordDecl *Record = nullptr; + if (auto *Method = dyn_cast<CXXMethodDecl>(FD)) { + ThisQuals = Method->getMethodQualifiers(); + Record = const_cast<CXXRecordDecl *>(Method->getParent()); + } + CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr); + return CheckConstraintSatisfaction( FD, {FD->getTrailingRequiresClause()}, *MLTAL, SourceRange(UsageLoc.isValid() ? UsageLoc : FD->getLocation()), @@ -900,9 +968,17 @@ bool Sema::CheckInstantiatedFunctionTemplateConstraints( Sema::ContextRAII savedContext(*this, Decl); LocalInstantiationScope Scope(*this); + FunctionScopeRAII FuncScope(*this); + if (isLambdaCallOperator(Decl)) { + LambdaScopeInfo *LSI = RebuildLambdaScopeInfo(cast<CXXMethodDecl>(Decl)); + LSI->AfterParameterList = false; + } else { + FuncScope.disable(); + } + std::optional<MultiLevelTemplateArgumentList> MLTAL = - SetupConstraintCheckingTemplateArgumentsAndScope(Decl, TemplateArgs, - Scope); + SetupConstraintCheckingTemplateArgumentsAndScope( + Decl, TemplateArgs, Scope, /*shouldAddDeclsFromParentScope=*/true); if (!MLTAL) return true; @@ -914,14 +990,6 @@ bool Sema::CheckInstantiatedFunctionTemplateConstraints( Record = Method->getParent(); } CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr); - FunctionScopeRAII FuncScope(*this); - - if (isLambdaCallOperator(Decl)) { - LambdaScopeInfo *LSI = RebuildLambdaScopeInfo(cast<CXXMethodDecl>(Decl)); - LSI->AfterParameterList = false; - } else { - FuncScope.disable(); - } llvm::SmallVector<Expr *, 1> Converted; return CheckConstraintSatisfaction(Template, TemplateAC, Converted, *MLTAL, diff --git a/clang/test/SemaCXX/pr64462.cpp b/clang/test/SemaCXX/pr64462.cpp new file mode 100644 index 00000000000000..cc8b5510d1a823 --- /dev/null +++ b/clang/test/SemaCXX/pr64462.cpp @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -verify -std=c++20 -fsyntax-only %s + +auto c1(auto f, auto ...fs) { + constexpr bool a = true; + // expected-note@+2{{because substituted constraint expression is ill-formed: no matching function for call to 'c1'}} + // expected-note@+1{{candidate template ignored: constraints not satisfied [with auto:1 = bool}} + return [](auto) requires a && (c1(fs...)) {}; +} + +auto c2(auto f, auto ...fs) { + constexpr bool a = true; + // expected-note@+2{{because substituted constraint expression is ill-formed: no matching function for call to 'c2'}} + // expected-note@+1{{candidate function not viable: constraints not satisfied}} + return []() requires a && (c2(fs...)) {}; +} + +void foo() { + c1(true)(true); // expected-error {{no matching function for call to object of type}} + c2(true)(); // expected-error {{no matching function for call to object of type}} +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits