[clang] Implement Change scope of lambda trailing-return-type

Implement P2036R3.

Captured variables by copy (explicitely or not), are deduced
correctly at the point we know whether the lambda is mutable,
and ill-formed before that.

Up until now, the entire lambda declaration up to the start of the body would be parsed in the parent scope, such that capture would not be available to look up.

The scoping is changed to have an outer lambda scope, followed by the lambda prototype and body.

The lambda scope is necessary because there may be a template scope between the start of the lambda (to which we want to attach the captured variable) and the prototype scope.

We also need to introduce a declaration context to attach the captured variable to (and several parts of clang assume captures are handled from the call operator context), before we know the type of the call operator.

The order of operations is as follow:

* Parse the init capture in the lambda's parent scope

* Introduce a lambda scope

* Create the lambda class and call operator

* Add the init captures to the call operator context and the lambda scope. But the variables are not capured yet (because we don't know their type).
Instead, explicit  captures are stored in a temporary map that conserves the order of capture (for the purpose of having a stable order in the ast dumps).

* A flag is set on LambdaScopeInfo to indicate that we have not yet injected the captures.

* The parameters are parsed (in the parent context, as lambda mangling recurses in the parent context, we couldn't mangle a lambda that is attached to the context of a lambda whose type is not yet known).

* The lambda qualifiers are parsed, at this point We can switch (for the second time) inside the lambda context, unset the flag indicating that we have not parsed the lambda qualifiers,
record the lambda is mutable and capture the explicit variables.

* We can parse the rest of the lambda type, transform the lambda and call operator's types and also transform the call operator to a template function decl where necessary.

At this point, both captures and parameters can be injected in the body's scope. When trying to capture an implicit variable, if we are before the qualifiers of a lambda, we need to remember that the variables are still in the parent's context (rather than in the call operator's).

Reviewed By: aaron.ballman, #clang-language-wg, ChuanqiXu

Differential Revision: https://reviews.llvm.org/D119136
This commit is contained in:
Corentin Jabot 2022-02-06 22:58:43 +01:00
parent be5c15c7ae
commit 04000c2f92
19 changed files with 918 additions and 386 deletions

View File

@ -255,6 +255,9 @@ C++2b Feature Support
- Implemented `P2128R6: Multidimensional subscript operator <https://wg21.link/P2128R6>`_.
- Implemented `P0849R8: auto(x): decay-copy in the language <https://wg21.link/P0849R8>`_.
- Implemented `P2242R3: Non-literal variables (and labels and gotos) in constexpr functions <https://wg21.link/P2242R3>`_.
- Implemented `P2036R3: Change scope of lambda trailing-return-type <https://wg21.link/P2036R3>`_.
This proposal modifies how variables captured in lambdas can appear in trailing return type
expressions and how their types are deduced therein, in all C++ language versions.
CUDA Language Changes in Clang
------------------------------

View File

@ -1799,6 +1799,20 @@ public:
return getLambdaData().MethodTyInfo;
}
void setLambdaTypeInfo(TypeSourceInfo *TS) {
auto *DD = DefinitionData;
assert(DD && DD->IsLambda && "setting lambda property of non-lambda class");
auto &DL = static_cast<LambdaDefinitionData &>(*DD);
DL.MethodTyInfo = TS;
}
void setLambdaIsGeneric(bool IsGeneric) {
auto *DD = DefinitionData;
assert(DD && DD->IsLambda && "setting lambda property of non-lambda class");
auto &DL = static_cast<LambdaDefinitionData &>(*DD);
DL.IsGenericLambda = IsGeneric;
}
// Determine whether this type is an Interface Like type for
// __interface inheritance purposes.
bool isInterfaceLike() const;

View File

@ -7710,6 +7710,8 @@ let CategoryName = "Lambda Issue" in {
def err_lambda_impcap : Error<
"variable %0 cannot be implicitly captured in a lambda with no "
"capture-default specified">;
def err_lambda_used_before_capture: Error<
"captured variable %0 cannot appear here">;
def note_lambda_variable_capture_fixit : Note<
"capture %0 by %select{value|reference}1">;
def note_lambda_default_capture_fixit : Note<

View File

@ -1901,6 +1901,8 @@ private:
ParseLambdaIntroducer(LambdaIntroducer &Intro,
LambdaIntroducerTentativeParse *Tentative = nullptr);
ExprResult ParseLambdaExpressionAfterIntroducer(LambdaIntroducer &Intro);
void ParseLambdaLexedGNUAttributeArgs(LateParsedAttribute &LA,
ParsedAttributes &Attrs, Declarator &D);
//===--------------------------------------------------------------------===//
// C++ 5.2p1: C++ Casts

View File

@ -44,11 +44,11 @@ public:
enum ScopeFlags {
/// This indicates that the scope corresponds to a function, which
/// means that labels are set here.
FnScope = 0x01,
FnScope = 0x01,
/// This is a while, do, switch, for, etc that can have break
/// statements embedded into it.
BreakScope = 0x02,
BreakScope = 0x02,
/// This is a while, do, for, which can have continue statements
/// embedded into it.
@ -140,6 +140,12 @@ public:
/// parsed. If such a scope is a ContinueScope, it's invalid to jump to the
/// continue block from here.
ConditionVarScope = 0x2000000,
/// This is the scope for a lambda, after the lambda introducer.
/// Lambdas need 2 FunctionPrototypeScope scopes (because there is a
/// template scope in between), the outer scope does not increase the
/// depth of recursion.
LambdaScope = 0x4000000,
};
private:

View File

@ -831,6 +831,28 @@ public:
/// The lambda's compiler-generated \c operator().
CXXMethodDecl *CallOperator = nullptr;
struct DelayedCapture {
VarDecl *Var;
SourceLocation Loc;
LambdaCaptureKind Kind;
};
/// Holds the captures until we parsed the qualifiers, as the cv qualified
/// type of captures can only be computed at that point, and the captures
/// should not be visible before.
/// The index represents the position in the original capture list.
/// We use a map as not all index represents captures (defaults), or are
/// captured (some captures are invalid).
llvm::DenseMap<unsigned, DelayedCapture> DelayedCaptures;
/// Whether the current scope when parsing the lambda
/// is after the call operator qualifiers,
/// which is the point at which the captures are usable
/// per [expr.prim.id.unqual]/p3.2 and [expr.prim.lambda.capture]/6.
/// This is set to false by default as the lambda can be reconstructed during
/// instantiation
bool BeforeLambdaQualifiersScope = false;
/// Source range covering the lambda introducer [...].
SourceRange IntroducerRange;

View File

@ -6852,11 +6852,9 @@ public:
///
/// CodeGen handles emission of lambda captures, ignoring these dummy
/// variables appropriately.
VarDecl *createLambdaInitCaptureVarDecl(SourceLocation Loc,
QualType InitCaptureType,
SourceLocation EllipsisLoc,
IdentifierInfo *Id,
unsigned InitStyle, Expr *Init);
VarDecl *createLambdaInitCaptureVarDecl(
SourceLocation Loc, QualType InitCaptureType, SourceLocation EllipsisLoc,
IdentifierInfo *Id, unsigned InitStyle, Expr *Init, DeclContext *DeclCtx);
/// Add an init-capture to a lambda scope.
void addInitCapture(sema::LambdaScopeInfo *LSI, VarDecl *Var);
@ -6865,21 +6863,29 @@ public:
/// given lambda.
void finishLambdaExplicitCaptures(sema::LambdaScopeInfo *LSI);
/// \brief This is called after parsing the explicit template parameter list
/// Deduce a block or lambda's return type based on the return
/// statements present in the body.
void deduceClosureReturnType(sema::CapturingScopeInfo &CSI);
/// Once the Lambdas capture are known, we can
/// start to create the closure, call operator method,
/// and keep track of the captures.
/// We do the capture lookup here, but they are not actually captured
/// until after we know what the qualifiers of the call operator are.
void ActOnLambdaIntroducer(LambdaIntroducer &Intro, Scope *CurContext);
/// This is called after parsing the explicit template parameter list
/// on a lambda (if it exists) in C++2a.
void ActOnLambdaExplicitTemplateParameterList(SourceLocation LAngleLoc,
void ActOnLambdaExplicitTemplateParameterList(LambdaIntroducer &Intro,
SourceLocation LAngleLoc,
ArrayRef<NamedDecl *> TParams,
SourceLocation RAngleLoc,
ExprResult RequiresClause);
/// Introduce the lambda parameters into scope.
void addLambdaParameters(
ArrayRef<LambdaIntroducer::LambdaCapture> Captures,
CXXMethodDecl *CallOperator, Scope *CurScope);
/// Deduce a block or lambda's return type based on the return
/// statements present in the body.
void deduceClosureReturnType(sema::CapturingScopeInfo &CSI);
void ActOnLambdaClosureQualifiers(
LambdaIntroducer &Intro, SourceLocation MutableLoc, SourceLocation EndLoc,
MutableArrayRef<DeclaratorChunk::ParamInfo> ParamInfo,
const DeclSpec &DS);
/// ActOnStartOfLambdaDefinition - This is called just before we start
/// parsing the body of a lambda; it analyzes the explicit captures and

View File

@ -1231,6 +1231,36 @@ static void addConstevalToLambdaDeclSpecifier(Parser &P,
}
}
void Parser::ParseLambdaLexedGNUAttributeArgs(LateParsedAttribute &LA,
ParsedAttributes &Attrs,
Declarator &D) {
// Create a fake EOF so that attribute parsing won't go off the end of the
// attribute.
Token AttrEnd;
AttrEnd.startToken();
AttrEnd.setKind(tok::eof);
AttrEnd.setLocation(Tok.getLocation());
AttrEnd.setEofData(LA.Toks.data());
LA.Toks.push_back(AttrEnd);
// Append the current token at the end of the new token stream so that it
// doesn't get lost.
LA.Toks.push_back(Tok);
PP.EnterTokenStream(LA.Toks, true, /*IsReinject=*/true);
// Consume the previously pushed token.
ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true);
ParseGNUAttributeArgs(&LA.AttrName, LA.AttrNameLoc, Attrs, nullptr, nullptr,
SourceLocation(), ParsedAttr::AS_GNU, &D);
// After parsing attribute arguments, we've either reached the EOF token
// (signaling that parsing was successful) or we have tokens we need to
// consume until we reach the EOF.
while (Tok.isNot(tok::eof))
ConsumeAnyToken();
assert(Tok.is(tok::eof));
ConsumeAnyToken();
}
/// ParseLambdaExpressionAfterIntroducer - Parse the rest of a lambda
/// expression.
ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
@ -1250,9 +1280,15 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
DeclSpec DS(AttrFactory);
Declarator D(DS, DeclaratorContext::LambdaExpr);
TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth);
Actions.PushLambdaScope();
ParsedAttributes Attr(AttrFactory);
ParseScope LambdaScope(this, Scope::LambdaScope | Scope::DeclScope |
Scope::FunctionDeclarationScope |
Scope::FunctionPrototypeScope);
Actions.PushLambdaScope();
Actions.ActOnLambdaIntroducer(Intro, getCurScope());
ParsedAttributes Attributes(AttrFactory);
if (getLangOpts().CUDA) {
// In CUDA code, GNU attributes are allowed to appear immediately after the
// "[...]", even if there is no "(...)" before the lambda body.
@ -1263,7 +1299,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
// after '(...)'. nvcc doesn't accept this.
auto WarnIfHasCUDATargetAttr = [&] {
if (getLangOpts().CUDA)
for (const ParsedAttr &A : Attr)
for (const ParsedAttr &A : Attributes)
if (A.getKind() == ParsedAttr::AT_CUDADevice ||
A.getKind() == ParsedAttr::AT_CUDAHost ||
A.getKind() == ParsedAttr::AT_CUDAGlobal)
@ -1300,7 +1336,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
}
Actions.ActOnLambdaExplicitTemplateParameterList(
LAngleLoc, TemplateParams, RAngleLoc, RequiresClause);
Intro, LAngleLoc, TemplateParams, RAngleLoc, RequiresClause);
++CurTemplateDepthTracker;
}
}
@ -1318,28 +1354,39 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
TypeResult TrailingReturnType;
SourceLocation TrailingReturnTypeLoc;
SourceLocation LParenLoc, RParenLoc;
SourceLocation DeclEndLoc;
bool HasParentheses = false;
bool HasSpecifiers = false;
SourceLocation MutableLoc;
LateParsedAttrList LateParsedAttrs(true);
auto ParseConstexprAndMutableSpecifiers = [&] {
// GNU-style attributes must be parsed before the mutable specifier to
// be compatible with GCC. MSVC-style attributes must be parsed before
// the mutable specifier to be compatible with MSVC.
// However, because GNU attributes could refer to captured variables,
// which only become visible after the mutable keyword is parsed
// we delay the parsing of gnu attributes - by reusing the mechanism used
// for C++ late method parsing. Note, __declspec attributes do not make
// use of late parsing (expressions cannot appear in __declspec arguments),
// so only GNU style attributes are affected here.
MaybeParseAttributes(PAKM_GNU | PAKM_Declspec, Attributes,
&LateParsedAttrs);
// Parse mutable-opt and/or constexpr-opt or consteval-opt, and update
// the DeclEndLoc.
SourceLocation ConstexprLoc;
SourceLocation ConstevalLoc;
tryConsumeLambdaSpecifierToken(*this, MutableLoc, ConstexprLoc,
ConstevalLoc, DeclEndLoc);
addConstexprToLambdaDeclSpecifier(*this, ConstexprLoc, DS);
addConstevalToLambdaDeclSpecifier(*this, ConstevalLoc, DS);
};
auto ParseLambdaSpecifiers =
[&](SourceLocation LParenLoc, SourceLocation RParenLoc,
MutableArrayRef<DeclaratorChunk::ParamInfo> ParamInfo,
[&](MutableArrayRef<DeclaratorChunk::ParamInfo> ParamInfo,
SourceLocation EllipsisLoc) {
SourceLocation DeclEndLoc = RParenLoc;
// GNU-style attributes must be parsed before the mutable specifier to
// be compatible with GCC. MSVC-style attributes must be parsed before
// the mutable specifier to be compatible with MSVC.
MaybeParseAttributes(PAKM_GNU | PAKM_Declspec, Attr);
// Parse mutable-opt and/or constexpr-opt or consteval-opt, and update
// the DeclEndLoc.
SourceLocation MutableLoc;
SourceLocation ConstexprLoc;
SourceLocation ConstevalLoc;
tryConsumeLambdaSpecifierToken(*this, MutableLoc, ConstexprLoc,
ConstevalLoc, DeclEndLoc);
addConstexprToLambdaDeclSpecifier(*this, ConstexprLoc, DS);
addConstevalToLambdaDeclSpecifier(*this, ConstevalLoc, DS);
// Parse exception-specification[opt].
ExceptionSpecificationType ESpecType = EST_None;
SourceRange ESpecRange;
@ -1347,6 +1394,15 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
SmallVector<SourceRange, 2> DynamicExceptionRanges;
ExprResult NoexceptExpr;
CachedTokens *ExceptionSpecTokens;
// At this point we know whether the lambda is mutable so we can
// complete the parsing of gnu attributes.
for (LateParsedAttribute *Attr : LateParsedAttrs) {
ParseLambdaLexedGNUAttributeArgs(*Attr, Attributes, D);
delete Attr;
}
LateParsedAttrs.clear();
ESpecType = tryParseExceptionSpecification(
/*Delayed=*/false, ESpecRange, DynamicExceptions,
DynamicExceptionRanges, NoexceptExpr, ExceptionSpecTokens);
@ -1355,8 +1411,8 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
DeclEndLoc = ESpecRange.getEnd();
// Parse attribute-specifier[opt].
if (MaybeParseCXX11Attributes(Attr))
DeclEndLoc = Attr.Range.getEnd();
if (MaybeParseCXX11Attributes(Attributes))
DeclEndLoc = Attributes.Range.getEnd();
// Parse OpenCL addr space attribute.
if (Tok.isOneOf(tok::kw___private, tok::kw___global, tok::kw___local,
@ -1392,27 +1448,29 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
/*ExceptionSpecTokens*/ nullptr,
/*DeclsInPrototype=*/None, LParenLoc, FunLocalRangeEnd, D,
TrailingReturnType, TrailingReturnTypeLoc, &DS),
std::move(Attr), DeclEndLoc);
std::move(Attributes), DeclEndLoc);
if (HasParentheses && Tok.is(tok::kw_requires))
ParseTrailingRequiresClause(D);
};
if (Tok.is(tok::l_paren)) {
ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope |
Scope::FunctionDeclarationScope |
Scope::DeclScope);
ParseScope Prototype(this, Scope::FunctionPrototypeScope |
Scope::FunctionDeclarationScope |
Scope::DeclScope);
// Parse parameter-declaration-clause.
SmallVector<DeclaratorChunk::ParamInfo, 16> ParamInfo;
SourceLocation EllipsisLoc;
if (Tok.is(tok::l_paren)) {
BalancedDelimiterTracker T(*this, tok::l_paren);
T.consumeOpen();
SourceLocation LParenLoc = T.getOpenLocation();
// Parse parameter-declaration-clause.
SmallVector<DeclaratorChunk::ParamInfo, 16> ParamInfo;
SourceLocation EllipsisLoc;
LParenLoc = T.getOpenLocation();
if (Tok.isNot(tok::r_paren)) {
Actions.RecordParsingTemplateParameterDepth(
CurTemplateDepthTracker.getOriginalDepth());
ParseParameterDeclarationClause(D.getContext(), Attr, ParamInfo,
ParseParameterDeclarationClause(D.getContext(), Attributes, ParamInfo,
EllipsisLoc);
// For a generic lambda, each 'auto' within the parameter declaration
// clause creates a template type parameter, so increment the depth.
@ -1424,36 +1482,40 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
}
T.consumeClose();
DeclEndLoc = RParenLoc = T.getCloseLocation();
HasParentheses = true;
}
// Parse lambda-specifiers.
ParseLambdaSpecifiers(LParenLoc, /*DeclEndLoc=*/T.getCloseLocation(),
ParamInfo, EllipsisLoc);
// Parse requires-clause[opt].
if (Tok.is(tok::kw_requires))
ParseTrailingRequiresClause(D);
} else if (Tok.isOneOf(tok::kw_mutable, tok::arrow, tok::kw___attribute,
tok::kw_constexpr, tok::kw_consteval,
tok::kw___private, tok::kw___global, tok::kw___local,
tok::kw___constant, tok::kw___generic,
tok::kw_requires, tok::kw_noexcept) ||
(Tok.is(tok::l_square) && NextToken().is(tok::l_square))) {
if (!getLangOpts().CPlusPlus2b)
// MSVC doesn't support [] __declspec(...) {}, so we do not check for it here.
if (Tok.isOneOf(tok::kw_mutable, tok::arrow, tok::kw___attribute,
tok::kw_constexpr, tok::kw_consteval, tok::kw___private,
tok::kw___global, tok::kw___local, tok::kw___constant,
tok::kw___generic, tok::kw_requires, tok::kw_noexcept) ||
(Tok.is(tok::l_square) && NextToken().is(tok::l_square))) {
HasSpecifiers = true;
if (!HasParentheses && !getLangOpts().CPlusPlus2b) {
// It's common to forget that one needs '()' before 'mutable', an
// attribute specifier, the result type, or the requires clause. Deal with
// this.
Diag(Tok, diag::ext_lambda_missing_parens)
<< FixItHint::CreateInsertion(Tok.getLocation(), "() ");
SourceLocation NoLoc;
// Parse lambda-specifiers.
std::vector<DeclaratorChunk::ParamInfo> EmptyParamInfo;
ParseLambdaSpecifiers(/*LParenLoc=*/NoLoc, /*RParenLoc=*/NoLoc,
EmptyParamInfo, /*EllipsisLoc=*/NoLoc);
}
}
if (HasParentheses || HasSpecifiers) {
ParseConstexprAndMutableSpecifiers();
}
Actions.ActOnLambdaClosureQualifiers(Intro, MutableLoc, DeclEndLoc, ParamInfo,
DS);
if (HasSpecifiers || HasParentheses || !LateParsedAttrs.empty())
ParseLambdaSpecifiers(ParamInfo, EllipsisLoc);
WarnIfHasCUDATargetAttr();
Prototype.Exit();
// FIXME: Rename BlockScope -> ClosureScope if we decide to continue using
// it.
unsigned ScopeFlags = Scope::BlockScope | Scope::FnScope | Scope::DeclScope |
@ -1472,6 +1534,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
StmtResult Stmt(ParseCompoundStatementBody());
BodyScope.Exit();
TemplateParamScope.Exit();
LambdaScope.Exit();
if (!Stmt.isInvalid() && !TrailingReturnType.isInvalid())
return Actions.ActOnLambdaExpr(LambdaBeginLoc, Stmt.get(), getCurScope());

View File

@ -67,8 +67,10 @@ void Scope::setFlags(Scope *parent, unsigned flags) {
if (flags & BlockScope) BlockParent = this;
if (flags & TemplateParamScope) TemplateParamParent = this;
// If this is a prototype scope, record that.
if (flags & FunctionPrototypeScope) PrototypeDepth++;
// If this is a prototype scope, record that. Lambdas have an extra prototype
// scope that doesn't add any depth.
if (flags & FunctionPrototypeScope && !(flags & LambdaScope))
PrototypeDepth++;
if (flags & DeclScope) {
if (flags & FunctionPrototypeScope)

View File

@ -2297,7 +2297,8 @@ FunctionScopeInfo *Sema::getEnclosingFunction() const {
LambdaScopeInfo *Sema::getEnclosingLambda() const {
for (auto *Scope : llvm::reverse(FunctionScopes)) {
if (auto *LSI = dyn_cast<sema::LambdaScopeInfo>(Scope)) {
if (LSI->Lambda && !LSI->Lambda->Encloses(CurContext)) {
if (LSI->Lambda && !LSI->Lambda->Encloses(CurContext) &&
!LSI->BeforeLambdaQualifiersScope) {
// We have switched contexts due to template instantiation.
// FIXME: We should swap out the FunctionScopes during code synthesis
// so that we don't need to check for this.
@ -2323,8 +2324,9 @@ LambdaScopeInfo *Sema::getCurLambda(bool IgnoreNonLambdaCapturingScope) {
return nullptr;
}
auto *CurLSI = dyn_cast<LambdaScopeInfo>(*I);
if (CurLSI && CurLSI->Lambda &&
!CurLSI->Lambda->Encloses(CurContext)) {
if (CurLSI && CurLSI->Lambda && CurLSI->CallOperator &&
!CurLSI->Lambda->Encloses(CurContext) &&
!CurLSI->BeforeLambdaQualifiersScope) {
// We have switched contexts due to template instantiation.
assert(!CodeSynthesisContexts.empty());
return nullptr;

View File

@ -292,6 +292,11 @@ bool Sema::ActOnCXXGlobalScopeSpecifier(SourceLocation CCLoc,
bool Sema::ActOnSuperScopeSpecifier(SourceLocation SuperLoc,
SourceLocation ColonColonLoc,
CXXScopeSpec &SS) {
if (getCurLambda()) {
Diag(SuperLoc, diag::err_super_in_lambda_unsupported);
return true;
}
CXXRecordDecl *RD = nullptr;
for (Scope *S = getCurScope(); S; S = S->getParent()) {
if (S->isFunctionScope()) {
@ -308,9 +313,6 @@ bool Sema::ActOnSuperScopeSpecifier(SourceLocation SuperLoc,
if (!RD) {
Diag(SuperLoc, diag::err_invalid_super_scope);
return true;
} else if (RD->isLambda()) {
Diag(SuperLoc, diag::err_super_in_lambda_unsupported);
return true;
} else if (RD->getNumBases() == 0) {
Diag(SuperLoc, diag::err_no_base_classes) << RD->getName();
return true;

View File

@ -3385,7 +3385,7 @@ ExprResult Sema::BuildDeclarationNameExpr(
// FIXME: Support lambda-capture of BindingDecls, once CWG actually
// decides how that's supposed to work.
auto *BD = cast<BindingDecl>(VD);
if (BD->getDeclContext() != CurContext) {
if (BD->getDeclContext() != CurContext && !isUnevaluatedContext()) {
auto *DD = dyn_cast_or_null<VarDecl>(BD->getDecomposedDecl());
if (DD && DD->hasLocalStorage())
diagnoseUncapturableValueReference(*this, Loc, BD);
@ -18542,6 +18542,37 @@ static void buildLambdaCaptureFixit(Sema &Sema, LambdaScopeInfo *LSI,
}
}
static bool CheckCaptureUseBeforeLambdaQualifiers(Sema &S, VarDecl *Var,
SourceLocation ExprLoc,
LambdaScopeInfo *LSI) {
if (Var->isInvalidDecl())
return false;
bool ByCopy = LSI->ImpCaptureStyle == LambdaScopeInfo::ImpCap_LambdaByval;
SourceLocation Loc = LSI->IntroducerRange.getBegin();
bool Explicitly = false;
for (auto &&C : LSI->DelayedCaptures) {
VarDecl *CV = C.second.Var;
if (Var != CV)
continue;
ByCopy = C.second.Kind == LambdaCaptureKind::LCK_ByCopy;
Loc = C.second.Loc;
Explicitly = true;
break;
}
if (ByCopy && LSI->BeforeLambdaQualifiersScope) {
// This can only occur in a non-ODR context, so we need to diagnose eagerly,
// even when BuildAndDiagnose is false
S.Diag(ExprLoc, diag::err_lambda_used_before_capture) << Var;
S.Diag(Loc, diag::note_var_explicitly_captured_here) << Var << Explicitly;
if (!Var->isInitCapture())
S.Diag(Var->getBeginLoc(), diag::note_entity_declared_at) << Var;
Var->setInvalidDecl();
return false;
}
return true;
}
bool Sema::tryCaptureVariable(
VarDecl *Var, SourceLocation ExprLoc, TryCaptureKind Kind,
SourceLocation EllipsisLoc, bool BuildAndDiagnose, QualType &CaptureType,
@ -18565,11 +18596,6 @@ bool Sema::tryCaptureVariable(
}
}
// If the variable is declared in the current context, there is no need to
// capture it.
if (VarDC == DC) return true;
// Capture global variables if it is required to use private copy of this
// variable.
bool IsGlobal = !Var->hasLocalStorage();
@ -18592,13 +18618,36 @@ bool Sema::tryCaptureVariable(
bool Nested = false;
bool Explicit = (Kind != TryCapture_Implicit);
unsigned FunctionScopesIndex = MaxFunctionScopesIndex;
bool IsInLambdaBeforeQualifiers;
do {
IsInLambdaBeforeQualifiers = false;
LambdaScopeInfo *LSI = nullptr;
if (!FunctionScopes.empty())
LSI = dyn_cast_or_null<LambdaScopeInfo>(
FunctionScopes[FunctionScopesIndex]);
if (LSI && LSI->BeforeLambdaQualifiersScope) {
if (isa<ParmVarDecl>(Var))
return true;
IsInLambdaBeforeQualifiers = true;
if (!CheckCaptureUseBeforeLambdaQualifiers(*this, Var, ExprLoc, LSI)) {
break;
}
}
// If the variable is declared in the current context, there is no need to
// capture it.
if (!IsInLambdaBeforeQualifiers &&
FunctionScopesIndex == MaxFunctionScopesIndex && VarDC == DC)
return true;
// Only block literals, captured statements, and lambda expressions can
// capture; other scopes don't work.
DeclContext *ParentDC = getParentOfCapturingContextOrNull(DC, Var,
ExprLoc,
BuildAndDiagnose,
*this);
DeclContext *ParentDC =
IsInLambdaBeforeQualifiers
? DC->getParent()
: getParentOfCapturingContextOrNull(DC, Var, ExprLoc,
BuildAndDiagnose, *this);
// We need to check for the parent *first* because, if we *have*
// private-captured a global variable, we need to recursively capture it in
// intermediate blocks, lambdas, etc.
@ -18613,9 +18662,9 @@ bool Sema::tryCaptureVariable(
FunctionScopeInfo *FSI = FunctionScopes[FunctionScopesIndex];
CapturingScopeInfo *CSI = cast<CapturingScopeInfo>(FSI);
// Check whether we've already captured it.
if (isVariableAlreadyCapturedInScopeInfo(CSI, Var, Nested, CaptureType,
if (!IsInLambdaBeforeQualifiers &&
isVariableAlreadyCapturedInScopeInfo(CSI, Var, Nested, CaptureType,
DeclRefType)) {
CSI->getCapture(Var).markUsed(BuildAndDiagnose);
break;
@ -18624,7 +18673,8 @@ bool Sema::tryCaptureVariable(
// we do not want to capture new variables. What was captured
// during either a lambdas transformation or initial parsing
// should be used.
if (isGenericLambdaCallOperatorSpecialization(DC)) {
if (!IsInLambdaBeforeQualifiers &&
isGenericLambdaCallOperatorSpecialization(DC)) {
if (BuildAndDiagnose) {
LambdaScopeInfo *LSI = cast<LambdaScopeInfo>(CSI);
if (LSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_None) {
@ -18639,7 +18689,8 @@ bool Sema::tryCaptureVariable(
}
// Try to capture variable-length arrays types.
if (Var->getType()->isVariablyModifiedType()) {
if (!IsInLambdaBeforeQualifiers &&
Var->getType()->isVariablyModifiedType()) {
// We're going to walk down into the type and look for VLA
// expressions.
QualType QTy = Var->getType();
@ -18648,7 +18699,7 @@ bool Sema::tryCaptureVariable(
captureVariablyModifiedType(Context, QTy, CSI);
}
if (getLangOpts().OpenMP) {
if (!IsInLambdaBeforeQualifiers && getLangOpts().OpenMP) {
if (auto *RSI = dyn_cast<CapturedRegionScopeInfo>(CSI)) {
// OpenMP private variables should not be captured in outer scope, so
// just break here. Similarly, global variables that are captured in a
@ -18729,11 +18780,11 @@ bool Sema::tryCaptureVariable(
}
return true;
}
FunctionScopesIndex--;
DC = ParentDC;
Explicit = false;
} while (!VarDC->Equals(DC));
FunctionScopesIndex--;
if (!IsInLambdaBeforeQualifiers)
DC = ParentDC;
} while (IsInLambdaBeforeQualifiers || !VarDC->Equals(DC));
// Walk back down the scope stack, (e.g. from outer lambda to inner lambda)
// computing the type of the capture at each step, checking type-specific
@ -18768,6 +18819,9 @@ bool Sema::tryCaptureVariable(
Nested = true;
} else {
LambdaScopeInfo *LSI = cast<LambdaScopeInfo>(CSI);
if (!CheckCaptureUseBeforeLambdaQualifiers(*this, Var, ExprLoc, LSI)) {
return true;
}
Invalid =
!captureInLambda(LSI, Var, ExprLoc, BuildAndDiagnose, CaptureType,
DeclRefType, Nested, Kind, EllipsisLoc,

View File

@ -1130,7 +1130,7 @@ static QualType adjustCVQualifiersForCXXThisWithinLambda(
if (C.isCopyCapture()) {
ClassType.removeLocalCVRQualifiers(Qualifiers::CVRMask);
if (CurLSI->CallOperator->isConst())
if (!CurLSI->Mutable)
ClassType.addConst();
return ASTCtx.getPointerType(ClassType);
}

View File

@ -245,8 +245,9 @@ Sema::createLambdaClosureType(SourceRange IntroducerRange, TypeSourceInfo *Info,
DeclContext *DC = CurContext;
while (!(DC->isFunctionOrMethod() || DC->isRecord() || DC->isFileContext()))
DC = DC->getParent();
bool IsGenericLambda = getGenericLambdaTemplateParameterList(getCurLambda(),
*this);
bool IsGenericLambda =
Info && getGenericLambdaTemplateParameterList(getCurLambda(), *this);
// Start constructing the lambda class.
CXXRecordDecl *Class = CXXRecordDecl::CreateLambda(
Context, DC, Info, IntroducerRange.getBegin(), LambdaDependencyKind,
@ -354,16 +355,13 @@ Sema::getCurrentMangleNumberContext(const DeclContext *DC) {
llvm_unreachable("unexpected context");
}
CXXMethodDecl *Sema::startLambdaDefinition(CXXRecordDecl *Class,
SourceRange IntroducerRange,
TypeSourceInfo *MethodTypeInfo,
SourceLocation EndLoc,
ArrayRef<ParmVarDecl *> Params,
ConstexprSpecKind ConstexprKind,
Expr *TrailingRequiresClause) {
static QualType
buildTypeForLambdaCallOperator(Sema &S, clang::CXXRecordDecl *Class,
TemplateParameterList *TemplateParams,
TypeSourceInfo *MethodTypeInfo) {
assert(MethodTypeInfo && "expected a non null type");
QualType MethodType = MethodTypeInfo->getType();
TemplateParameterList *TemplateParams =
getGenericLambdaTemplateParameterList(getCurLambda(), *this);
// If a lambda appears in a dependent context or is a generic lambda (has
// template parameters) and has an 'auto' return type, deduce it to a
// dependent type.
@ -371,19 +369,43 @@ CXXMethodDecl *Sema::startLambdaDefinition(CXXRecordDecl *Class,
const FunctionProtoType *FPT = MethodType->castAs<FunctionProtoType>();
QualType Result = FPT->getReturnType();
if (Result->isUndeducedType()) {
Result = SubstAutoTypeDependent(Result);
MethodType = Context.getFunctionType(Result, FPT->getParamTypes(),
FPT->getExtProtoInfo());
Result = S.SubstAutoTypeDependent(Result);
MethodType = S.Context.getFunctionType(Result, FPT->getParamTypes(),
FPT->getExtProtoInfo());
}
}
return MethodType;
}
/// Start the definition of a lambda expression.
/// In this overload, we do not know the type yet
CXXMethodDecl *Sema::startLambdaDefinition(CXXRecordDecl *Class,
SourceRange IntroducerRange,
TypeSourceInfo *MethodTypeInfo,
SourceLocation EndLoc,
ArrayRef<ParmVarDecl *> Params,
ConstexprSpecKind ConstexprKind,
Expr *TrailingRequiresClause) {
LambdaScopeInfo *LSI = getCurLambda();
TemplateParameterList *TemplateParams =
getGenericLambdaTemplateParameterList(LSI, *this);
// At this point, we may not know the type of the lambda, if we have not
// parsed a trailing return type yet
QualType MethodType = MethodTypeInfo
? buildTypeForLambdaCallOperator(
*this, Class, TemplateParams, MethodTypeInfo)
: QualType();
// C++11 [expr.prim.lambda]p5:
// The closure type for a lambda-expression has a public inline function
// call operator (13.5.4) whose parameters and return type are described by
// the lambda-expression's parameter-declaration-clause and
// call operator (13.5.4) whose parameters and return type are described
// by the lambda-expression's parameter-declaration-clause and
// trailing-return-type respectively.
DeclarationName MethodName
= Context.DeclarationNames.getCXXOperatorName(OO_Call);
DeclarationName MethodName =
Context.DeclarationNames.getCXXOperatorName(OO_Call);
DeclarationNameLoc MethodNameLoc =
DeclarationNameLoc::makeCXXOperatorNameLoc(IntroducerRange);
CXXMethodDecl *Method = CXXMethodDecl::Create(
@ -400,11 +422,11 @@ CXXMethodDecl *Sema::startLambdaDefinition(CXXRecordDecl *Class,
// context, so that the Scope stack matches the lexical nesting.
Method->setLexicalDeclContext(CurContext);
// Create a function template if we have a template parameter list
FunctionTemplateDecl *const TemplateMethod = TemplateParams ?
FunctionTemplateDecl::Create(Context, Class,
Method->getLocation(), MethodName,
TemplateParams,
Method) : nullptr;
FunctionTemplateDecl *const TemplateMethod =
TemplateParams
? FunctionTemplateDecl::Create(Context, Class, Method->getLocation(),
MethodName, TemplateParams, Method)
: nullptr;
if (TemplateMethod) {
TemplateMethod->setAccess(AS_public);
Method->setDescribedFunctionTemplate(TemplateMethod);
@ -480,14 +502,27 @@ void Sema::handleLambdaNumbering(
}
}
void Sema::buildLambdaScope(LambdaScopeInfo *LSI,
CXXMethodDecl *CallOperator,
SourceRange IntroducerRange,
LambdaCaptureDefault CaptureDefault,
SourceLocation CaptureDefaultLoc,
bool ExplicitParams,
bool ExplicitResultType,
bool Mutable) {
static void buildLambdaScopeReturnType(Sema &S, LambdaScopeInfo *LSI,
CXXMethodDecl *CallOperator,
bool ExplicitResultType) {
if (ExplicitResultType) {
LSI->HasImplicitReturnType = false;
LSI->ReturnType = CallOperator->getReturnType();
if (!LSI->ReturnType->isDependentType() && !LSI->ReturnType->isVoidType()) {
S.RequireCompleteType(CallOperator->getBeginLoc(), LSI->ReturnType,
diag::err_lambda_incomplete_result);
}
} else {
LSI->HasImplicitReturnType = true;
}
}
static void buildLambdaScopeCaptures(LambdaScopeInfo *LSI,
CXXMethodDecl *CallOperator,
SourceRange IntroducerRange,
LambdaCaptureDefault CaptureDefault,
SourceLocation CaptureDefaultLoc,
bool ExplicitParams, bool Mutable) {
LSI->CallOperator = CallOperator;
CXXRecordDecl *LambdaClass = CallOperator->getParent();
LSI->Lambda = LambdaClass;
@ -499,30 +534,27 @@ void Sema::buildLambdaScope(LambdaScopeInfo *LSI,
LSI->IntroducerRange = IntroducerRange;
LSI->ExplicitParams = ExplicitParams;
LSI->Mutable = Mutable;
}
if (ExplicitResultType) {
LSI->ReturnType = CallOperator->getReturnType();
if (!LSI->ReturnType->isDependentType() &&
!LSI->ReturnType->isVoidType()) {
if (RequireCompleteType(CallOperator->getBeginLoc(), LSI->ReturnType,
diag::err_lambda_incomplete_result)) {
// Do nothing.
}
}
} else {
LSI->HasImplicitReturnType = true;
}
void Sema::buildLambdaScope(LambdaScopeInfo *LSI, CXXMethodDecl *CallOperator,
SourceRange IntroducerRange,
LambdaCaptureDefault CaptureDefault,
SourceLocation CaptureDefaultLoc,
bool ExplicitParams, bool ExplicitResultType,
bool Mutable) {
buildLambdaScopeCaptures(LSI, CallOperator, IntroducerRange, CaptureDefault,
CaptureDefaultLoc, ExplicitParams, Mutable);
buildLambdaScopeReturnType(*this, LSI, CallOperator, ExplicitResultType);
}
void Sema::finishLambdaExplicitCaptures(LambdaScopeInfo *LSI) {
LSI->finishedExplicitCaptures();
}
void Sema::ActOnLambdaExplicitTemplateParameterList(SourceLocation LAngleLoc,
ArrayRef<NamedDecl *> TParams,
SourceLocation RAngleLoc,
ExprResult RequiresClause) {
void Sema::ActOnLambdaExplicitTemplateParameterList(
LambdaIntroducer &Intro, SourceLocation LAngleLoc,
ArrayRef<NamedDecl *> TParams, SourceLocation RAngleLoc,
ExprResult RequiresClause) {
LambdaScopeInfo *LSI = getCurLambda();
assert(LSI && "Expected a lambda scope");
assert(LSI->NumExplicitTemplateParams == 0 &&
@ -538,35 +570,6 @@ void Sema::ActOnLambdaExplicitTemplateParameterList(SourceLocation LAngleLoc,
LSI->RequiresClause = RequiresClause;
}
void Sema::addLambdaParameters(
ArrayRef<LambdaIntroducer::LambdaCapture> Captures,
CXXMethodDecl *CallOperator, Scope *CurScope) {
// Introduce our parameters into the function scope
for (unsigned p = 0, NumParams = CallOperator->getNumParams();
p < NumParams; ++p) {
ParmVarDecl *Param = CallOperator->getParamDecl(p);
// If this has an identifier, add it to the scope stack.
if (CurScope && Param->getIdentifier()) {
bool Error = false;
// Resolution of CWG 2211 in C++17 renders shadowing ill-formed, but we
// retroactively apply it.
for (const auto &Capture : Captures) {
if (Capture.Id == Param->getIdentifier()) {
Error = true;
Diag(Param->getLocation(), diag::err_parameter_shadow_capture);
Diag(Capture.Loc, diag::note_var_explicitly_captured_here)
<< Capture.Id << true;
}
}
if (!Error)
CheckShadow(CurScope, Param);
PushOnScopeChains(Param, CurScope);
}
}
}
/// If this expression is an enumerator-like expression of some type
/// T, return the type T; otherwise, return null.
///
@ -853,11 +856,9 @@ QualType Sema::buildLambdaInitCaptureInitialization(
return DeducedType;
}
VarDecl *Sema::createLambdaInitCaptureVarDecl(SourceLocation Loc,
QualType InitCaptureType,
SourceLocation EllipsisLoc,
IdentifierInfo *Id,
unsigned InitStyle, Expr *Init) {
VarDecl *Sema::createLambdaInitCaptureVarDecl(
SourceLocation Loc, QualType InitCaptureType, SourceLocation EllipsisLoc,
IdentifierInfo *Id, unsigned InitStyle, Expr *Init, DeclContext *DeclCtx) {
// FIXME: Retain the TypeSourceInfo from buildLambdaInitCaptureInitialization
// rather than reconstructing it here.
TypeSourceInfo *TSI = Context.getTrivialTypeSourceInfo(InitCaptureType, Loc);
@ -868,8 +869,8 @@ VarDecl *Sema::createLambdaInitCaptureVarDecl(SourceLocation Loc,
// used as a variable, and only exists as a way to name and refer to the
// init-capture.
// FIXME: Pass in separate source locations for '&' and identifier.
VarDecl *NewVD = VarDecl::Create(Context, CurContext, Loc,
Loc, Id, InitCaptureType, TSI, SC_Auto);
VarDecl *NewVD = VarDecl::Create(Context, DeclCtx, Loc, Loc, Id,
InitCaptureType, TSI, SC_Auto);
NewVD->setInitCapture(true);
NewVD->setReferenced(true);
// FIXME: Pass in a VarDecl::InitializationStyle.
@ -888,12 +889,107 @@ void Sema::addInitCapture(LambdaScopeInfo *LSI, VarDecl *Var) {
Var->getType(), /*Invalid*/false);
}
void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
Declarator &ParamInfo,
Scope *CurScope) {
// Unlike getCurLambda, getCurrentLambdaScopeUnsafe doesn't
// check that the current lambda is in a consistent or fully constructed state.
static LambdaScopeInfo *getCurrentLambdaScopeUnsafe(Sema &S) {
assert(!S.FunctionScopes.empty());
return cast<LambdaScopeInfo>(S.FunctionScopes[S.FunctionScopes.size() - 1]);
}
static TypeSourceInfo *
getDummyLambdaType(Sema &S, SourceLocation Loc = SourceLocation()) {
// C++11 [expr.prim.lambda]p4:
// If a lambda-expression does not include a lambda-declarator, it is as
// if the lambda-declarator were ().
FunctionProtoType::ExtProtoInfo EPI(S.Context.getDefaultCallingConvention(
/*IsVariadic=*/false, /*IsCXXMethod=*/true));
EPI.HasTrailingReturn = true;
EPI.TypeQuals.addConst();
LangAS AS = S.getDefaultCXXMethodAddrSpace();
if (AS != LangAS::Default)
EPI.TypeQuals.addAddressSpace(AS);
// C++1y [expr.prim.lambda]:
// The lambda return type is 'auto', which is replaced by the
// trailing-return type if provided and/or deduced from 'return'
// statements
// We don't do this before C++1y, because we don't support deduced return
// types there.
QualType DefaultTypeForNoTrailingReturn = S.getLangOpts().CPlusPlus14
? S.Context.getAutoDeductType()
: S.Context.DependentTy;
QualType MethodTy =
S.Context.getFunctionType(DefaultTypeForNoTrailingReturn, None, EPI);
return S.Context.getTrivialTypeSourceInfo(MethodTy, Loc);
}
static TypeSourceInfo *getLambdaType(Sema &S, LambdaIntroducer &Intro,
Declarator &ParamInfo, Scope *CurScope,
SourceLocation Loc,
bool &ExplicitResultType) {
ExplicitResultType = false;
TypeSourceInfo *MethodTyInfo;
if (ParamInfo.getNumTypeObjects() == 0) {
MethodTyInfo = getDummyLambdaType(S, Loc);
} else {
DeclaratorChunk::FunctionTypeInfo &FTI = ParamInfo.getFunctionTypeInfo();
ExplicitResultType = FTI.hasTrailingReturnType();
if (!FTI.hasMutableQualifier()) {
FTI.getOrCreateMethodQualifiers().SetTypeQual(DeclSpec::TQ_const, Loc);
}
MethodTyInfo = S.GetTypeForDeclarator(ParamInfo, CurScope);
assert(MethodTyInfo && "no type from lambda-declarator");
// Check for unexpanded parameter packs in the method type.
if (MethodTyInfo->getType()->containsUnexpandedParameterPack())
S.DiagnoseUnexpandedParameterPack(Intro.Range.getBegin(), MethodTyInfo,
S.UPPC_DeclarationType);
}
return MethodTyInfo;
}
static CXXMethodDecl *CreateMethod(Sema &S, SourceRange IntroducerRange,
CXXRecordDecl *Class) {
// C++11 [expr.prim.lambda]p5:
// The closure type for a lambda-expression has a public inline function
// call operator (13.5.4) whose parameters and return type are described
// by the lambda-expression's parameter-declaration-clause and
// trailing-return-type respectively.
DeclarationName MethodName =
S.Context.DeclarationNames.getCXXOperatorName(OO_Call);
DeclarationNameLoc MethodNameLoc =
DeclarationNameLoc::makeCXXOperatorNameLoc(IntroducerRange.getBegin());
CXXMethodDecl *Method = CXXMethodDecl::Create(
S.Context, Class, SourceLocation(),
DeclarationNameInfo(MethodName, IntroducerRange.getBegin(),
MethodNameLoc),
QualType(), nullptr, SC_None, S.getCurFPFeatures().isFPConstrained(),
/*isInline=*/true, ConstexprSpecKind::Unspecified, SourceLocation(),
nullptr);
Method->setAccess(AS_public);
return Method;
}
void Sema::ActOnLambdaIntroducer(LambdaIntroducer &Intro, Scope *CurrentScope) {
LambdaScopeInfo *const LSI = getCurLambda();
assert(LSI && "LambdaScopeInfo should be on stack!");
if (Intro.Default == LCD_ByCopy)
LSI->ImpCaptureStyle = LambdaScopeInfo::ImpCap_LambdaByval;
else if (Intro.Default == LCD_ByRef)
LSI->ImpCaptureStyle = LambdaScopeInfo::ImpCap_LambdaByref;
LSI->CaptureDefaultLoc = Intro.DefaultLoc;
LSI->IntroducerRange = Intro.Range;
LSI->BeforeLambdaQualifiersScope = true;
assert(LSI->NumExplicitTemplateParams == 0);
// Determine if we're within a context where we know that the lambda will
// be dependent, because there are template parameters in scope.
CXXRecordDecl::LambdaDependencyKind LambdaDependencyKind =
@ -910,181 +1006,37 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
LambdaDependencyKind = CXXRecordDecl::LDK_AlwaysDependent;
}
// Determine the signature of the call operator.
TypeSourceInfo *MethodTyInfo;
bool ExplicitParams = true;
bool ExplicitResultType = true;
bool ContainsUnexpandedParameterPack = false;
SourceLocation EndLoc;
SmallVector<ParmVarDecl *, 8> Params;
if (ParamInfo.getNumTypeObjects() == 0) {
// C++11 [expr.prim.lambda]p4:
// If a lambda-expression does not include a lambda-declarator, it is as
// if the lambda-declarator were ().
FunctionProtoType::ExtProtoInfo EPI(Context.getDefaultCallingConvention(
/*IsVariadic=*/false, /*IsCXXMethod=*/true));
EPI.HasTrailingReturn = true;
EPI.TypeQuals.addConst();
LangAS AS = getDefaultCXXMethodAddrSpace();
if (AS != LangAS::Default)
EPI.TypeQuals.addAddressSpace(AS);
// C++1y [expr.prim.lambda]:
// The lambda return type is 'auto', which is replaced by the
// trailing-return type if provided and/or deduced from 'return'
// statements
// We don't do this before C++1y, because we don't support deduced return
// types there.
QualType DefaultTypeForNoTrailingReturn =
getLangOpts().CPlusPlus14 ? Context.getAutoDeductType()
: Context.DependentTy;
QualType MethodTy =
Context.getFunctionType(DefaultTypeForNoTrailingReturn, None, EPI);
MethodTyInfo = Context.getTrivialTypeSourceInfo(MethodTy);
ExplicitParams = false;
ExplicitResultType = false;
EndLoc = Intro.Range.getEnd();
} else {
assert(ParamInfo.isFunctionDeclarator() &&
"lambda-declarator is a function");
DeclaratorChunk::FunctionTypeInfo &FTI = ParamInfo.getFunctionTypeInfo();
// C++11 [expr.prim.lambda]p5:
// This function call operator is declared const (9.3.1) if and only if
// the lambda-expression's parameter-declaration-clause is not followed
// by mutable. It is neither virtual nor declared volatile. [...]
if (!FTI.hasMutableQualifier()) {
FTI.getOrCreateMethodQualifiers().SetTypeQual(DeclSpec::TQ_const,
SourceLocation());
}
MethodTyInfo = GetTypeForDeclarator(ParamInfo, CurScope);
assert(MethodTyInfo && "no type from lambda-declarator");
EndLoc = ParamInfo.getSourceRange().getEnd();
ExplicitResultType = FTI.hasTrailingReturnType();
if (FTIHasNonVoidParameters(FTI)) {
Params.reserve(FTI.NumParams);
for (unsigned i = 0, e = FTI.NumParams; i != e; ++i)
Params.push_back(cast<ParmVarDecl>(FTI.Params[i].Param));
}
// Check for unexpanded parameter packs in the method type.
if (MethodTyInfo->getType()->containsUnexpandedParameterPack())
DiagnoseUnexpandedParameterPack(Intro.Range.getBegin(), MethodTyInfo,
UPPC_DeclarationType);
}
CXXRecordDecl *Class = createLambdaClosureType(
Intro.Range, MethodTyInfo, LambdaDependencyKind, Intro.Default);
CXXMethodDecl *Method =
startLambdaDefinition(Class, Intro.Range, MethodTyInfo, EndLoc, Params,
ParamInfo.getDeclSpec().getConstexprSpecifier(),
ParamInfo.getTrailingRequiresClause());
if (ExplicitParams)
CheckCXXDefaultArguments(Method);
Intro.Range, nullptr, LambdaDependencyKind, Intro.Default);
LSI->Lambda = Class;
// This represents the function body for the lambda function, check if we
// have to apply optnone due to a pragma.
AddRangeBasedOptnone(Method);
// C++11 [expr.prim.lambda]p5:
// The closure type for a lambda-expression has a public inline function
// call operator (13.5.4) whose parameters and return type are described
// by the lambda-expression's parameter-declaration-clause and
// trailing-return-type respectively.
// code_seg attribute on lambda apply to the method.
if (Attr *A = getImplicitCodeSegOrSectionAttrForFunction(Method, /*IsDefinition=*/true))
Method->addAttr(A);
CXXMethodDecl *Method = CreateMethod(*this, Intro.Range, Class);
LSI->CallOperator = Method;
Method->setLexicalDeclContext(CurContext);
// Attributes on the lambda apply to the method.
ProcessDeclAttributes(CurScope, Method, ParamInfo);
// CUDA lambdas get implicit host and device attributes.
if (getLangOpts().CUDA)
CUDASetLambdaAttrs(Method);
// OpenMP lambdas might get assumumption attributes.
if (LangOpts.OpenMP)
ActOnFinishedFunctionDefinitionInOpenMPAssumeScope(Method);
// Number the lambda for linkage purposes if necessary.
handleLambdaNumbering(Class, Method);
// Introduce the function call operator as the current declaration context.
PushDeclContext(CurScope, Method);
// Build the lambda scope.
buildLambdaScope(LSI, Method, Intro.Range, Intro.Default, Intro.DefaultLoc,
ExplicitParams, ExplicitResultType, !Method->isConst());
// C++11 [expr.prim.lambda]p9:
// A lambda-expression whose smallest enclosing scope is a block scope is a
// local lambda expression; any other lambda expression shall not have a
// capture-default or simple-capture in its lambda-introducer.
//
// For simple-captures, this is covered by the check below that any named
// entity is a variable that can be captured.
//
// For DR1632, we also allow a capture-default in any context where we can
// odr-use 'this' (in particular, in a default initializer for a non-static
// data member).
if (Intro.Default != LCD_None && !Class->getParent()->isFunctionOrMethod() &&
(getCurrentThisType().isNull() ||
CheckCXXThisCapture(SourceLocation(), /*Explicit*/true,
/*BuildAndDiagnose*/false)))
Diag(Intro.DefaultLoc, diag::err_capture_default_non_local);
bool ContainsUnexpandedParameterPack = false;
// Distinct capture names, for diagnostics.
llvm::SmallSet<IdentifierInfo*, 8> CaptureNames;
llvm::SmallSet<IdentifierInfo *, 8> CaptureNames;
// Handle explicit captures.
SourceLocation PrevCaptureLoc
= Intro.Default == LCD_None? Intro.Range.getBegin() : Intro.DefaultLoc;
SourceLocation PrevCaptureLoc =
Intro.Default == LCD_None ? Intro.Range.getBegin() : Intro.DefaultLoc;
for (auto C = Intro.Captures.begin(), E = Intro.Captures.end(); C != E;
PrevCaptureLoc = C->Loc, ++C) {
if (C->Kind == LCK_This || C->Kind == LCK_StarThis) {
if (C->Kind == LCK_StarThis)
Diag(C->Loc, !getLangOpts().CPlusPlus17
? diag::ext_star_this_lambda_capture_cxx17
: diag::warn_cxx14_compat_star_this_lambda_capture);
// C++11 [expr.prim.lambda]p8:
// An identifier or this shall not appear more than once in a
// lambda-capture.
if (LSI->isCXXThisCaptured()) {
Diag(C->Loc, diag::err_capture_more_than_once)
<< "'this'" << SourceRange(LSI->getCXXThisCapture().getLocation())
<< FixItHint::CreateRemoval(
SourceRange(getLocForEndOfToken(PrevCaptureLoc), C->Loc));
continue;
}
// C++2a [expr.prim.lambda]p8:
// If a lambda-capture includes a capture-default that is =,
// each simple-capture of that lambda-capture shall be of the form
// "&identifier", "this", or "* this". [ Note: The form [&,this] is
// redundant but accepted for compatibility with ISO C++14. --end note ]
if (Intro.Default == LCD_ByCopy && C->Kind != LCK_StarThis)
Diag(C->Loc, !getLangOpts().CPlusPlus20
? diag::ext_equals_this_lambda_capture_cxx20
: diag::warn_cxx17_compat_equals_this_lambda_capture);
// C++11 [expr.prim.lambda]p12:
// If this is captured by a local lambda expression, its nearest
// enclosing function shall be a non-static member function.
QualType ThisCaptureType = getCurrentThisType();
if (ThisCaptureType.isNull()) {
Diag(C->Loc, diag::err_this_capture) << true;
continue;
}
CheckCXXThisCapture(C->Loc, /*Explicit=*/true, /*BuildAndDiagnose*/ true,
/*FunctionScopeIndexToStopAtPtr*/ nullptr,
C->Kind == LCK_StarThis);
if (!LSI->Captures.empty())
LSI->ExplicitCaptureRanges[LSI->Captures.size() - 1] = C->ExplicitRange;
continue;
}
assert(C->Id && "missing identifier for capture");
if (C->Init.isInvalid())
continue;
@ -1122,13 +1074,10 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
}
Var = createLambdaInitCaptureVarDecl(C->Loc, C->InitCaptureType.get(),
C->EllipsisLoc, C->Id, InitStyle,
C->Init.get());
// C++1y [expr.prim.lambda]p11:
// An init-capture behaves as if it declares and explicitly
// captures a variable [...] whose declarative region is the
// lambda-expression's compound-statement
if (Var)
PushOnScopeChains(Var, CurScope, false);
C->Init.get(), Method);
assert(Var && "createLambdaInitCaptureVarDecl returned a null VarDecl?");
CheckShadow(CurrentScope, Var);
PushOnScopeChains(Var, CurrentScope, false);
} else {
assert(C->InitKind == LambdaCaptureInitKind::NoInit &&
"init capture has valid but null init?");
@ -1171,22 +1120,6 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
continue;
}
// C++11 [expr.prim.lambda]p8:
// An identifier or this shall not appear more than once in a
// lambda-capture.
if (!CaptureNames.insert(C->Id).second) {
if (Var && LSI->isCaptured(Var)) {
Diag(C->Loc, diag::err_capture_more_than_once)
<< C->Id << SourceRange(LSI->getCapture(Var).getLocation())
<< FixItHint::CreateRemoval(
SourceRange(getLocForEndOfToken(PrevCaptureLoc), C->Loc));
} else
// Previous capture captured something different (one or both was
// an init-cpature): no fixit.
Diag(C->Loc, diag::err_capture_more_than_once) << C->Id;
continue;
}
// C++11 [expr.prim.lambda]p10:
// [...] each such lookup shall find a variable with automatic storage
// duration declared in the reaching scope of the local lambda expression.
@ -1196,6 +1129,25 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
continue;
}
// C++11 [expr.prim.lambda]p8:
// An identifier or this shall not appear more than once in a
// lambda-capture.
if (!CaptureNames.insert(C->Id).second) {
auto It = llvm::find_if(LSI->DelayedCaptures, [&Var](auto &&Pair) {
return Pair.second.Var == Var;
});
if (It != LSI->DelayedCaptures.end()) {
Diag(C->Loc, diag::err_capture_more_than_once)
<< C->Id << SourceRange(It->second.Loc)
<< FixItHint::CreateRemoval(
SourceRange(getLocForEndOfToken(PrevCaptureLoc), C->Loc));
} else
// Previous capture captured something different (one or both was
// an init-cpature): no fixit.
Diag(C->Loc, diag::err_capture_more_than_once) << C->Id;
continue;
}
// Ignore invalid decls; they'll just confuse the code later.
if (Var->isInvalidDecl())
continue;
@ -1223,22 +1175,250 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
ContainsUnexpandedParameterPack = true;
}
if (C->Init.isUsable()) {
addInitCapture(LSI, Var);
if (Var)
LSI->DelayedCaptures[std::distance(Intro.Captures.begin(), C)] =
LambdaScopeInfo::DelayedCapture{Var, C->ExplicitRange.getBegin(),
C->Kind};
}
LSI->ContainsUnexpandedParameterPack |= ContainsUnexpandedParameterPack;
PopDeclContext();
}
static void AddExplicitCapturesToContext(Sema &S, LambdaScopeInfo *LSI,
LambdaIntroducer &Intro) {
SourceLocation PrevCaptureLoc;
for (auto C = Intro.Captures.begin(), E = Intro.Captures.end(); C != E;
PrevCaptureLoc = C->Loc, ++C) {
if (C->Kind == LCK_This || C->Kind == LCK_StarThis) {
if (C->Kind == LCK_StarThis)
S.Diag(C->Loc, !S.getLangOpts().CPlusPlus17
? diag::ext_star_this_lambda_capture_cxx17
: diag::warn_cxx14_compat_star_this_lambda_capture);
// C++11 [expr.prim.lambda]p8:
// An identifier or this shall not appear more than once in a
// lambda-capture.
if (LSI->isCXXThisCaptured()) {
S.Diag(C->Loc, diag::err_capture_more_than_once)
<< "'this'" << SourceRange(LSI->getCXXThisCapture().getLocation())
<< FixItHint::CreateRemoval(
SourceRange(S.getLocForEndOfToken(PrevCaptureLoc), C->Loc));
continue;
}
// C++20 [expr.prim.lambda]p8:
// If a lambda-capture includes a capture-default that is =,
// each simple-capture of that lambda-capture shall be of the form
// "&identifier", "this", or "* this". [ Note: The form [&,this] is
// redundant but accepted for compatibility with ISO C++14. --end note ]
if (Intro.Default == LCD_ByCopy && C->Kind != LCK_StarThis)
S.Diag(C->Loc,
!S.getLangOpts().CPlusPlus20
? diag::ext_equals_this_lambda_capture_cxx20
: diag::warn_cxx17_compat_equals_this_lambda_capture);
// C++11 [expr.prim.lambda]p12:
// If this is captured by a local lambda expression, its nearest
// enclosing function shall be a non-static member function.
QualType ThisCaptureType = S.getCurrentThisType();
if (ThisCaptureType.isNull()) {
S.Diag(C->Loc, diag::err_this_capture) << true;
continue;
}
S.CheckCXXThisCapture(C->Loc, true, true, nullptr,
C->Kind == LCK_StarThis);
} else {
TryCaptureKind Kind = C->Kind == LCK_ByRef ? TryCapture_ExplicitByRef :
TryCapture_ExplicitByVal;
tryCaptureVariable(Var, C->Loc, Kind, EllipsisLoc);
VarDecl *Var =
LSI->DelayedCaptures[std::distance(Intro.Captures.begin(), C)].Var;
if (!Var)
continue;
if (Var->isInitCapture() && C->Init.isUsable()) {
S.addInitCapture(LSI, Var);
S.PushOnScopeChains(Var, S.getCurScope(), false);
} else {
Sema::TryCaptureKind Kind = C->Kind == LCK_ByRef
? Sema::TryCapture_ExplicitByRef
: Sema::TryCapture_ExplicitByVal;
S.tryCaptureVariable(Var, C->Loc, Kind, C->EllipsisLoc);
}
}
if (!LSI->Captures.empty())
LSI->ExplicitCaptureRanges[LSI->Captures.size() - 1] = C->ExplicitRange;
}
finishLambdaExplicitCaptures(LSI);
S.finishLambdaExplicitCaptures(LSI);
}
LSI->ContainsUnexpandedParameterPack |= ContainsUnexpandedParameterPack;
void Sema::ActOnLambdaClosureQualifiers(
LambdaIntroducer &Intro, SourceLocation MutableLoc, SourceLocation EndLoc,
MutableArrayRef<DeclaratorChunk::ParamInfo> Params, const DeclSpec &DS) {
// Add lambda parameters into scope.
addLambdaParameters(Intro.Captures, Method, CurScope);
LambdaScopeInfo *const LSI = getCurrentLambdaScopeUnsafe(*this);
LSI->Mutable = MutableLoc.isValid();
LSI->BeforeLambdaQualifiersScope = false;
LSI->CallOperator->setConstexprKind(DS.getConstexprSpecifier());
// C++11 [expr.prim.lambda]p9:
// A lambda-expression whose smallest enclosing scope is a block scope is a
// local lambda expression; any other lambda expression shall not have a
// capture-default or simple-capture in its lambda-introducer.
//
// For simple-captures, this is covered by the check below that any named
// entity is a variable that can be captured.
//
// For DR1632, we also allow a capture-default in any context where we can
// odr-use 'this' (in particular, in a default initializer for a non-static
// data member).
if (Intro.Default != LCD_None &&
!LSI->Lambda->getParent()->isFunctionOrMethod() &&
(getCurrentThisType().isNull() ||
CheckCXXThisCapture(SourceLocation(), /*Explicit*/ true,
/*BuildAndDiagnose*/ false)))
Diag(Intro.DefaultLoc, diag::err_capture_default_non_local);
PushDeclContext(CurScope, LSI->CallOperator);
for (const DeclaratorChunk::ParamInfo &P : Params) {
auto *Param = cast<ParmVarDecl>(P.Param);
Param->setOwningFunction(LSI->CallOperator);
if (Param->getIdentifier())
PushOnScopeChains(Param, CurScope, false);
}
AddExplicitCapturesToContext(*this, LSI, Intro);
}
void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
Declarator &ParamInfo,
Scope *CurScope) {
LambdaScopeInfo *const LSI = getCurrentLambdaScopeUnsafe(*this);
SmallVector<ParmVarDecl *, 8> Params;
bool ExplicitResultType;
SourceLocation TypeLoc, LambdaLoc;
if (ParamInfo.getNumTypeObjects() == 0) {
LambdaLoc = TypeLoc = Intro.Range.getEnd();
} else {
unsigned index;
ParamInfo.isFunctionDeclarator(index);
const auto &Object = ParamInfo.getTypeObject(index);
TypeLoc =
Object.Loc.isValid() ? Object.Loc : ParamInfo.getSourceRange().getEnd();
LambdaLoc = ParamInfo.getSourceRange().getEnd();
}
CXXRecordDecl *Class = LSI->Lambda;
CXXMethodDecl *Method = LSI->CallOperator;
if (auto *C = ParamInfo.getTrailingRequiresClause())
Method->setTrailingRequiresClause(C);
TemplateParameterList *TemplateParams =
getGenericLambdaTemplateParameterList(LSI, *this);
auto DC = Method->getLexicalDeclContext();
Method->setLexicalDeclContext(Class);
if (TemplateParams) {
FunctionTemplateDecl *const TemplateMethod = FunctionTemplateDecl::Create(
Context, Class, Method->getLocation(), Method->getDeclName(),
TemplateParams, Method);
TemplateMethod->setAccess(AS_public);
Method->setDescribedFunctionTemplate(TemplateMethod);
Class->addDecl(TemplateMethod);
TemplateMethod->setLexicalDeclContext(DC);
} else {
Class->addDecl(Method);
}
Method->setLexicalDeclContext(DC);
Class->setLambdaIsGeneric(TemplateParams);
TypeSourceInfo *MethodTyInfo = getLambdaType(
*this, Intro, ParamInfo, getCurScope(), TypeLoc, ExplicitResultType);
Class->setLambdaTypeInfo(MethodTyInfo);
Method->setInnerLocStart(LambdaLoc);
Method->setLocation(Intro.Range.getBegin());
Method->setTypeSourceInfo(MethodTyInfo);
Method->setType(buildTypeForLambdaCallOperator(*this, Class, TemplateParams,
MethodTyInfo));
Method->setConstexprKind(ParamInfo.getDeclSpec().getConstexprSpecifier());
buildLambdaScopeReturnType(*this, LSI, Method, ExplicitResultType);
LSI->ExplicitParams = ParamInfo.getNumTypeObjects() != 0;
if (ParamInfo.isFunctionDeclarator() != 0 &&
!FTIHasSingleVoidParameter(ParamInfo.getFunctionTypeInfo())) {
const auto &FTI = ParamInfo.getFunctionTypeInfo();
Params.reserve(Params.size());
for (unsigned I = 0; I < FTI.NumParams; ++I) {
auto *Param = cast<ParmVarDecl>(FTI.Params[I].Param);
Param->setScopeInfo(0, Params.size());
Params.push_back(Param);
}
}
ContextRAII ManglingContext(*this, Class->getDeclContext());
CheckParmsForFunctionDef(Params, /*CheckParameterNames=*/false);
if (LSI->ExplicitParams) {
Method->setParams(Params);
CheckCXXDefaultArguments(Method);
}
// This represents the function body for the lambda function, check if we
// have to apply optnone due to a pragma.
AddRangeBasedOptnone(Method);
// code_seg attribute on lambda apply to the method.
if (Attr *A = getImplicitCodeSegOrSectionAttrForFunction(
Method, /*IsDefinition=*/true))
Method->addAttr(A);
// Attributes on the lambda apply to the method.
ProcessDeclAttributes(CurScope, Method, ParamInfo);
// CUDA lambdas get implicit host and device attributes.
if (getLangOpts().CUDA)
CUDASetLambdaAttrs(Method);
// OpenMP lambdas might get assumumption attributes.
if (LangOpts.OpenMP)
ActOnFinishedFunctionDefinitionInOpenMPAssumeScope(Method);
handleLambdaNumbering(Class, Method);
ManglingContext.pop();
for (auto &&C : LSI->DelayedCaptures) {
VarDecl *Var = C.second.Var;
if (Var && Var->isInitCapture()) {
PushOnScopeChains(Var, CurScope, false);
}
}
LSI->DelayedCaptures.clear();
auto CheckRedefinition = [&](ParmVarDecl *Param) {
for (const auto &Capture : Intro.Captures) {
if (Capture.Id == Param->getIdentifier()) {
Diag(Param->getLocation(), diag::err_parameter_shadow_capture);
Diag(Capture.Loc, diag::note_var_explicitly_captured_here)
<< Capture.Id << true;
return false;
}
}
return true;
};
for (ParmVarDecl *P : Params) {
if (!P->getIdentifier())
continue;
if (CheckRedefinition(P))
CheckShadow(CurScope, P);
PushOnScopeChains(P, CurScope);
}
// Enter a new evaluation context to insulate the lambda from any
// cleanups from the enclosing full-expression.

View File

@ -13100,7 +13100,8 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
}
VarDecl *NewVD = getSema().createLambdaInitCaptureVarDecl(
OldVD->getLocation(), InitQualType, NewC.EllipsisLoc,
OldVD->getIdentifier(), OldVD->getInitStyle(), Init.get());
OldVD->getIdentifier(), OldVD->getInitStyle(), Init.get(),
getSema().CurContext);
if (!NewVD) {
Invalid = true;
break;

View File

@ -12,16 +12,16 @@ auto with_float_2 = [&f(f)] { // ok, refers to outer f
using T = double&;
};
// Within the lambda-expression's compound-statement,
// the identifier in the init-capture hides any declaration
// of the same name in scopes enclosing the lambda-expression.
// Within the lambda-expression the identifier in the init-capture
// hides any declaration of the same name in scopes enclosing
// the lambda-expression.
void hiding() {
char c;
(void) [c("foo")] {
static_assert(sizeof(c) == sizeof(const char*), "");
};
(void) [c("bar")] () -> decltype(c) { // outer c, not init-capture
return "baz"; // expected-error {{cannot initialize}}
(void)[c("bar")]()->decltype(c) { // inner c
return "baz";
};
}

View File

@ -0,0 +1,173 @@
// RUN: %clang_cc1 -std=c++2b -verify -fsyntax-only %s
template <typename T, typename U>
constexpr bool is_same = false;
template <typename T>
constexpr bool is_same<T, T> = true;
void f() {
int y;
static_assert(is_same<const int &,
decltype([x = 1] -> decltype((x)) { return x; }())>);
static_assert(is_same<int &,
decltype([x = 1] mutable -> decltype((x)) { return x; }())>);
static_assert(is_same<const int &,
decltype([=] -> decltype((y)) { return y; }())>);
static_assert(is_same<int &,
decltype([=] mutable -> decltype((y)) { return y; }())>);
static_assert(is_same<const int &,
decltype([=] -> decltype((y)) { return y; }())>);
static_assert(is_same<int &,
decltype([=] mutable -> decltype((y)) { return y; }())>);
auto ref = [&x = y](
decltype([&](decltype(x)) { return 0; }) y) {
return x;
};
}
void test_noexcept() {
int y;
static_assert(noexcept([x = 1] noexcept(is_same<const int &, decltype((x))>) {}()));
static_assert(noexcept([x = 1] mutable noexcept(is_same<int &, decltype((x))>) {}()));
static_assert(noexcept([y] noexcept(is_same<const int &, decltype((y))>) {}()));
static_assert(noexcept([y] mutable noexcept(is_same<int &, decltype((y))>) {}()));
static_assert(noexcept([=] noexcept(is_same<const int &, decltype((y))>) {}()));
static_assert(noexcept([=] mutable noexcept(is_same<int &, decltype((y))>) {}()));
static_assert(noexcept([&] noexcept(is_same<int &, decltype((y))>) {}()));
static_assert(noexcept([&] mutable noexcept(is_same<int &, decltype((y))>) {}()));
static_assert(noexcept([&] mutable noexcept(!is_same<int &, decltype((y))>) {}())); // expected-error {{static_assert failed due}}
}
void test_requires() {
int x;
[x = 1]() requires is_same<const int &, decltype((x))> {}
();
[x = 1]() mutable requires is_same<int &, decltype((x))> {}
();
[x]() requires is_same<const int &, decltype((x))> {}
();
[x]() mutable requires is_same<int &, decltype((x))> {}
();
[=]() requires is_same<const int &, decltype((x))> {}
();
[=]() mutable requires is_same<int &, decltype((x))> {}
();
[&]() requires is_same<int &, decltype((x))> {}
();
[&]() mutable requires is_same<int &, decltype((x))> {}
();
[&x]() requires is_same<int &, decltype((x))> {}
();
[&x]() mutable requires is_same<int &, decltype((x))> {}
();
[x = 1]() requires is_same<int &, decltype((x))> {} (); //expected-error {{no matching function for call to object of type}} \
// expected-note {{candidate function not viable}} \
// expected-note {{'is_same<int &, decltype((x))>' evaluated to false}}
[x = 1]() mutable requires is_same<const int &, decltype((x))> {} (); // expected-error {{no matching function for call to object of type}} \
// expected-note {{candidate function not viable}} \
// expected-note {{'is_same<const int &, decltype((x))>' evaluated to false}}
}
void err() {
int y, z; // expected-note 2{{declared here}}
auto implicit_tpl = [=]( // expected-note {{variable 'y' is captured here}}
decltype(
[&]<decltype(y)> { return 0; }) y) { //expected-error{{captured variable 'y' cannot appear here}}
return y;
};
auto init_tpl = [x = 1]( // expected-note{{explicitly captured here}}
decltype([&]<decltype(x)> { return 0; }) y) { // expected-error {{captured variable 'x' cannot appear here}}
return x;
};
auto implicit = [=]( // expected-note {{variable 'z' is captured here}}
decltype(
[&](decltype(z)) { return 0; }) z) { //expected-error{{captured variable 'z' cannot appear here}}
return z;
};
auto init = [x = 1]( // expected-note{{explicitly captured here}}
decltype([&](decltype(x)) { return 0; }) y) { // expected-error {{captured variable 'x' cannot appear here}}
return x;
};
auto use_before_params = [x = 1]<typename T> // expected-note {{variable 'x' is explicitly captured here}}
requires(is_same<const int &, decltype((x))>) // expected-error {{captured variable 'x' cannot appear here}}
{};
auto use_before_params2 = [x = 1]<typename T = decltype((x))> // expected-note {{variable 'x' is explicitly captured here}} \
// expected-error {{captured variable 'x' cannot appear here}}
{};
}
void gnu_attributes() {
int y;
(void)[=]() __attribute__((diagnose_if(!is_same<decltype((y)), const int &>, "wrong type", "warning"))){}();
(void)[=]() __attribute__((diagnose_if(!is_same<decltype((y)), int &>, "wrong type", "warning"))){}();
// expected-warning@-1 {{wrong type}} expected-note@-1{{'diagnose_if' attribute on 'operator()'}}
(void)[=]() __attribute__((diagnose_if(!is_same<decltype((y)), int &>, "wrong type", "warning"))) mutable {}();
(void)[=]() __attribute__((diagnose_if(!is_same<decltype((y)), const int &>, "wrong type", "warning"))) mutable {}();
// expected-warning@-1 {{wrong type}} expected-note@-1{{'diagnose_if' attribute on 'operator()'}}
(void)[x=1]() __attribute__((diagnose_if(!is_same<decltype((x)), const int &>, "wrong type", "warning"))){}();
(void)[x=1]() __attribute__((diagnose_if(!is_same<decltype((x)), int &>, "wrong type", "warning"))){}();
// expected-warning@-1 {{wrong type}} expected-note@-1{{'diagnose_if' attribute on 'operator()'}}
(void)[x=1]() __attribute__((diagnose_if(!is_same<decltype((x)), int &>, "wrong type", "warning"))) mutable {}();
(void)[x=1]() __attribute__((diagnose_if(!is_same<decltype((x)), const int &>, "wrong type", "warning"))) mutable {}();
// expected-warning@-1 {{wrong type}} expected-note@-1{{'diagnose_if' attribute on 'operator()'}}
}
void nested() {
int x, y, z; // expected-note {{'x' declared here}} expected-note {{'z' declared here}}
(void)[&](
decltype([&](
decltype([=]( // expected-note {{variable 'x' is captured here}}
decltype([&](
decltype([&](decltype(x)) {}) // expected-error{{captured variable 'x' cannot appear here}}
) {})) {})) {})){};
(void)[&](
decltype([&](
decltype([&](
decltype([&](
decltype([&](decltype(y)) {})) {})) {})) {})){};
(void)[=](
decltype([=](
decltype([=](
decltype([=]( // expected-note {{variable 'z' is captured here}}
decltype([&]<decltype(z)> {}) // expected-error{{captured variable 'z' cannot appear here}}
) {})) {})) {})){};
}
template <typename T, typename U>
void dependent(U&& u) {
[&]() requires is_same<decltype(u), T> {}();
}
void test_dependent() {
int v = 0;
int & r = v;
const int & cr = v;
dependent<int&>(v);
dependent<int&>(r);
dependent<const int&>(cr);
}

View File

@ -95,7 +95,7 @@ void foo(int param) { // expected-note 1+ {{previous declaration is here}}
#ifdef AVOID
auto l4 = [var = param] (int param) { ; }; // no warning
#else
auto l4 = [var = param] (int param) { ; }; // expected-warning {{declaration shadows a local variable}}
auto l4 = [var = param](int param) { ; }; // expected-warning 2{{declaration shadows a local variable}}
#endif
// Make sure that inner lambdas work as well.

View File

@ -1356,7 +1356,7 @@ C++20, informally referred to as C++2b.</p>
<tr>
<td>Change scope of lambda trailing-return-type</td>
<td><a href="https://wg21.link/P2036R3">P2036R3</a></td>
<td class="none" align="center">No</td>
<td class="unreleased" align="center">Clang 15</td>
</tr>
<tr>
<td>Multidimensional subscript operator</td>