diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -286,6 +286,9 @@ - Implemented `P2128R6: Multidimensional subscript operator `_. - Implemented `P0849R8: auto(x): decay-copy in the language `_. - Implemented `P2242R3: Non-literal variables (and labels and gotos) in constexpr functions `_. +- Implemented `P2036R3: Change scope of lambda trailing-return-type `_. + 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 ------------------------------ diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -1799,6 +1799,20 @@ 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(*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(*DD); + DL.IsGenericLambda = IsGeneric; + } + // Determine whether this type is an Interface Like type for // __interface inheritance purposes. bool isInterfaceLike() const; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -7723,6 +7723,8 @@ 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< diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -1900,7 +1900,10 @@ bool ParseLambdaIntroducer(LambdaIntroducer &Intro, LambdaIntroducerTentativeParse *Tentative = nullptr); + SourceLocation LambdaLookAheadMutableSpecifier(); ExprResult ParseLambdaExpressionAfterIntroducer(LambdaIntroducer &Intro); + void ParseLambdaLexedGNUAttributeArgs(LateParsedAttribute &LA, + ParsedAttributes &Attrs, Declarator &D); //===--------------------------------------------------------------------===// // C++ 5.2p1: C++ Casts diff --git a/clang/include/clang/Sema/Scope.h b/clang/include/clang/Sema/Scope.h --- a/clang/include/clang/Sema/Scope.h +++ b/clang/include/clang/Sema/Scope.h @@ -44,11 +44,11 @@ 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 @@ /// 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: diff --git a/clang/include/clang/Sema/ScopeInfo.h b/clang/include/clang/Sema/ScopeInfo.h --- a/clang/include/clang/Sema/ScopeInfo.h +++ b/clang/include/clang/Sema/ScopeInfo.h @@ -831,6 +831,29 @@ /// 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 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 HasKnownMutability = true; + bool AfterParameterList = true; + /// Source range covering the lambda introducer [...]. SourceRange IntroducerRange; 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 @@ -6816,15 +6816,6 @@ unsigned LambdaDependencyKind, LambdaCaptureDefault CaptureDefault); - /// Start the definition of a lambda expression. - CXXMethodDecl *startLambdaDefinition(CXXRecordDecl *Class, - SourceRange IntroducerRange, - TypeSourceInfo *MethodType, - SourceLocation EndLoc, - ArrayRef Params, - ConstexprSpecKind ConstexprKind, - Expr *TrailingRequiresClause); - /// Number lambda for linkage purposes if necessary. void handleLambdaNumbering( CXXRecordDecl *Class, CXXMethodDecl *Method, @@ -6837,9 +6828,16 @@ LambdaCaptureDefault CaptureDefault, SourceLocation CaptureDefaultLoc, bool ExplicitParams, - bool ExplicitResultType, bool Mutable); + CXXMethodDecl *CreateLambdaCallOperator(SourceRange IntroducerRange, + CXXRecordDecl *Class); + void CompleteLambdaCallOperator( + CXXMethodDecl *Method, SourceLocation LambdaLoc, + SourceLocation CallOperatorLoc, Expr *TrailingRequiresClause, + TypeSourceInfo *MethodTyInfo, ConstexprSpecKind ConstexprKind, + ArrayRef Params, bool HasExplicitResultType); + /// Perform initialization analysis of the init-capture and perform /// any implicit conversions such as an lvalue-to-rvalue conversion if /// not being used to initialize a reference. @@ -6860,11 +6858,9 @@ /// /// 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); @@ -6873,28 +6869,38 @@ /// 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 TParams, SourceLocation RAngleLoc, ExprResult RequiresClause); - /// Introduce the lambda parameters into scope. - void addLambdaParameters( - ArrayRef Captures, - CXXMethodDecl *CallOperator, Scope *CurScope); + void ActOnLambdaClosureQualifiers(LambdaIntroducer &Intro, + SourceLocation MutableLoc); - /// Deduce a block or lambda's return type based on the return - /// statements present in the body. - void deduceClosureReturnType(sema::CapturingScopeInfo &CSI); + void ActOnLambdaClosureParameters( + Scope *LambdaScope, + MutableArrayRef ParamInfo); /// ActOnStartOfLambdaDefinition - This is called just before we start /// parsing the body of a lambda; it analyzes the explicit captures and /// arguments, and sets up various data-structures for the body of the /// lambda. void ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, - Declarator &ParamInfo, Scope *CurScope); + Declarator &ParamInfo, const DeclSpec &DS); /// ActOnLambdaError - If there is an error parsing a lambda, this callback /// is invoked to pop the information about the lambda. diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -1172,9 +1172,10 @@ P.Diag(P.getCurToken().getLocation(), diag::err_lambda_decl_specifier_repeated) << 0 << FixItHint::CreateRemoval(P.getCurToken().getLocation()); + } else { + MutableLoc = P.getCurToken().getLocation(); } - MutableLoc = P.ConsumeToken(); - DeclEndLoc = MutableLoc; + DeclEndLoc = P.ConsumeToken(); break /*switch*/; } case tok::kw_constexpr: @@ -1231,6 +1232,62 @@ } } +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(); +} + +/// LambdaMayHaveMutableSpecifier Look ahead to see if the lkambda will be +/// mutable +/// \returns true on success and false if the token was not balanced +SourceLocation Parser::LambdaLookAheadMutableSpecifier() { + assert(Tok.is(tok::l_paren) && "expected the start of a lambda parameter"); + RevertingTentativeParsingAction RAII(*this); + // skip the parameter list + ConsumeParen(); + if (!SkipUntil(tok::r_paren)) + return SourceLocation{}; + // skip GNU/MSVC attributes + while (Tok.isOneOf(tok::kw___attribute, tok::kw___declspec)) { + ConsumeToken(); + if (!Tok.is(tok::l_paren)) + return SourceLocation{}; + ConsumeParen(); + if (!SkipUntil(tok::r_paren)) + return SourceLocation{}; + } + while (Tok.isOneOf(tok::kw_constexpr, tok::kw_consteval)) + ConsumeToken(); + if (Tok.is(tok::kw_mutable)) + return Tok.getLocation(); + return SourceLocation{}; +} + /// ParseLambdaExpressionAfterIntroducer - Parse the rest of a lambda /// expression. ExprResult Parser::ParseLambdaExpressionAfterIntroducer( @@ -1250,9 +1307,15 @@ DeclSpec DS(AttrFactory); Declarator D(DS, DeclaratorContext::LambdaExpr); TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth); + + ParseScope LambdaScope(this, Scope::LambdaScope | Scope::DeclScope | + Scope::FunctionDeclarationScope | + Scope::FunctionPrototypeScope); + Actions.PushLambdaScope(); + Actions.ActOnLambdaIntroducer(Intro, getCurScope()); - ParsedAttributes Attr(AttrFactory); + 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 +1326,7 @@ // 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 +1363,7 @@ } Actions.ActOnLambdaExplicitTemplateParameterList( - LAngleLoc, TemplateParams, RAngleLoc, RequiresClause); + Intro, LAngleLoc, TemplateParams, RAngleLoc, RequiresClause); ++CurTemplateDepthTracker; } } @@ -1318,28 +1381,45 @@ 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; + SourceLocation CheckMutableLoc; + + tryConsumeLambdaSpecifierToken(*this, CheckMutableLoc, ConstexprLoc, + ConstevalLoc, DeclEndLoc); + + addConstexprToLambdaDeclSpecifier(*this, ConstexprLoc, DS); + addConstevalToLambdaDeclSpecifier(*this, ConstevalLoc, DS); + + assert((MutableLoc.isInvalid() || CheckMutableLoc == MutableLoc) && + "inconsistent mutable specifier in lambda expression"); + MutableLoc = CheckMutableLoc; + }; auto ParseLambdaSpecifiers = - [&](SourceLocation LParenLoc, SourceLocation RParenLoc, - MutableArrayRef ParamInfo, + [&](MutableArrayRef 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 +1427,15 @@ SmallVector 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 +1444,8 @@ 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 +1481,33 @@ /*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); }; + ParseScope Prototype(this, Scope::FunctionPrototypeScope | + Scope::FunctionDeclarationScope | + Scope::DeclScope); + + // Parse parameter-declaration-clause. + SmallVector ParamInfo; + SourceLocation EllipsisLoc; + if (Tok.is(tok::l_paren)) { - ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope | - Scope::FunctionDeclarationScope | - Scope::DeclScope); + MutableLoc = LambdaLookAheadMutableSpecifier(); + Actions.ActOnLambdaClosureQualifiers(Intro, MutableLoc); BalancedDelimiterTracker T(*this, tok::l_paren); T.consumeOpen(); - SourceLocation LParenLoc = T.getOpenLocation(); - - // Parse parameter-declaration-clause. - SmallVector 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,43 +1519,49 @@ } 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 EmptyParamInfo; - ParseLambdaSpecifiers(/*LParenLoc=*/NoLoc, /*RParenLoc=*/NoLoc, - EmptyParamInfo, /*EllipsisLoc=*/NoLoc); + if (HasParentheses || HasSpecifiers) { + ParseConstexprAndMutableSpecifiers(); } + if (!HasParentheses) { + Actions.ActOnLambdaClosureQualifiers(Intro, MutableLoc); + } + Actions.ActOnLambdaClosureParameters(getCurScope(), ParamInfo); + + 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 | Scope::CompoundStmtScope; ParseScope BodyScope(this, ScopeFlags); - Actions.ActOnStartOfLambdaDefinition(Intro, D, getCurScope()); + Actions.ActOnStartOfLambdaDefinition(Intro, D, DS); // Parse compound-statement. if (!Tok.is(tok::l_brace)) { @@ -1472,6 +1573,7 @@ StmtResult Stmt(ParseCompoundStatementBody()); BodyScope.Exit(); TemplateParamScope.Exit(); + LambdaScope.Exit(); if (!Stmt.isInvalid() && !TrailingReturnType.isInvalid()) return Actions.ActOnLambdaExpr(LambdaBeginLoc, Stmt.get(), getCurScope()); diff --git a/clang/lib/Sema/Scope.cpp b/clang/lib/Sema/Scope.cpp --- a/clang/lib/Sema/Scope.cpp +++ b/clang/lib/Sema/Scope.cpp @@ -67,8 +67,10 @@ 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) diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -2297,7 +2297,8 @@ LambdaScopeInfo *Sema::getEnclosingLambda() const { for (auto *Scope : llvm::reverse(FunctionScopes)) { if (auto *LSI = dyn_cast(Scope)) { - if (LSI->Lambda && !LSI->Lambda->Encloses(CurContext)) { + if (LSI->Lambda && !LSI->Lambda->Encloses(CurContext) && + LSI->AfterParameterList) { // 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,8 @@ return nullptr; } auto *CurLSI = dyn_cast(*I); - if (CurLSI && CurLSI->Lambda && - !CurLSI->Lambda->Encloses(CurContext)) { + if (CurLSI && CurLSI->Lambda && CurLSI->CallOperator && + !CurLSI->Lambda->Encloses(CurContext) && CurLSI->AfterParameterList) { // We have switched contexts due to template instantiation. assert(!CodeSynthesisContexts.empty()); return nullptr; diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp --- a/clang/lib/Sema/SemaCXXScopeSpec.cpp +++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp @@ -292,6 +292,11 @@ 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 @@ 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; 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 @@ -3386,7 +3386,7 @@ // FIXME: Support lambda-capture of BindingDecls, once CWG actually // decides how that's supposed to work. auto *BD = cast(VD); - if (BD->getDeclContext() != CurContext) { + if (BD->getDeclContext() != CurContext && !isUnevaluatedContext()) { auto *DD = dyn_cast_or_null(BD->getDecomposedDecl()); if (DD && DD->hasLocalStorage()) diagnoseUncapturableValueReference(*this, Loc, BD); @@ -18547,10 +18547,42 @@ } } +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->HasKnownMutability) { + // 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, QualType &DeclRefType, const unsigned *const FunctionScopeIndexToStopAt) { + // An init-capture is notionally from the context surrounding its // declaration, but its parent DC is the lambda class. DeclContext *VarDC = Var->getDeclContext(); @@ -18570,11 +18602,6 @@ } } - - // 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(); @@ -18597,13 +18624,45 @@ bool Nested = false; bool Explicit = (Kind != TryCapture_Implicit); unsigned FunctionScopesIndex = MaxFunctionScopesIndex; + bool IsInScopeDeclarationContext; + bool CanCapture; do { + IsInScopeDeclarationContext = true; + CanCapture = true; + + LambdaScopeInfo *LSI = nullptr; + if (!FunctionScopes.empty()) + LSI = dyn_cast_or_null( + FunctionScopes[FunctionScopesIndex]); + + IsInScopeDeclarationContext = + !LSI || LSI->AfterParameterList || CurContext == LSI->CallOperator; + + if (LSI && !LSI->AfterParameterList) { + // This allows capturing parameters from a default value which does not + // seems correct + if (isa(Var) && !Var->getDeclContext()->isFunctionOrMethod()) + return true; + } + if (LSI && !LSI->HasKnownMutability) { + if (!CheckCaptureUseBeforeLambdaQualifiers(*this, Var, ExprLoc, LSI)) { + break; + } + CanCapture = false; + } + // If the variable is declared in the current context, there is no need to + // capture it. + if (IsInScopeDeclarationContext && + 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 = + !IsInScopeDeclarationContext + ? 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. @@ -18618,10 +18677,9 @@ FunctionScopeInfo *FSI = FunctionScopes[FunctionScopesIndex]; CapturingScopeInfo *CSI = cast(FSI); - // Check whether we've already captured it. - if (isVariableAlreadyCapturedInScopeInfo(CSI, Var, Nested, CaptureType, - DeclRefType)) { + if (CanCapture && isVariableAlreadyCapturedInScopeInfo( + CSI, Var, Nested, CaptureType, DeclRefType)) { CSI->getCapture(Var).markUsed(BuildAndDiagnose); break; } @@ -18629,7 +18687,7 @@ // 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 (CanCapture && isGenericLambdaCallOperatorSpecialization(DC)) { if (BuildAndDiagnose) { LambdaScopeInfo *LSI = cast(CSI); if (LSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_None) { @@ -18644,7 +18702,7 @@ } // Try to capture variable-length arrays types. - if (Var->getType()->isVariablyModifiedType()) { + if (CanCapture && Var->getType()->isVariablyModifiedType()) { // We're going to walk down into the type and look for VLA // expressions. QualType QTy = Var->getType(); @@ -18653,7 +18711,7 @@ captureVariablyModifiedType(Context, QTy, CSI); } - if (getLangOpts().OpenMP) { + if (CanCapture && getLangOpts().OpenMP) { if (auto *RSI = dyn_cast(CSI)) { // OpenMP private variables should not be captured in outer scope, so // just break here. Similarly, global variables that are captured in a @@ -18734,10 +18792,10 @@ } return true; } - - FunctionScopesIndex--; - DC = ParentDC; Explicit = false; + FunctionScopesIndex--; + if (IsInScopeDeclarationContext) + DC = ParentDC; } while (!VarDC->Equals(DC)); // Walk back down the scope stack, (e.g. from outer lambda to inner lambda) @@ -18773,6 +18831,9 @@ Nested = true; } else { LambdaScopeInfo *LSI = cast(CSI); + if (!CheckCaptureUseBeforeLambdaQualifiers(*this, Var, ExprLoc, LSI)) { + return true; + } Invalid = !captureInLambda(LSI, Var, ExprLoc, BuildAndDiagnose, CaptureType, DeclRefType, Nested, Kind, EllipsisLoc, diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -1130,7 +1130,7 @@ if (C.isCopyCapture()) { ClassType.removeLocalCVRQualifiers(Qualifiers::CVRMask); - if (CurLSI->CallOperator->isConst()) + if (!CurLSI->Mutable) ClassType.addConst(); return ASTCtx.getPointerType(ClassType); } diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -245,8 +245,9 @@ 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 @@ llvm_unreachable("unexpected context"); } -CXXMethodDecl *Sema::startLambdaDefinition(CXXRecordDecl *Class, - SourceRange IntroducerRange, - TypeSourceInfo *MethodTypeInfo, - SourceLocation EndLoc, - ArrayRef 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,58 +369,12 @@ const FunctionProtoType *FPT = MethodType->castAs(); 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()); } } - - // 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 - = Context.DeclarationNames.getCXXOperatorName(OO_Call); - DeclarationNameLoc MethodNameLoc = - DeclarationNameLoc::makeCXXOperatorNameLoc(IntroducerRange); - CXXMethodDecl *Method = CXXMethodDecl::Create( - Context, Class, EndLoc, - DeclarationNameInfo(MethodName, IntroducerRange.getBegin(), - MethodNameLoc), - MethodType, MethodTypeInfo, SC_None, getCurFPFeatures().isFPConstrained(), - /*isInline=*/true, ConstexprKind, EndLoc, TrailingRequiresClause); - Method->setAccess(AS_public); - if (!TemplateParams) - Class->addDecl(Method); - - // Temporarily set the lexical declaration context to the current - // 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; - if (TemplateMethod) { - TemplateMethod->setAccess(AS_public); - Method->setDescribedFunctionTemplate(TemplateMethod); - Class->addDecl(TemplateMethod); - TemplateMethod->setLexicalDeclContext(CurContext); - } - - // Add parameters. - if (!Params.empty()) { - Method->setParams(Params); - CheckParmsForFunctionDef(Params, - /*CheckParameterNames=*/false); - - for (auto P : Method->parameters()) - P->setOwningFunction(Method); - } - - return Method; + return MethodType; } void Sema::handleLambdaNumbering( @@ -480,14 +432,26 @@ } } -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; + } +} + +void Sema::buildLambdaScope(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 +463,16 @@ 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::finishLambdaExplicitCaptures(LambdaScopeInfo *LSI) { LSI->finishedExplicitCaptures(); } -void Sema::ActOnLambdaExplicitTemplateParameterList(SourceLocation LAngleLoc, - ArrayRef TParams, - SourceLocation RAngleLoc, - ExprResult RequiresClause) { +void Sema::ActOnLambdaExplicitTemplateParameterList( + LambdaIntroducer &Intro, SourceLocation LAngleLoc, + ArrayRef TParams, SourceLocation RAngleLoc, + ExprResult RequiresClause) { LambdaScopeInfo *LSI = getCurLambda(); assert(LSI && "Expected a lambda scope"); assert(LSI->NumExplicitTemplateParams == 0 && @@ -538,35 +488,6 @@ LSI->RequiresClause = RequiresClause; } -void Sema::addLambdaParameters( - ArrayRef 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 +774,9 @@ 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 +787,8 @@ // 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 +807,156 @@ 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(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; +} + +CXXMethodDecl *Sema::CreateLambdaCallOperator(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 = + Context.DeclarationNames.getCXXOperatorName(OO_Call); + DeclarationNameLoc MethodNameLoc = + DeclarationNameLoc::makeCXXOperatorNameLoc(IntroducerRange.getBegin()); + CXXMethodDecl *Method = CXXMethodDecl::Create( + Context, Class, SourceLocation(), + DeclarationNameInfo(MethodName, IntroducerRange.getBegin(), + MethodNameLoc), + QualType(), nullptr, SC_None, getCurFPFeatures().isFPConstrained(), + /*isInline=*/true, ConstexprSpecKind::Unspecified, SourceLocation(), + nullptr); + Method->setAccess(AS_public); + return Method; +} + +void Sema::CompleteLambdaCallOperator( + CXXMethodDecl *Method, SourceLocation LambdaLoc, + SourceLocation CallOperatorLoc, Expr *TrailingRequiresClause, + TypeSourceInfo *MethodTyInfo, ConstexprSpecKind ConstexprKind, + ArrayRef Params, bool HasExplicitResultType) { + + LambdaScopeInfo *const LSI = getCurrentLambdaScopeUnsafe(*this); + + if (TrailingRequiresClause) + Method->setTrailingRequiresClause(TrailingRequiresClause); + + TemplateParameterList *TemplateParams = + getGenericLambdaTemplateParameterList(LSI, *this); + + auto DC = Method->getLexicalDeclContext(); + Method->setLexicalDeclContext(LSI->Lambda); + if (TemplateParams) { + FunctionTemplateDecl *const TemplateMethod = FunctionTemplateDecl::Create( + Context, LSI->Lambda, Method->getLocation(), Method->getDeclName(), + TemplateParams, Method); + TemplateMethod->setAccess(AS_public); + Method->setDescribedFunctionTemplate(TemplateMethod); + LSI->Lambda->addDecl(TemplateMethod); + TemplateMethod->setLexicalDeclContext(DC); + } else { + LSI->Lambda->addDecl(Method); + } + LSI->Lambda->setLambdaIsGeneric(TemplateParams); + LSI->Lambda->setLambdaTypeInfo(MethodTyInfo); + + Method->setLexicalDeclContext(DC); + Method->setLocation(LambdaLoc); + Method->setInnerLocStart(CallOperatorLoc); + Method->setTypeSourceInfo(MethodTyInfo); + Method->setType(buildTypeForLambdaCallOperator(*this, LSI->Lambda, + TemplateParams, MethodTyInfo)); + Method->setConstexprKind(ConstexprKind); + if (!Params.empty()) { + CheckParmsForFunctionDef(Params, /*CheckParameterNames=*/false); + Method->setParams(Params); + for (auto P : Method->parameters()) + P->setOwningFunction(Method); + } + + buildLambdaScopeReturnType(*this, LSI, Method, HasExplicitResultType); +} + +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->HasKnownMutability = false; + LSI->AfterParameterList = false; + + 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 +973,37 @@ 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 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(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); - - // 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); + Intro.Range, nullptr, LambdaDependencyKind, Intro.Default); + LSI->Lambda = Class; - // OpenMP lambdas might get assumumption attributes. - if (LangOpts.OpenMP) - ActOnFinishedFunctionDefinitionInOpenMPAssumeScope(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. - // Number the lambda for linkage purposes if necessary. - handleLambdaNumbering(Class, Method); + CXXMethodDecl *Method = CreateLambdaCallOperator(Intro.Range, Class); + LSI->CallOperator = Method; + Method->setLexicalDeclContext(CurContext); - // 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 CaptureNames; + llvm::SmallSet 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 +1041,10 @@ } 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,13 +1087,25 @@ 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. + // Note that the 'reaching scope' check happens in tryCaptureVariable(). + if (!Var) { + Diag(C->Loc, diag::err_capture_does_not_name_variable) << C->Id; + 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)) { + 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(LSI->getCapture(Var).getLocation()) + << C->Id << SourceRange(It->second.Loc) << FixItHint::CreateRemoval( SourceRange(getLocForEndOfToken(PrevCaptureLoc), C->Loc)); } else @@ -1187,15 +1115,6 @@ 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. - // Note that the 'reaching scope' check happens in tryCaptureVariable(). - if (!Var) { - Diag(C->Loc, diag::err_capture_does_not_name_variable) << C->Id; - continue; - } - // Ignore invalid decls; they'll just confuse the code later. if (Var->isInvalidDecl()) continue; @@ -1223,22 +1142,232 @@ 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) { + + LambdaScopeInfo *const LSI = getCurrentLambdaScopeUnsafe(*this); + LSI->Mutable = MutableLoc.isValid(); + LSI->HasKnownMutability = true; + ContextRAII Context(*this, LSI->CallOperator, /*NewThisContext*/ false); + AddExplicitCapturesToContext(*this, LSI, Intro); + + // 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); + + for (auto &&C : LSI->DelayedCaptures) { + VarDecl *Var = C.second.Var; + if (Var && Var->isInitCapture()) { + PushOnScopeChains(Var, CurScope, false); + } + } +} + +void Sema::ActOnLambdaClosureParameters( + Scope *LambdaScope, MutableArrayRef Params) { + LambdaScopeInfo *const LSI = getCurrentLambdaScopeUnsafe(*this); + PushDeclContext(LambdaScope, LSI->CallOperator); + + for (const DeclaratorChunk::ParamInfo &P : Params) { + auto *Param = cast(P.Param); + Param->setOwningFunction(LSI->CallOperator); + if (Param->getIdentifier()) + PushOnScopeChains(Param, LambdaScope, false); + } + + LSI->AfterParameterList = true; +} + +void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, + Declarator &ParamInfo, + const DeclSpec &DS) { + + LambdaScopeInfo *const LSI = getCurrentLambdaScopeUnsafe(*this); + LSI->CallOperator->setConstexprKind(DS.getConstexprSpecifier()); + + SmallVector Params; + bool ExplicitResultType; + + SourceLocation TypeLoc, CallOperatorLoc; + if (ParamInfo.getNumTypeObjects() == 0) { + CallOperatorLoc = 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(); + CallOperatorLoc = ParamInfo.getSourceRange().getEnd(); + } - // Add lambda parameters into scope. - addLambdaParameters(Intro.Captures, Method, CurScope); + CXXRecordDecl *Class = LSI->Lambda; + CXXMethodDecl *Method = LSI->CallOperator; + + TypeSourceInfo *MethodTyInfo = getLambdaType( + *this, Intro, ParamInfo, getCurScope(), TypeLoc, 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(FTI.Params[I].Param); + Param->setScopeInfo(0, Params.size()); + Params.push_back(Param); + } + } + + CompleteLambdaCallOperator(Method, Intro.Range.getBegin(), CallOperatorLoc, + ParamInfo.getTrailingRequiresClause(), + MethodTyInfo, + ParamInfo.getDeclSpec().getConstexprSpecifier(), + Params, ExplicitResultType); + + ContextRAII ManglingContext(*this, Class->getDeclContext()); + + 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. diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -12966,44 +12966,6 @@ LambdaScopeInfo *LSI = getSema().PushLambdaScope(); Sema::FunctionScopeRAII FuncScopeCleanup(getSema()); - // Transform the template parameters, and add them to the current - // instantiation scope. The null case is handled correctly. - auto TPL = getDerived().TransformTemplateParameterList( - E->getTemplateParameterList()); - LSI->GLTemplateParameterList = TPL; - - // Transform the type of the original lambda's call operator. - // The transformation MUST be done in the CurrentInstantiationScope since - // it introduces a mapping of the original to the newly created - // transformed parameters. - TypeSourceInfo *NewCallOpTSI = nullptr; - { - TypeSourceInfo *OldCallOpTSI = E->getCallOperator()->getTypeSourceInfo(); - FunctionProtoTypeLoc OldCallOpFPTL = - OldCallOpTSI->getTypeLoc().getAs(); - - TypeLocBuilder NewCallOpTLBuilder; - SmallVector ExceptionStorage; - TreeTransform *This = this; // Work around gcc.gnu.org/PR56135. - QualType NewCallOpType = TransformFunctionProtoType( - NewCallOpTLBuilder, OldCallOpFPTL, nullptr, Qualifiers(), - [&](FunctionProtoType::ExceptionSpecInfo &ESI, bool &Changed) { - return This->TransformExceptionSpec(OldCallOpFPTL.getBeginLoc(), ESI, - ExceptionStorage, Changed); - }); - if (NewCallOpType.isNull()) - return ExprError(); - NewCallOpTSI = NewCallOpTLBuilder.getTypeSourceInfo(getSema().Context, - NewCallOpType); - } - - // Transform the trailing requires clause - ExprResult NewTrailingRequiresClause; - if (Expr *TRC = E->getCallOperator()->getTrailingRequiresClause()) - // FIXME: Concepts: Substitution into requires clause should only happen - // when checking satisfaction. - NewTrailingRequiresClause = getDerived().TransformExpr(TRC); - // Create the local class that will describe the lambda. // FIXME: DependencyKind below is wrong when substituting inside a templated @@ -13019,10 +12981,8 @@ DependencyKind = CXXRecordDecl::LDK_NeverDependent; CXXRecordDecl *OldClass = E->getLambdaClass(); - CXXRecordDecl *Class = - getSema().createLambdaClosureType(E->getIntroducerRange(), NewCallOpTSI, - DependencyKind, E->getCaptureDefault()); - + CXXRecordDecl *Class = getSema().createLambdaClosureType( + E->getIntroducerRange(), nullptr, DependencyKind, E->getCaptureDefault()); getDerived().transformedLocalDecl(OldClass, {Class}); Optional> Mangling; @@ -13032,35 +12992,19 @@ OldClass->getDeviceLambdaManglingNumber(), OldClass->getLambdaContextDecl()); - // Build the call operator. - CXXMethodDecl *NewCallOperator = getSema().startLambdaDefinition( - Class, E->getIntroducerRange(), NewCallOpTSI, - E->getCallOperator()->getEndLoc(), - NewCallOpTSI->getTypeLoc().castAs().getParams(), - E->getCallOperator()->getConstexprKind(), - NewTrailingRequiresClause.get()); - - LSI->CallOperator = NewCallOperator; + CXXMethodDecl *NewCallOperator = + getSema().CreateLambdaCallOperator(E->getIntroducerRange(), Class); + NewCallOperator->setLexicalDeclContext(getSema().CurContext); - getDerived().transformAttrs(E->getCallOperator(), NewCallOperator); - getDerived().transformedLocalDecl(E->getCallOperator(), {NewCallOperator}); - - // Number the lambda for linkage purposes if necessary. - getSema().handleLambdaNumbering(Class, NewCallOperator, Mangling); + // Enter the scope of the lambda. + getSema().buildLambdaScope(LSI, NewCallOperator, E->getIntroducerRange(), + E->getCaptureDefault(), E->getCaptureDefaultLoc(), + E->hasExplicitParameters(), E->isMutable()); // Introduce the context of the call operator. Sema::ContextRAII SavedContext(getSema(), NewCallOperator, /*NewThisContext*/false); - // Enter the scope of the lambda. - getSema().buildLambdaScope(LSI, NewCallOperator, - E->getIntroducerRange(), - E->getCaptureDefault(), - E->getCaptureDefaultLoc(), - E->hasExplicitParameters(), - E->hasExplicitResultType(), - E->isMutable()); - bool Invalid = false; // Transform captures. @@ -13100,7 +13044,8 @@ } 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; @@ -13181,6 +13126,60 @@ } getSema().finishLambdaExplicitCaptures(LSI); + // Transform the template parameters, and add them to the current + // instantiation scope. The null case is handled correctly. + auto TPL = getDerived().TransformTemplateParameterList( + E->getTemplateParameterList()); + LSI->GLTemplateParameterList = TPL; + + // Transform the type of the original lambda's call operator. + // The transformation MUST be done in the CurrentInstantiationScope since + // it introduces a mapping of the original to the newly created + // transformed parameters. + TypeSourceInfo *NewCallOpTSI = nullptr; + { + TypeSourceInfo *OldCallOpTSI = E->getCallOperator()->getTypeSourceInfo(); + FunctionProtoTypeLoc OldCallOpFPTL = + OldCallOpTSI->getTypeLoc().getAs(); + + TypeLocBuilder NewCallOpTLBuilder; + SmallVector ExceptionStorage; + TreeTransform *This = this; // Work around gcc.gnu.org/PR56135. + QualType NewCallOpType = TransformFunctionProtoType( + NewCallOpTLBuilder, OldCallOpFPTL, nullptr, Qualifiers(), + [&](FunctionProtoType::ExceptionSpecInfo &ESI, bool &Changed) { + return This->TransformExceptionSpec(OldCallOpFPTL.getBeginLoc(), ESI, + ExceptionStorage, Changed); + }); + if (NewCallOpType.isNull()) + return ExprError(); + NewCallOpTSI = + NewCallOpTLBuilder.getTypeSourceInfo(getSema().Context, NewCallOpType); + } + + // Transform the trailing requires clause + ExprResult NewTrailingRequiresClause; + if (Expr *TRC = E->getCallOperator()->getTrailingRequiresClause()) + // FIXME: Concepts: Substitution into requires clause should only happen + // when checking satisfaction. + NewTrailingRequiresClause = getDerived().TransformExpr(TRC); + + getSema().CompleteLambdaCallOperator( + NewCallOperator, E->getCallOperator()->getLocation(), + E->getCallOperator()->getInnerLocStart(), NewTrailingRequiresClause.get(), + NewCallOpTSI, E->getCallOperator()->getConstexprKind(), + NewCallOpTSI->getTypeLoc().castAs().getParams(), + E->hasExplicitResultType()); + + getDerived().transformAttrs(E->getCallOperator(), NewCallOperator); + getDerived().transformedLocalDecl(E->getCallOperator(), {NewCallOperator}); + + { + // Number the lambda for linkage purposes if necessary. + Sema::ContextRAII ManglingContext(getSema(), Class->getDeclContext()); + getSema().handleLambdaNumbering(Class, NewCallOperator, Mangling); + } + // FIXME: Sema's lambda-building mechanism expects us to push an expression // evaluation context even if we're not transforming the function body. getSema().PushExpressionEvaluationContext( diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p11-1y.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p11-1y.cpp --- a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p11-1y.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p11-1y.cpp @@ -12,16 +12,16 @@ 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"; }; } diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p4.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p4.cpp --- a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p4.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p4.cpp @@ -43,10 +43,21 @@ }(5); } -struct Incomplete; // expected-note{{forward declaration of 'Incomplete'}} +struct Incomplete; // expected-note 2{{forward declaration of 'Incomplete'}} void test_result_type(int N) { auto l1 = [] () -> Incomplete { }; // expected-error{{incomplete result type 'Incomplete' in lambda expression}} typedef int vla[N]; auto l2 = [] () -> vla { }; // expected-error{{function cannot return array type 'vla' (aka 'int[N]')}} } + +template +void test_result_type_tpl(int N) { + auto l1 = []() -> T {}; // expected-error{{incomplete result type 'Incomplete' in lambda expression}} + typedef int vla[N]; + auto l2 = []() -> vla {}; // expected-error{{function cannot return array type 'vla' (aka 'int[N]')}} +} + +void test_result_type_call() { + test_result_type_tpl(10); // expected-note {{requested here}} +} diff --git a/clang/test/SemaCXX/lambda-capture-type-deduction.cpp b/clang/test/SemaCXX/lambda-capture-type-deduction.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/lambda-capture-type-deduction.cpp @@ -0,0 +1,248 @@ +// RUN: %clang_cc1 -std=c++2b -verify -fsyntax-only %s + +template +constexpr bool is_same = false; + +template +constexpr bool is_same = true; + +void f() { + + int y; + + static_assert(is_same decltype((x)) { return x; }())>); + + static_assert(is_same decltype((x)) { return x; }())>); + + static_assert(is_same decltype((y)) { return y; }())>); + + static_assert(is_same decltype((y)) { return y; }())>); + + static_assert(is_same decltype((y)) { return y; }())>); + + static_assert(is_same 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) {}())); + static_assert(noexcept([x = 1] mutable noexcept(is_same) {}())); + static_assert(noexcept([y] noexcept(is_same) {}())); + static_assert(noexcept([y] mutable noexcept(is_same) {}())); + static_assert(noexcept([=] noexcept(is_same) {}())); + static_assert(noexcept([=] mutable noexcept(is_same) {}())); + static_assert(noexcept([&] noexcept(is_same) {}())); + static_assert(noexcept([&] mutable noexcept(is_same) {}())); + + static_assert(noexcept([&] mutable noexcept(!is_same) {}())); // expected-error {{static_assert failed due}} +} + +void test_requires() { + + int x; + + [x = 1]() requires is_same {} + (); + [x = 1]() mutable requires is_same {} + (); + [x]() requires is_same {} + (); + [x]() mutable requires is_same {} + (); + [=]() requires is_same {} + (); + [=]() mutable requires is_same {} + (); + [&]() requires is_same {} + (); + [&]() mutable requires is_same {} + (); + [&x]() requires is_same {} + (); + [&x]() mutable requires is_same {} + (); + + [x = 1]() requires is_same {} (); //expected-error {{no matching function for call to object of type}} \ + // expected-note {{candidate function not viable}} \ + // expected-note {{'is_same' evaluated to false}} + [x = 1]() mutable requires is_same {} (); // expected-error {{no matching function for call to object of type}} \ + // expected-note {{candidate function not viable}} \ + // expected-note {{'is_same' evaluated to false}} +} + +void err() { + int y, z; // expected-note 2{{declared here}} + + (void)[x = 1] // expected-note {{variable 'x' is explicitly captured here}} + requires(is_same) {}; // expected-error {{captured variable 'x' cannot appear here}} + + (void)[x = 1]{}; // expected-note {{variable 'x' is explicitly captured here}} \ + // expected-error {{captured variable 'x' cannot appear here}} + + (void)[=]{}; // expected-note {{variable 'y' is captured here}} \ + // expected-error {{captured variable 'y' cannot appear here}} + + (void)[z]{}; // expected-note {{variable 'z' is explicitly captured here}} \ + // expected-error {{captured variable 'z' cannot appear here}} +} + +void gnu_attributes() { + int y; + (void)[=]() __attribute__((diagnose_if(!is_same, "wrong type", "warning"))){}(); + (void)[=]() __attribute__((diagnose_if(!is_same, "wrong type", "warning"))){}(); + // expected-warning@-1 {{wrong type}} expected-note@-1{{'diagnose_if' attribute on 'operator()'}} + + (void)[=]() __attribute__((diagnose_if(!is_same, "wrong type", "warning"))) mutable {}(); + (void)[=]() __attribute__((diagnose_if(!is_same, "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, "wrong type", "warning"))){}(); + (void)[x=1]() __attribute__((diagnose_if(!is_same, "wrong type", "warning"))){}(); + // expected-warning@-1 {{wrong type}} expected-note@-1{{'diagnose_if' attribute on 'operator()'}} + + (void)[x=1]() __attribute__((diagnose_if(!is_same, "wrong type", "warning"))) mutable {}(); + (void)[x=1]() __attribute__((diagnose_if(!is_same, "wrong type", "warning"))) mutable {}(); + // expected-warning@-1 {{wrong type}} expected-note@-1{{'diagnose_if' attribute on 'operator()'}} +} + +void nested() { + int x, y, z; + (void)[&]( + decltype([&]( + decltype([=]( + decltype([&]( + decltype([&](decltype(x)) {})) {})) {})) {})){}; + + (void)[&]( + decltype([&]( + decltype([&]( + decltype([&]( + decltype([&](decltype(y)) {})) {})) {})) {})){}; + + (void)[=]( + decltype([=]( + decltype([=]( + decltype([=]( + decltype([&] {})) {})) {})) {})){}; +} + +template +void dependent(U&& u) { + [&]() requires is_same {}(); +} + +template +void dependent_init_capture(T x = 0) { + [ y = x + 1, x ]() mutable -> decltype(y + x) requires(is_same && is_same) { + return y; + } + (); + [ y = x + 1, x ]() -> decltype(y + x) requires(is_same && is_same) { + return y; + } + (); +} + +template +struct extract_type { + using type = T; +}; + +template +void dependent_variadic_capture(T... x) { + [... y = x, x... ](auto...) mutable -> typename extract_type::type requires((is_same && ...) && (is_same && ...)) { + return 0; + } + (x...); + [... y = x, x... ](auto...) -> typename extract_type::type requires((is_same && ...) && (is_same && ...)) { + return 0; + } + (x...); +} + +void test_dependent() { + int v = 0; + int & r = v; + const int & cr = v; + dependent(v); + dependent(r); + dependent(cr); + dependent_init_capture(0); + dependent_variadic_capture(1, 2, 3, 4); +} + +void check_params() { + int i = 0; + int &j = i; + (void)[=](decltype((j)) jp, decltype((i)) ip) { + static_assert(is_same); + static_assert(is_same); + static_assert(is_same); + static_assert(is_same); + }; + + (void)[=](decltype((j)) jp, decltype((i)) ip) mutable { + static_assert(is_same); + static_assert(is_same); + static_assert(is_same); + static_assert(is_same); + static_assert(is_same); + static_assert(is_same); + }; + + (void)[a = 0](decltype((a)) ap) mutable { + static_assert(is_same); + static_assert(is_same); + static_assert(is_same); + }; + (void)[a = 0](decltype((a)) ap) { + static_assert(is_same); + static_assert(is_same); + static_assert(is_same); + }; +} + +template +void check_params_tpl() { + T i = 0; + T &j = i; + (void)[=](decltype((j)) jp, decltype((i)) ip) { + static_assert(is_same); + static_assert(is_same); + static_assert(is_same); + static_assert(is_same); + }; + + (void)[=](decltype((j)) jp, decltype((i)) ip) mutable { + static_assert(is_same); + static_assert(is_same); + static_assert(is_same); + static_assert(is_same); + static_assert(is_same); + static_assert(is_same); + }; + + (void)[a = 0](decltype((a)) ap) mutable { + static_assert(is_same); + static_assert(is_same); + static_assert(is_same); + }; + (void)[a = 0](decltype((a)) ap) { + static_assert(is_same); + static_assert(is_same); + static_assert(is_same); + }; +} diff --git a/clang/test/SemaCXX/warn-shadow-in-lambdas.cpp b/clang/test/SemaCXX/warn-shadow-in-lambdas.cpp --- a/clang/test/SemaCXX/warn-shadow-in-lambdas.cpp +++ b/clang/test/SemaCXX/warn-shadow-in-lambdas.cpp @@ -95,7 +95,7 @@ #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. diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -1356,7 +1356,7 @@ Change scope of lambda trailing-return-type P2036R3 - No + Clang 15 Multidimensional subscript operator