diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -7343,6 +7343,8 @@ CXXConversionDecl *Conv, Expr *Src); + sema::LambdaScopeInfo *RebuildLambdaScopeInfo(CXXMethodDecl *CallOperator); + /// Check whether the given expression is a valid constraint expression. /// A diagnostic is emitted if it is not, false is returned, and /// PossibleNonPrimary will be set to true if the failure might be due to a diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -13,12 +13,14 @@ #include "clang/Sema/SemaConcept.h" #include "TreeTransform.h" #include "clang/AST/ASTLambda.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/ExprConcepts.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Basic/OperatorPrecedence.h" #include "clang/Sema/EnterExpressionEvaluationContext.h" #include "clang/Sema/Initialization.h" #include "clang/Sema/Overload.h" +#include "clang/Sema/ScopeInfo.h" #include "clang/Sema/Sema.h" #include "clang/Sema/SemaDiagnostic.h" #include "clang/Sema/SemaInternal.h" @@ -540,11 +542,6 @@ auto AddSingleCapture = [&](const ValueDecl *CapturedPattern, unsigned Index) { ValueDecl *CapturedVar = LambdaClass->getCapture(Index)->getCapturedVar(); - if (cast(Function)->isConst()) { - QualType T = CapturedVar->getType(); - T.addConst(); - CapturedVar->setType(T); - } if (CapturedVar->isInitCapture()) Scope.InstantiatedLocal(CapturedPattern, CapturedVar); }; @@ -714,6 +711,22 @@ Record = const_cast(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 + // of a variable referring to a capture is correctly const-adjusted. + FunctionScopeRAII FuncScope(*this); + if (isLambdaCallOperator(FD)) { + LambdaScopeInfo *LSI = RebuildLambdaScopeInfo( + const_cast(cast(FD))); + // Constraints are checked from the parent context of the lambda, so we set + // AfterParameterList to false, so that `tryCaptureVariable` finds + // explicit captures in the appropriate context. + LSI->AfterParameterList = false; + } else { + FuncScope.disable(); + } + return CheckConstraintSatisfaction( FD, {FD->getTrailingRequiresClause()}, *MLTAL, SourceRange(UsageLoc.isValid() ? UsageLoc : FD->getLocation()), @@ -902,10 +915,13 @@ } CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr); FunctionScopeRAII FuncScope(*this); - if (isLambdaCallOperator(Decl)) - PushLambdaScope(); - else + + if (isLambdaCallOperator(Decl)) { + LambdaScopeInfo *LSI = RebuildLambdaScopeInfo(cast(Decl)); + LSI->AfterParameterList = false; + } else { FuncScope.disable(); + } llvm::SmallVector Converted; return CheckConstraintSatisfaction(Template, TemplateAC, Converted, *MLTAL, diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -15289,11 +15289,10 @@ FD->setInvalidDecl(); } -static void RebuildLambdaScopeInfo(CXXMethodDecl *CallOperator, - Sema &S) { - CXXRecordDecl *const LambdaClass = CallOperator->getParent(); +LambdaScopeInfo *Sema::RebuildLambdaScopeInfo(CXXMethodDecl *CallOperator) { + CXXRecordDecl *LambdaClass = CallOperator->getParent(); - LambdaScopeInfo *LSI = S.PushLambdaScope(); + LambdaScopeInfo *LSI = PushLambdaScope(); LSI->CallOperator = CallOperator; LSI->Lambda = LambdaClass; LSI->ReturnType = CallOperator->getReturnType(); @@ -15317,7 +15316,7 @@ if (C.capturesVariable()) { ValueDecl *VD = C.getCapturedVar(); if (VD->isInitCapture()) - S.CurrentInstantiationScope->InstantiatedLocal(VD, VD); + CurrentInstantiationScope->InstantiatedLocal(VD, VD); const bool ByRef = C.getCaptureKind() == LCK_ByRef; LSI->addCapture(VD, /*IsBlock*/false, ByRef, /*RefersToEnclosingVariableOrCapture*/true, C.getLocation(), @@ -15334,6 +15333,7 @@ } ++I; } + return LSI; } Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D, @@ -15437,7 +15437,7 @@ assert(inTemplateInstantiation() && "There should be an active template instantiation on the stack " "when instantiating a generic lambda!"); - RebuildLambdaScopeInfo(cast(D), *this); + RebuildLambdaScopeInfo(cast(D)); } else { // Enter a new function scope PushFunctionScope(); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -19722,13 +19722,6 @@ FunctionScopesIndex == MaxFunctionScopesIndex && VarDC == DC) return true; - // When evaluating some attributes (like enable_if) we might refer to a - // function parameter appertaining to the same declaration as that - // attribute. - if (const auto *Parm = dyn_cast(Var); - Parm && Parm->getDeclContext() == DC) - return true; - // Only block literals, captured statements, and lambda expressions can // capture; other scopes don't work. DeclContext *ParentDC = @@ -19756,6 +19749,14 @@ CSI->getCapture(Var).markUsed(BuildAndDiagnose); break; } + + // When evaluating some attributes (like enable_if) we might refer to a + // function parameter appertaining to the same declaration as that + // attribute. + if (const auto *Parm = dyn_cast(Var); + Parm && Parm->getDeclContext() == DC) + return true; + // If we are instantiating a generic lambda call operator body, // we do not want to capture new variables. What was captured // during either a lambdas transformation or initial parsing diff --git a/clang/test/SemaCXX/lambda-capture-type-deduction.cpp b/clang/test/SemaCXX/lambda-capture-type-deduction.cpp --- a/clang/test/SemaCXX/lambda-capture-type-deduction.cpp +++ b/clang/test/SemaCXX/lambda-capture-type-deduction.cpp @@ -246,3 +246,17 @@ static_assert(is_same); }; } + +namespace GH61267 { +template concept C = true; + +template +void f(int) { + int i; + [i](P) {}(0); + i = 4; +} + +void test() { f(0); } + +}