[clang] 74ce297 - Revert "[Clang] Implement Change scope of lambda trailing-return-type"
Jordan Rupprecht via cfe-commits
cfe-commits at lists.llvm.org
Fri Feb 3 08:49:42 PST 2023
Author: Jordan Rupprecht
Date: 2023-02-03T08:49:34-08:00
New Revision: 74ce297045bac4bc475b8e762d2a1ea19bb16d3c
URL: https://github.com/llvm/llvm-project/commit/74ce297045bac4bc475b8e762d2a1ea19bb16d3c
DIFF: https://github.com/llvm/llvm-project/commit/74ce297045bac4bc475b8e762d2a1ea19bb16d3c.diff
LOG: Revert "[Clang] Implement Change scope of lambda trailing-return-type"
This reverts commit d708a186b6a9b050d09558163dd353d9f738c82d (and typo fix e4bc9898ddbeb70bc49d713bbf863f050f21e03f). It causes a compilation error for this:
```
struct StringLiteral {
template <int N>
StringLiteral(const char (&array)[N])
__attribute__((enable_if(N > 0 && N == __builtin_strlen(array) + 1,
"invalid string literal")));
};
struct Message {
Message(StringLiteral);
};
void Func1() {
auto x = Message("x"); // Note: this is fine
// Note: "xx\0" to force a different type, StringLiteral<3>, otherwise this
// successfully builds.
auto y = [&](decltype(Message("xx"))) {};
// ^ fails with: repro.cc:18:13: error: reference to local variable 'array'
// declared in enclosing function 'StringLiteral::StringLiteral<3>'
(void)x;
(void)y;
}
```
More details posted to D124351.
Added:
Modified:
clang/docs/ReleaseNotes.rst
clang/include/clang/AST/DeclCXX.h
clang/include/clang/Sema/Scope.h
clang/include/clang/Sema/ScopeInfo.h
clang/include/clang/Sema/Sema.h
clang/lib/Parse/ParseExprCXX.cpp
clang/lib/Sema/Scope.cpp
clang/lib/Sema/Sema.cpp
clang/lib/Sema/SemaCXXScopeSpec.cpp
clang/lib/Sema/SemaConcept.cpp
clang/lib/Sema/SemaExpr.cpp
clang/lib/Sema/SemaExprCXX.cpp
clang/lib/Sema/SemaLambda.cpp
clang/lib/Sema/SemaTemplateInstantiate.cpp
clang/lib/Sema/TreeTransform.h
clang/test/CXX/expr/expr.prim/expr.prim.lambda/p11-1y.cpp
clang/test/CXX/expr/expr.prim/expr.prim.lambda/p4.cpp
clang/test/SemaCXX/warn-shadow-in-lambdas.cpp
clang/www/cxx_status.html
Removed:
clang/test/SemaCXX/lambda-capture-type-deduction.cpp
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index d2f1919f24a3..4463f0317454 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -130,11 +130,6 @@ C++20 Feature Support
C++2b Feature Support
^^^^^^^^^^^^^^^^^^^^^
-- Implemented `P2036R3: Change scope of lambda trailing-return-type <https://wg21.link/P2036R3>`_
- and `P2579R0 Mitigation strategies for P2036 <https://wg21.link/P2579R0>`_.
- These proposals modify 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/HIP Language Changes in Clang
----------------------------------
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index ff8f8a1bb12d..11276c77490c 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -1092,11 +1092,6 @@ class CXXRecordDecl : public RecordDecl {
unsigned capture_size() const { return getLambdaData().NumCaptures; }
- const LambdaCapture *getCapture(unsigned I) const {
- assert(isLambda() && I < capture_size() && "invalid index for capture");
- return captures_begin() + I;
- }
-
using conversion_iterator = UnresolvedSetIterator;
conversion_iterator conversion_begin() const {
@@ -1831,20 +1826,6 @@ class CXXRecordDecl : public RecordDecl {
return getLambdaData().MethodTyInfo;
}
- void setLambdaTypeInfo(TypeSourceInfo *TS) {
- assert(DefinitionData && DefinitionData->IsLambda &&
- "setting lambda property of non-lambda class");
- auto &DL = static_cast<LambdaDefinitionData &>(*DefinitionData);
- DL.MethodTyInfo = TS;
- }
-
- void setLambdaIsGeneric(bool IsGeneric) {
- assert(DefinitionData && DefinitionData->IsLambda &&
- "setting lambda property of non-lambda class");
- auto &DL = static_cast<LambdaDefinitionData &>(*DefinitionData);
- 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/Sema/Scope.h b/clang/include/clang/Sema/Scope.h
index 9e81706cd2aa..be5cdb62045b 100644
--- a/clang/include/clang/Sema/Scope.h
+++ b/clang/include/clang/Sema/Scope.h
@@ -145,11 +145,6 @@ class Scope {
/// This is a scope of some OpenMP directive with
/// order clause which specifies concurrent
OpenMPOrderClauseScope = 0x4000000,
- /// This is the scope for a lambda, after the lambda introducer.
- /// Lambdas need two FunctionPrototypeScope scopes (because there is a
- /// template scope in between), the outer scope does not increase the
- /// depth of recursion.
- LambdaScope = 0x8000000,
};
private:
diff --git a/clang/include/clang/Sema/ScopeInfo.h b/clang/include/clang/Sema/ScopeInfo.h
index 5888dee0a883..65fa18fbb290 100644
--- a/clang/include/clang/Sema/ScopeInfo.h
+++ b/clang/include/clang/Sema/ScopeInfo.h
@@ -838,11 +838,6 @@ class LambdaScopeInfo final :
/// The lambda's compiler-generated \c operator().
CXXMethodDecl *CallOperator = nullptr;
- /// Indicate that we parsed the parameter list
- /// at which point the mutability of the lambda
- /// is known.
- bool AfterParameterList = true;
-
/// Source range covering the lambda introducer [...].
SourceRange IntroducerRange;
@@ -854,9 +849,8 @@ class LambdaScopeInfo final :
/// explicit captures.
unsigned NumExplicitCaptures = 0;
- /// Whether this is a mutable lambda. Until the mutable keyword is parsed,
- /// we assume the lambda is mutable.
- bool Mutable = true;
+ /// Whether this is a mutable lambda.
+ bool Mutable = false;
/// Whether the (empty) parameter list is explicit.
bool ExplicitParams = false;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 67d55ab3d8c6..b8a0727d6510 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -7103,21 +7103,15 @@ class Sema final {
std::nullopt);
/// Endow the lambda scope info with the relevant properties.
- void buildLambdaScope(sema::LambdaScopeInfo *LSI, CXXMethodDecl *CallOperator,
+ void buildLambdaScope(sema::LambdaScopeInfo *LSI,
+ CXXMethodDecl *CallOperator,
SourceRange IntroducerRange,
LambdaCaptureDefault CaptureDefault,
- SourceLocation CaptureDefaultLoc, bool ExplicitParams,
+ 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,
- StorageClass SC, ArrayRef<ParmVarDecl *> 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.
@@ -7138,9 +7132,11 @@ class Sema final {
///
/// 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, DeclContext *DeclCtx);
+ VarDecl *createLambdaInitCaptureVarDecl(SourceLocation Loc,
+ QualType InitCaptureType,
+ SourceLocation EllipsisLoc,
+ IdentifierInfo *Id,
+ unsigned InitStyle, Expr *Init);
/// Add an init-capture to a lambda scope.
void addInitCapture(sema::LambdaScopeInfo *LSI, VarDecl *Var,
@@ -7150,38 +7146,28 @@ class Sema final {
/// given lambda.
void finishLambdaExplicitCaptures(sema::LambdaScopeInfo *LSI);
- /// 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 ActOnLambdaExpressionAfterIntroducer(LambdaIntroducer &Intro,
- Scope *CurContext);
-
- /// This is called after parsing the explicit template parameter list
+ /// \brief This is called after parsing the explicit template parameter list
/// on a lambda (if it exists) in C++2a.
- void ActOnLambdaExplicitTemplateParameterList(LambdaIntroducer &Intro,
- SourceLocation LAngleLoc,
+ void ActOnLambdaExplicitTemplateParameterList(SourceLocation LAngleLoc,
ArrayRef<NamedDecl *> TParams,
SourceLocation RAngleLoc,
ExprResult RequiresClause);
- void ActOnLambdaClosureQualifiers(LambdaIntroducer &Intro,
- SourceLocation MutableLoc);
+ /// Introduce the lambda parameters into scope.
+ void addLambdaParameters(
+ ArrayRef<LambdaIntroducer::LambdaCapture> Captures,
+ CXXMethodDecl *CallOperator, Scope *CurScope);
- void ActOnLambdaClosureParameters(
- Scope *LambdaScope,
- MutableArrayRef<DeclaratorChunk::ParamInfo> ParamInfo);
+ /// Deduce a block or lambda's return type based on the return
+ /// statements present in the body.
+ void deduceClosureReturnType(sema::CapturingScopeInfo &CSI);
/// 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, const DeclSpec &DS);
+ Declarator &ParamInfo, Scope *CurScope);
/// ActOnLambdaError - If there is an error parsing a lambda, this callback
/// is invoked to pop the information about the lambda.
@@ -7276,13 +7262,6 @@ class Sema final {
LocalInstantiationScope &Scope,
const MultiLevelTemplateArgumentList &TemplateArgs);
- /// Introduce the instantiated captures of the lambda into the local
- /// instantiation scope.
- bool addInstantiatedCapturesToScope(
- FunctionDecl *Function, const FunctionDecl *PatternDecl,
- LocalInstantiationScope &Scope,
- const MultiLevelTemplateArgumentList &TemplateArgs);
-
/// used by SetupConstraintCheckingTemplateArgumentsAndScope to recursively(in
/// the case of lambdas) set up the LocalInstantiationScope of the current
/// function.
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 45460db85b87..7f09120574a7 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1275,19 +1275,18 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
PrettyStackTraceLoc CrashInfo(PP.getSourceManager(), LambdaBeginLoc,
"lambda expression parsing");
+
+
+ // FIXME: Call into Actions to add any init-capture declarations to the
+ // scope while parsing the lambda-declarator and compound-statement.
+
// Parse lambda-declarator[opt].
DeclSpec DS(AttrFactory);
Declarator D(DS, ParsedAttributesView::none(), DeclaratorContext::LambdaExpr);
TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth);
-
- ParseScope LambdaScope(this, Scope::LambdaScope | Scope::DeclScope |
- Scope::FunctionDeclarationScope |
- Scope::FunctionPrototypeScope);
-
Actions.PushLambdaScope();
- Actions.ActOnLambdaExpressionAfterIntroducer(Intro, getCurScope());
- ParsedAttributes Attributes(AttrFactory);
+ ParsedAttributes Attr(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.
@@ -1298,22 +1297,22 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
if (Tok.is(tok::kw___noinline__)) {
IdentifierInfo *AttrName = Tok.getIdentifierInfo();
SourceLocation AttrNameLoc = ConsumeToken();
- Attributes.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr,
- 0, ParsedAttr::AS_Keyword);
+ Attr.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr, 0,
+ ParsedAttr::AS_Keyword);
} else if (Tok.is(tok::kw___attribute))
- ParseGNUAttributes(Attributes, nullptr, &D);
+ ParseGNUAttributes(Attr, nullptr, &D);
else
break;
}
- D.takeAttributes(Attributes);
+ D.takeAttributes(Attr);
}
// Helper to emit a warning if we see a CUDA host/device/global attribute
// after '(...)'. nvcc doesn't accept this.
auto WarnIfHasCUDATargetAttr = [&] {
if (getLangOpts().CUDA)
- for (const ParsedAttr &A : Attributes)
+ for (const ParsedAttr &A : Attr)
if (A.getKind() == ParsedAttr::AT_CUDADevice ||
A.getKind() == ParsedAttr::AT_CUDAHost ||
A.getKind() == ParsedAttr::AT_CUDAGlobal)
@@ -1350,7 +1349,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
}
Actions.ActOnLambdaExplicitTemplateParameterList(
- Intro, LAngleLoc, TemplateParams, RAngleLoc, RequiresClause);
+ LAngleLoc, TemplateParams, RAngleLoc, RequiresClause);
++CurTemplateDepthTracker;
}
}
@@ -1368,36 +1367,32 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
TypeResult TrailingReturnType;
SourceLocation TrailingReturnTypeLoc;
- SourceLocation LParenLoc, RParenLoc;
- SourceLocation DeclEndLoc;
- bool HasParentheses = false;
- bool HasSpecifiers = false;
- SourceLocation MutableLoc;
-
- 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.
- MaybeParseAttributes(PAKM_GNU | PAKM_Declspec, Attributes);
- // Parse mutable-opt and/or constexpr-opt or consteval-opt, and update
- // the DeclEndLoc.
- SourceLocation ConstexprLoc;
- SourceLocation ConstevalLoc;
- SourceLocation StaticLoc;
-
- tryConsumeLambdaSpecifierToken(*this, MutableLoc, StaticLoc, ConstexprLoc,
- ConstevalLoc, DeclEndLoc);
-
- DiagnoseStaticSpecifierRestrictions(*this, StaticLoc, MutableLoc, Intro);
-
- addStaticToLambdaDeclSpecifier(*this, StaticLoc, DS);
- addConstexprToLambdaDeclSpecifier(*this, ConstexprLoc, DS);
- addConstevalToLambdaDeclSpecifier(*this, ConstevalLoc, DS);
- };
auto ParseLambdaSpecifiers =
- [&](MutableArrayRef<DeclaratorChunk::ParamInfo> ParamInfo,
+ [&](SourceLocation LParenLoc, SourceLocation RParenLoc,
+ 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 lambda specifiers and update the DeclEndLoc.
+ SourceLocation MutableLoc;
+ SourceLocation StaticLoc;
+ SourceLocation ConstexprLoc;
+ SourceLocation ConstevalLoc;
+ tryConsumeLambdaSpecifierToken(*this, MutableLoc, StaticLoc,
+ ConstexprLoc, ConstevalLoc, DeclEndLoc);
+
+ DiagnoseStaticSpecifierRestrictions(*this, StaticLoc, MutableLoc,
+ Intro);
+
+ addStaticToLambdaDeclSpecifier(*this, StaticLoc, DS);
+ addConstexprToLambdaDeclSpecifier(*this, ConstexprLoc, DS);
+ addConstevalToLambdaDeclSpecifier(*this, ConstevalLoc, DS);
// Parse exception-specification[opt].
ExceptionSpecificationType ESpecType = EST_None;
SourceRange ESpecRange;
@@ -1405,7 +1400,6 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
SmallVector<SourceRange, 2> DynamicExceptionRanges;
ExprResult NoexceptExpr;
CachedTokens *ExceptionSpecTokens;
-
ESpecType = tryParseExceptionSpecification(
/*Delayed=*/false, ESpecRange, DynamicExceptions,
DynamicExceptionRanges, NoexceptExpr, ExceptionSpecTokens);
@@ -1414,8 +1408,8 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
DeclEndLoc = ESpecRange.getEnd();
// Parse attribute-specifier[opt].
- if (MaybeParseCXX11Attributes(Attributes))
- DeclEndLoc = Attributes.Range.getEnd();
+ if (MaybeParseCXX11Attributes(Attr))
+ DeclEndLoc = Attr.Range.getEnd();
// Parse OpenCL addr space attribute.
if (Tok.isOneOf(tok::kw___private, tok::kw___global, tok::kw___local,
@@ -1451,32 +1445,27 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
/*ExceptionSpecTokens*/ nullptr,
/*DeclsInPrototype=*/std::nullopt, LParenLoc, FunLocalRangeEnd,
D, TrailingReturnType, TrailingReturnTypeLoc, &DS),
- std::move(Attributes), DeclEndLoc);
-
- Actions.ActOnLambdaClosureQualifiers(Intro, MutableLoc);
-
- if (HasParentheses && Tok.is(tok::kw_requires))
- ParseTrailingRequiresClause(D);
+ std::move(Attr), DeclEndLoc);
};
- 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)) {
+ ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope |
+ Scope::FunctionDeclarationScope |
+ Scope::DeclScope);
+
BalancedDelimiterTracker T(*this, tok::l_paren);
T.consumeOpen();
- LParenLoc = T.getOpenLocation();
+ SourceLocation LParenLoc = T.getOpenLocation();
+
+ // Parse parameter-declaration-clause.
+ SmallVector<DeclaratorChunk::ParamInfo, 16> ParamInfo;
+ SourceLocation EllipsisLoc;
if (Tok.isNot(tok::r_paren)) {
Actions.RecordParsingTemplateParameterDepth(
CurTemplateDepthTracker.getOriginalDepth());
- ParseParameterDeclarationClause(D, Attributes, ParamInfo, EllipsisLoc);
+ ParseParameterDeclarationClause(D, Attr, ParamInfo, EllipsisLoc);
// For a generic lambda, each 'auto' within the parameter declaration
// clause creates a template type parameter, so increment the depth.
// If we've parsed any explicit template parameters, then the depth will
@@ -1487,47 +1476,44 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
}
T.consumeClose();
- DeclEndLoc = RParenLoc = T.getCloseLocation();
- HasParentheses = true;
- }
- if (Tok.isOneOf(tok::kw_mutable, tok::arrow, tok::kw___attribute,
- tok::kw_constexpr, tok::kw_consteval, tok::kw_static,
- tok::kw___private, tok::kw___global, tok::kw___local,
- tok::kw___constant, tok::kw___generic, tok::kw_groupshared,
- tok::kw_requires, tok::kw_noexcept) ||
- (Tok.is(tok::l_square) && NextToken().is(tok::l_square))) {
- HasSpecifiers = true;
- if (!HasParentheses && !getLangOpts().CPlusPlus2b)
+ // 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_static,
+ tok::kw___private, tok::kw___global, tok::kw___local,
+ tok::kw___constant, tok::kw___generic,
+ tok::kw_groupshared, tok::kw_requires,
+ tok::kw_noexcept) ||
+ (Tok.is(tok::l_square) && NextToken().is(tok::l_square))) {
+ if (!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(), "() ");
- }
- if (HasParentheses || HasSpecifiers)
- ParseConstexprAndMutableSpecifiers();
-
- Actions.ActOnLambdaClosureParameters(getCurScope(), ParamInfo);
-
- if (!HasParentheses)
- Actions.ActOnLambdaClosureQualifiers(Intro, MutableLoc);
-
- if (HasSpecifiers || HasParentheses)
- ParseLambdaSpecifiers(ParamInfo, EllipsisLoc);
+ SourceLocation NoLoc;
+ // Parse lambda-specifiers.
+ std::vector<DeclaratorChunk::ParamInfo> EmptyParamInfo;
+ ParseLambdaSpecifiers(/*LParenLoc=*/NoLoc, /*RParenLoc=*/NoLoc,
+ EmptyParamInfo, /*EllipsisLoc=*/NoLoc);
+ }
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, DS);
+ Actions.ActOnStartOfLambdaDefinition(Intro, D, getCurScope());
// Parse compound-statement.
if (!Tok.is(tok::l_brace)) {
@@ -1539,7 +1525,6 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
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
index 4570d8c615fe..c995c7e65f4b 100644
--- a/clang/lib/Sema/Scope.cpp
+++ b/clang/lib/Sema/Scope.cpp
@@ -70,10 +70,8 @@ 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. Lambdas have an extra prototype
- // scope that doesn't add any depth.
- if (flags & FunctionPrototypeScope && !(flags & LambdaScope))
- PrototypeDepth++;
+ // If this is a prototype scope, record that.
+ if (flags & FunctionPrototypeScope) PrototypeDepth++;
if (flags & DeclScope) {
if (flags & FunctionPrototypeScope)
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 770b8db7e350..e5566b177aff 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -2333,8 +2333,7 @@ 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) &&
- LSI->AfterParameterList) {
+ if (LSI->Lambda && !LSI->Lambda->Encloses(CurContext)) {
// 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.
@@ -2360,8 +2359,8 @@ LambdaScopeInfo *Sema::getCurLambda(bool IgnoreNonLambdaCapturingScope) {
return nullptr;
}
auto *CurLSI = dyn_cast<LambdaScopeInfo>(*I);
- if (CurLSI && CurLSI->Lambda && CurLSI->CallOperator &&
- !CurLSI->Lambda->Encloses(CurContext) && CurLSI->AfterParameterList) {
+ if (CurLSI && CurLSI->Lambda &&
+ !CurLSI->Lambda->Encloses(CurContext)) {
// 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
index 1ef83ea52e73..daa61ba45e8e 100644
--- a/clang/lib/Sema/SemaCXXScopeSpec.cpp
+++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp
@@ -292,11 +292,6 @@ 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()) {
@@ -313,6 +308,9 @@ 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;
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 765110d6b230..a92bbde113fc 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -519,55 +519,6 @@ bool Sema::CheckConstraintSatisfaction(const Expr *ConstraintExpr,
.isInvalid();
}
-bool Sema::addInstantiatedCapturesToScope(
- FunctionDecl *Function, const FunctionDecl *PatternDecl,
- LocalInstantiationScope &Scope,
- const MultiLevelTemplateArgumentList &TemplateArgs) {
- const auto *LambdaClass = cast<CXXMethodDecl>(Function)->getParent();
- const auto *LambdaPattern = cast<CXXMethodDecl>(PatternDecl)->getParent();
-
- unsigned Instantiated = 0;
- for (const LambdaCapture &CapturePattern : LambdaPattern->captures()) {
- if (!CapturePattern.capturesVariable()) {
- Instantiated++;
- continue;
- }
- const ValueDecl *CapturedPattern = CapturePattern.getCapturedVar();
- if (!CapturedPattern->isParameterPack()) {
- ValueDecl *CapturedVar =
- LambdaClass->getCapture(Instantiated)->getCapturedVar();
- if (cast<CXXMethodDecl>(Function)->isConst()) {
- QualType T = CapturedVar->getType();
- T.addConst();
- CapturedVar->setType(T);
- }
- if (CapturedVar->isInitCapture())
- Scope.InstantiatedLocal(CapturedPattern, CapturedVar);
- Instantiated++;
- } else {
- Scope.MakeInstantiatedLocalArgPack(CapturedPattern);
- std::optional<unsigned> NumArgumentsInExpansion =
- getNumArgumentsInExpansion(CapturedPattern->getType(), TemplateArgs);
- if (!NumArgumentsInExpansion)
- continue;
- for (unsigned Arg = 0; Arg < *NumArgumentsInExpansion; ++Arg) {
- ValueDecl *CapturedVar =
- LambdaClass->getCapture(Instantiated)->getCapturedVar();
- if (cast<CXXMethodDecl>(Function)->isConst()) {
- QualType T = CapturedVar->getType();
- T.addConst();
- CapturedVar->setType(T);
- }
- if (CapturedVar->isInitCapture())
- Scope.InstantiatedLocalPackArg(CapturedPattern,
- cast<VarDecl>(CapturedVar));
- Instantiated++;
- }
- }
- }
- return false;
-}
-
bool Sema::SetupConstraintScope(
FunctionDecl *FD, std::optional<ArrayRef<TemplateArgument>> TemplateArgs,
MultiLevelTemplateArgumentList MLTAL, LocalInstantiationScope &Scope) {
@@ -601,11 +552,6 @@ bool Sema::SetupConstraintScope(
if (addInstantiatedParametersToScope(FD, FromMemTempl->getTemplatedDecl(),
Scope, MLTAL))
return true;
- // Make sure the captures are also added to the instantiation scope.
- if (isLambdaCallOperator(FD) &&
- addInstantiatedCapturesToScope(FD, FromMemTempl->getTemplatedDecl(),
- Scope, MLTAL))
- return true;
}
return false;
@@ -630,11 +576,6 @@ bool Sema::SetupConstraintScope(
// child-function.
if (addInstantiatedParametersToScope(FD, InstantiatedFrom, Scope, MLTAL))
return true;
-
- // Make sure the captures are also added to the instantiation scope.
- if (isLambdaCallOperator(FD) &&
- addInstantiatedCapturesToScope(FD, InstantiatedFrom, Scope, MLTAL))
- return true;
}
return false;
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 829765c7c461..6758797a1107 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -19082,6 +19082,11 @@ 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 = !VD->hasLocalStorage();
@@ -19107,34 +19112,12 @@ bool Sema::tryCaptureVariable(
bool Explicit = (Kind != TryCapture_Implicit);
unsigned FunctionScopesIndex = MaxFunctionScopesIndex;
do {
-
- LambdaScopeInfo *LSI = nullptr;
- if (!FunctionScopes.empty())
- LSI = dyn_cast_or_null<LambdaScopeInfo>(
- FunctionScopes[FunctionScopesIndex]);
-
- bool 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<ParmVarDecl>(Var) && !Var->getDeclContext()->isFunctionOrMethod())
- return true;
- }
- // 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 =
- !IsInScopeDeclarationContext
- ? DC->getParent()
- : getParentOfCapturingContextOrNull(DC, Var, ExprLoc,
- BuildAndDiagnose, *this);
+ DeclContext *ParentDC = 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.
@@ -19149,6 +19132,7 @@ 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,
DeclRefType)) {
@@ -19264,10 +19248,10 @@ bool Sema::tryCaptureVariable(
}
return true;
}
- Explicit = false;
+
FunctionScopesIndex--;
- if (IsInScopeDeclarationContext)
- DC = ParentDC;
+ DC = ParentDC;
+ Explicit = false;
} while (!VarDC->Equals(DC));
// Walk back down the scope stack, (e.g. from outer lambda to inner lambda)
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 17408ef9504e..ef012770747c 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1136,7 +1136,7 @@ static QualType adjustCVQualifiersForCXXThisWithinLambda(
if (C.isCopyCapture()) {
ClassType.removeLocalCVRQualifiers(Qualifiers::CVRMask);
- if (!CurLSI->Mutable)
+ if (CurLSI->CallOperator->isConst())
ClassType.addConst();
return ASTCtx.getPointerType(ClassType);
}
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index 2b2b2e3afd86..00ab6ba580bf 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -247,9 +247,8 @@ Sema::createLambdaClosureType(SourceRange IntroducerRange, TypeSourceInfo *Info,
DeclContext *DC = CurContext;
while (!(DC->isFunctionOrMethod() || DC->isRecord() || DC->isFileContext()))
DC = DC->getParent();
-
- bool IsGenericLambda =
- Info && getGenericLambdaTemplateParameterList(getCurLambda(), *this);
+ bool IsGenericLambda = getGenericLambdaTemplateParameterList(getCurLambda(),
+ *this);
// Start constructing the lambda class.
CXXRecordDecl *Class = CXXRecordDecl::CreateLambda(
Context, DC, Info, IntroducerRange.getBegin(), LambdaDependencyKind,
@@ -365,13 +364,14 @@ Sema::getCurrentMangleNumberContext(const DeclContext *DC) {
llvm_unreachable("unexpected context");
}
-static QualType
-buildTypeForLambdaCallOperator(Sema &S, clang::CXXRecordDecl *Class,
- TemplateParameterList *TemplateParams,
- TypeSourceInfo *MethodTypeInfo) {
- assert(MethodTypeInfo && "expected a non null type");
-
+CXXMethodDecl *Sema::startLambdaDefinition(
+ CXXRecordDecl *Class, SourceRange IntroducerRange,
+ TypeSourceInfo *MethodTypeInfo, SourceLocation EndLoc,
+ ArrayRef<ParmVarDecl *> Params, ConstexprSpecKind ConstexprKind,
+ StorageClass SC, Expr *TrailingRequiresClause) {
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.
@@ -379,12 +379,58 @@ buildTypeForLambdaCallOperator(Sema &S, clang::CXXRecordDecl *Class,
const FunctionProtoType *FPT = MethodType->castAs<FunctionProtoType>();
QualType Result = FPT->getReturnType();
if (Result->isUndeducedType()) {
- Result = S.SubstAutoTypeDependent(Result);
- MethodType = S.Context.getFunctionType(Result, FPT->getParamTypes(),
- FPT->getExtProtoInfo());
+ Result = SubstAutoTypeDependent(Result);
+ MethodType = Context.getFunctionType(Result, FPT->getParamTypes(),
+ FPT->getExtProtoInfo());
}
}
- return MethodType;
+
+ // 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, 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;
}
void Sema::handleLambdaNumbering(
@@ -442,25 +488,14 @@ void Sema::handleLambdaNumbering(
}
}
-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) {
+void Sema::buildLambdaScope(LambdaScopeInfo *LSI,
+ CXXMethodDecl *CallOperator,
+ SourceRange IntroducerRange,
+ LambdaCaptureDefault CaptureDefault,
+ SourceLocation CaptureDefaultLoc,
+ bool ExplicitParams,
+ bool ExplicitResultType,
+ bool Mutable) {
LSI->CallOperator = CallOperator;
CXXRecordDecl *LambdaClass = CallOperator->getParent();
LSI->Lambda = LambdaClass;
@@ -472,16 +507,30 @@ void Sema::buildLambdaScope(LambdaScopeInfo *LSI, CXXMethodDecl *CallOperator,
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(
- LambdaIntroducer &Intro, SourceLocation LAngleLoc,
- ArrayRef<NamedDecl *> TParams, SourceLocation RAngleLoc,
- ExprResult RequiresClause) {
+void Sema::ActOnLambdaExplicitTemplateParameterList(SourceLocation LAngleLoc,
+ ArrayRef<NamedDecl *> TParams,
+ SourceLocation RAngleLoc,
+ ExprResult RequiresClause) {
LambdaScopeInfo *LSI = getCurLambda();
assert(LSI && "Expected a lambda scope");
assert(LSI->NumExplicitTemplateParams == 0 &&
@@ -497,6 +546,35 @@ void Sema::ActOnLambdaExplicitTemplateParameterList(
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.
///
@@ -783,9 +861,11 @@ QualType Sema::buildLambdaInitCaptureInitialization(
return DeducedType;
}
-VarDecl *Sema::createLambdaInitCaptureVarDecl(
- SourceLocation Loc, QualType InitCaptureType, SourceLocation EllipsisLoc,
- IdentifierInfo *Id, unsigned InitStyle, Expr *Init, DeclContext *DeclCtx) {
+VarDecl *Sema::createLambdaInitCaptureVarDecl(SourceLocation Loc,
+ QualType InitCaptureType,
+ SourceLocation EllipsisLoc,
+ IdentifierInfo *Id,
+ unsigned InitStyle, Expr *Init) {
// FIXME: Retain the TypeSourceInfo from buildLambdaInitCaptureInitialization
// rather than reconstructing it here.
TypeSourceInfo *TSI = Context.getTrivialTypeSourceInfo(InitCaptureType, Loc);
@@ -796,8 +876,8 @@ VarDecl *Sema::createLambdaInitCaptureVarDecl(
// 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, DeclCtx, Loc, Loc, Id,
- InitCaptureType, TSI, SC_Auto);
+ VarDecl *NewVD = VarDecl::Create(Context, CurContext, Loc,
+ Loc, Id, InitCaptureType, TSI, SC_Auto);
NewVD->setInitCapture(true);
NewVD->setReferenced(true);
// FIXME: Pass in a VarDecl::InitializationStyle.
@@ -817,46 +897,35 @@ void Sema::addInitCapture(LambdaScopeInfo *LSI, VarDecl *Var,
Var->getType(), /*Invalid*/ false);
}
-// 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,
- std::nullopt, EPI);
- return S.Context.getTrivialTypeSourceInfo(MethodTy, Loc);
-}
+void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
+ Declarator &ParamInfo,
+ Scope *CurScope) {
+ LambdaScopeInfo *const LSI = getCurLambda();
+ assert(LSI && "LambdaScopeInfo should be on stack!");
-static TypeSourceInfo *getLambdaType(Sema &S, LambdaIntroducer &Intro,
- Declarator &ParamInfo, Scope *CurScope,
- SourceLocation Loc,
- bool &ExplicitResultType) {
+ // 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 =
+ CXXRecordDecl::LDK_Unknown;
+ if (LSI->NumExplicitTemplateParams > 0) {
+ auto *TemplateParamScope = CurScope->getTemplateParamParent();
+ assert(TemplateParamScope &&
+ "Lambda with explicit template param list should establish a "
+ "template param scope");
+ assert(TemplateParamScope->getParent());
+ if (TemplateParamScope->getParent()->getTemplateParamParent() != nullptr)
+ LambdaDependencyKind = CXXRecordDecl::LDK_AlwaysDependent;
+ } else if (CurScope->getTemplateParamParent() != nullptr) {
+ LambdaDependencyKind = CXXRecordDecl::LDK_AlwaysDependent;
+ }
- ExplicitResultType = false;
+ // Determine the signature of the call operator.
+ TypeSourceInfo *MethodTyInfo;
+ bool ExplicitParams = true;
+ bool ExplicitResultType = true;
+ bool ContainsUnexpandedParameterPack = false;
+ SourceLocation EndLoc;
+ SmallVector<ParmVarDecl *, 8> Params;
assert(
(ParamInfo.getDeclSpec().getStorageClassSpec() ==
@@ -866,174 +935,146 @@ static TypeSourceInfo *getLambdaType(Sema &S, LambdaIntroducer &Intro,
bool IsLambdaStatic =
ParamInfo.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static;
- TypeSourceInfo *MethodTyInfo;
-
if (ParamInfo.getNumTypeObjects() == 0) {
- MethodTyInfo = getDummyLambdaType(S, Loc);
+ // 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,
+ std::nullopt, 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() && !IsLambdaStatic) {
+ 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 (!FTI.hasMutableQualifier() && !IsLambdaStatic)
- FTI.getOrCreateMethodQualifiers().SetTypeQual(DeclSpec::TQ_const, Loc);
- if (ExplicitResultType && S.getLangOpts().HLSL) {
+ if (ExplicitResultType && getLangOpts().HLSL) {
QualType RetTy = FTI.getTrailingReturnType().get();
if (!RetTy.isNull()) {
// HLSL does not support specifying an address space on a lambda return
// type.
LangAS AddressSpace = RetTy.getAddressSpace();
if (AddressSpace != LangAS::Default)
- S.Diag(FTI.getTrailingReturnTypeLoc(),
- diag::err_return_value_with_address_space);
+ Diag(FTI.getTrailingReturnTypeLoc(),
+ diag::err_return_value_with_address_space);
}
}
- MethodTyInfo = S.GetTypeForDeclarator(ParamInfo, CurScope);
- assert(MethodTyInfo && "no type from lambda-declarator");
+ 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())
- 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,
- StorageClass SC, ArrayRef<ParmVarDecl *> Params,
- bool HasExplicitResultType) {
-
- LambdaScopeInfo *LSI = getCurrentLambdaScopeUnsafe(*this);
-
- if (TrailingRequiresClause)
- Method->setTrailingRequiresClause(TrailingRequiresClause);
-
- TemplateParameterList *TemplateParams =
- getGenericLambdaTemplateParameterList(LSI, *this);
-
- DeclContext *DC = Method->getLexicalDeclContext();
- Method->setLexicalDeclContext(LSI->Lambda);
- if (TemplateParams) {
- FunctionTemplateDecl *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);
+ DiagnoseUnexpandedParameterPack(Intro.Range.getBegin(), MethodTyInfo,
+ UPPC_DeclarationType);
}
- 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);
- Method->setStorageClass(SC);
- 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::ActOnLambdaExpressionAfterIntroducer(LambdaIntroducer &Intro,
- Scope *CurrentScope) {
-
- LambdaScopeInfo *LSI = getCurLambda();
- assert(LSI && "LambdaScopeInfo should be on stack!");
+ CXXRecordDecl *Class = createLambdaClosureType(
+ Intro.Range, MethodTyInfo, LambdaDependencyKind, Intro.Default);
+ CXXMethodDecl *Method =
+ startLambdaDefinition(Class, Intro.Range, MethodTyInfo, EndLoc, Params,
+ ParamInfo.getDeclSpec().getConstexprSpecifier(),
+ IsLambdaStatic ? SC_Static : SC_None,
+ ParamInfo.getTrailingRequiresClause());
+ if (ExplicitParams)
+ CheckCXXDefaultArguments(Method);
- 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->AfterParameterList = false;
+ // This represents the function body for the lambda function, check if we
+ // have to apply optnone due to a pragma.
+ AddRangeBasedOptnone(Method);
- assert(LSI->NumExplicitTemplateParams == 0);
+ // code_seg attribute on lambda apply to the method.
+ if (Attr *A = getImplicitCodeSegOrSectionAttrForFunction(Method, /*IsDefinition=*/true))
+ Method->addAttr(A);
- // 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 =
- CXXRecordDecl::LDK_Unknown;
- if (LSI->NumExplicitTemplateParams > 0) {
- Scope *TemplateParamScope = CurScope->getTemplateParamParent();
- assert(TemplateParamScope &&
- "Lambda with explicit template param list should establish a "
- "template param scope");
- assert(TemplateParamScope->getParent());
- if (TemplateParamScope->getParent()->getTemplateParamParent() != nullptr)
- LambdaDependencyKind = CXXRecordDecl::LDK_AlwaysDependent;
- } else if (CurScope->getTemplateParamParent() != nullptr) {
- LambdaDependencyKind = CXXRecordDecl::LDK_AlwaysDependent;
- }
+ // Attributes on the lambda apply to the method.
+ ProcessDeclAttributes(CurScope, Method, ParamInfo);
- CXXRecordDecl *Class = createLambdaClosureType(
- Intro.Range, nullptr, LambdaDependencyKind, Intro.Default);
- LSI->Lambda = Class;
+ // CUDA lambdas get implicit host and device attributes.
+ if (getLangOpts().CUDA)
+ CUDASetLambdaAttrs(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.
+ // OpenMP lambdas might get assumumption attributes.
+ if (LangOpts.OpenMP)
+ ActOnFinishedFunctionDefinitionInOpenMPAssumeScope(Method);
- CXXMethodDecl *Method = CreateLambdaCallOperator(Intro.Range, Class);
- LSI->CallOperator = Method;
- Method->setLexicalDeclContext(CurContext);
+ // Number the lambda for linkage purposes if necessary.
+ handleLambdaNumbering(Class, Method);
+ // Introduce the function call operator as the current declaration context.
PushDeclContext(CurScope, Method);
- bool ContainsUnexpandedParameterPack = false;
+ // 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);
// Distinct capture names, for diagnostics.
- llvm::DenseMap<IdentifierInfo *, ValueDecl *> 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);
+ ? 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
@@ -1046,7 +1087,7 @@ void Sema::ActOnLambdaExpressionAfterIntroducer(LambdaIntroducer &Intro,
continue;
}
- // C++20 [expr.prim.lambda]p8:
+ // 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
@@ -1112,11 +1153,13 @@ void Sema::ActOnLambdaExpressionAfterIntroducer(LambdaIntroducer &Intro,
}
Var = createLambdaInitCaptureVarDecl(C->Loc, C->InitCaptureType.get(),
C->EllipsisLoc, C->Id, InitStyle,
- C->Init.get(), Method);
- assert(Var && "createLambdaInitCaptureVarDecl returned a null VarDecl?");
- if (auto *V = dyn_cast<VarDecl>(Var))
- CheckShadow(CurrentScope, V);
- PushOnScopeChains(Var, CurrentScope, false);
+ 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);
} else {
assert(C->InitKind == LambdaCaptureInitKind::NoInit &&
"init capture has valid but null init?");
@@ -1162,33 +1205,31 @@ void Sema::ActOnLambdaExpressionAfterIntroducer(LambdaIntroducer &Intro,
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 (auto [It, Inserted] = CaptureNames.insert(std::pair{C->Id, Var});
- !Inserted) {
- if (C->InitKind == LambdaCaptureInitKind::NoInit &&
- !Var->isInitCapture()) {
+ if (!CaptureNames.insert(C->Id).second) {
+ if (Var && LSI->isCaptured(Var)) {
Diag(C->Loc, diag::err_capture_more_than_once)
- << C->Id << It->second->getBeginLoc()
+ << C->Id << SourceRange(LSI->getCapture(Var).getLocation())
<< FixItHint::CreateRemoval(
SourceRange(getLocForEndOfToken(PrevCaptureLoc), C->Loc));
} else
// Previous capture captured something
diff erent (one or both was
- // an init-capture): no fixit.
+ // 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.
+ // 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;
@@ -1220,167 +1261,20 @@ void Sema::ActOnLambdaExpressionAfterIntroducer(LambdaIntroducer &Intro,
if (C->Init.isUsable()) {
addInitCapture(LSI, cast<VarDecl>(Var), C->Kind == LCK_ByRef);
- PushOnScopeChains(Var, CurScope, false);
} else {
- TryCaptureKind Kind = C->Kind == LCK_ByRef ? TryCapture_ExplicitByRef
- : TryCapture_ExplicitByVal;
+ TryCaptureKind Kind = C->Kind == LCK_ByRef ? TryCapture_ExplicitByRef :
+ TryCapture_ExplicitByVal;
tryCaptureVariable(Var, C->Loc, Kind, EllipsisLoc);
}
if (!LSI->Captures.empty())
LSI->ExplicitCaptureRanges[LSI->Captures.size() - 1] = C->ExplicitRange;
}
finishLambdaExplicitCaptures(LSI);
- LSI->ContainsUnexpandedParameterPack |= ContainsUnexpandedParameterPack;
- PopDeclContext();
-}
-
-void Sema::ActOnLambdaClosureQualifiers(LambdaIntroducer &Intro,
- SourceLocation MutableLoc) {
-
- LambdaScopeInfo *LSI = getCurrentLambdaScopeUnsafe(*this);
- LSI->Mutable = MutableLoc.isValid();
- ContextRAII Context(*this, LSI->CallOperator, /*NewThisContext*/ false);
-
- // 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);
-}
-
-void Sema::ActOnLambdaClosureParameters(
- Scope *LambdaScope, MutableArrayRef<DeclaratorChunk::ParamInfo> Params) {
- LambdaScopeInfo *LSI = getCurrentLambdaScopeUnsafe(*this);
- PushDeclContext(LambdaScope, LSI->CallOperator);
-
- for (const DeclaratorChunk::ParamInfo &P : Params) {
- auto *Param = cast<ParmVarDecl>(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 *LSI = getCurrentLambdaScopeUnsafe(*this);
- LSI->CallOperator->setConstexprKind(DS.getConstexprSpecifier());
-
- SmallVector<ParmVarDecl *, 8> 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();
- }
-
- 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<ParmVarDecl>(FTI.Params[I].Param);
- Param->setScopeInfo(0, Params.size());
- Params.push_back(Param);
- }
- }
-
- bool IsLambdaStatic =
- ParamInfo.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static;
-
- CompleteLambdaCallOperator(
- Method, Intro.Range.getBegin(), CallOperatorLoc,
- ParamInfo.getTrailingRequiresClause(), MethodTyInfo,
- ParamInfo.getDeclSpec().getConstexprSpecifier(),
- IsLambdaStatic ? SC_Static : SC_None, 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->Captures) {
- if (!C.isVariableCapture())
- continue;
- ValueDecl *Var = C.getVariable();
- if (Var && Var->isInitCapture()) {
- PushOnScopeChains(Var, CurScope, false);
- }
- }
-
- 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;
- };
+ LSI->ContainsUnexpandedParameterPack |= ContainsUnexpandedParameterPack;
- for (ParmVarDecl *P : Params) {
- if (!P->getIdentifier())
- continue;
- if (CheckRedefinition(P))
- CheckShadow(CurScope, P);
- PushOnScopeChains(P, CurScope);
- }
+ // Add lambda parameters into scope.
+ addLambdaParameters(Intro.Captures, Method, CurScope);
// Enter a new evaluation context to insulate the lambda from any
// cleanups from the enclosing full-expression.
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index da66ffea46f4..2790e78aa53a 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1231,8 +1231,7 @@ namespace {
// We recreated a local declaration, but not by instantiating it. There
// may be pending dependent diagnostics to produce.
- if (auto *DC = dyn_cast<DeclContext>(Old);
- DC && DC->isDependentContext() && DC->isFunctionOrMethod())
+ if (auto *DC = dyn_cast<DeclContext>(Old); DC && DC->isDependentContext())
SemaRef.PerformDependentDiagnostics(DC, TemplateArgs);
}
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 6b33c8f2fb82..ef56f6219868 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -13238,6 +13238,37 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
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<FunctionProtoTypeLoc>();
+
+ TypeLocBuilder NewCallOpTLBuilder;
+ SmallVector<QualType, 4> 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);
+ }
+
// Create the local class that will describe the lambda.
// FIXME: DependencyKind below is wrong when substituting inside a templated
@@ -13254,8 +13285,10 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
DependencyKind = CXXRecordDecl::LDK_NeverDependent;
CXXRecordDecl *OldClass = E->getLambdaClass();
- CXXRecordDecl *Class = getSema().createLambdaClosureType(
- E->getIntroducerRange(), nullptr, DependencyKind, E->getCaptureDefault());
+ CXXRecordDecl *Class =
+ getSema().createLambdaClosureType(E->getIntroducerRange(), NewCallOpTSI,
+ DependencyKind, E->getCaptureDefault());
+
getDerived().transformedLocalDecl(OldClass, {Class});
std::optional<std::tuple<bool, unsigned, unsigned, Decl *>> Mangling;
@@ -13265,19 +13298,36 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
OldClass->getDeviceLambdaManglingNumber(),
OldClass->getLambdaContextDecl());
- CXXMethodDecl *NewCallOperator =
- getSema().CreateLambdaCallOperator(E->getIntroducerRange(), Class);
- NewCallOperator->setLexicalDeclContext(getSema().CurContext);
+ // Build the call operator.
+ CXXMethodDecl *NewCallOperator = getSema().startLambdaDefinition(
+ Class, E->getIntroducerRange(), NewCallOpTSI,
+ E->getCallOperator()->getEndLoc(),
+ NewCallOpTSI->getTypeLoc().castAs<FunctionProtoTypeLoc>().getParams(),
+ E->getCallOperator()->getConstexprKind(),
+ E->getCallOperator()->getStorageClass(),
+ E->getCallOperator()->getTrailingRequiresClause());
- // Enter the scope of the lambda.
- getSema().buildLambdaScope(LSI, NewCallOperator, E->getIntroducerRange(),
- E->getCaptureDefault(), E->getCaptureDefaultLoc(),
- E->hasExplicitParameters(), E->isMutable());
+ LSI->CallOperator = NewCallOperator;
+
+ getDerived().transformAttrs(E->getCallOperator(), NewCallOperator);
+ getDerived().transformedLocalDecl(E->getCallOperator(), {NewCallOperator});
+
+ // Number the lambda for linkage purposes if necessary.
+ getSema().handleLambdaNumbering(Class, NewCallOperator, Mangling);
// 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.
@@ -13317,8 +13367,7 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
}
VarDecl *NewVD = getSema().createLambdaInitCaptureVarDecl(
OldVD->getLocation(), InitQualType, NewC.EllipsisLoc,
- OldVD->getIdentifier(), OldVD->getInitStyle(), Init.get(),
- getSema().CurContext);
+ OldVD->getIdentifier(), OldVD->getInitStyle(), Init.get());
if (!NewVD) {
Invalid = true;
break;
@@ -13398,55 +13447,6 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
}
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();
- auto OldCallOpFPTL =
- OldCallOpTSI->getTypeLoc().getAs<FunctionProtoTypeLoc>();
-
- TypeLocBuilder NewCallOpTLBuilder;
- SmallVector<QualType, 4> 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);
- }
-
- getSema().CompleteLambdaCallOperator(
- NewCallOperator, E->getCallOperator()->getLocation(),
- E->getCallOperator()->getInnerLocStart(),
- E->getCallOperator()->getTrailingRequiresClause(), NewCallOpTSI,
- E->getCallOperator()->getConstexprKind(),
- E->getCallOperator()->getStorageClass(),
- NewCallOpTSI->getTypeLoc().castAs<FunctionProtoTypeLoc>().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
index 67953c6a6f90..4a0cf39bc56b 100644
--- 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 @@ auto with_float_2 = [&f(f)] { // ok, refers to outer f
using T = double&;
};
-// Within the lambda-expression the identifier in the init-capture
-// hides any declaration of the same name in scopes enclosing
-// the lambda-expression.
+// 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.
void hiding() {
char c;
(void) [c("foo")] {
static_assert(sizeof(c) == sizeof(const char*), "");
};
- (void)[c("bar")]()->decltype(c) { // inner c
- return "baz";
+ (void) [c("bar")] () -> decltype(c) { // outer c, not init-capture
+ return "baz"; // expected-error {{cannot initialize}}
};
}
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
index 660f6091bb66..5f7f73d622d8 100644
--- 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,21 +43,10 @@ X infer_X_return_type_2(X x) {
}(5);
}
-struct Incomplete; // expected-note 2{{forward declaration of 'Incomplete'}}
+struct Incomplete; // expected-note{{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 <typename T>
-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<Incomplete>(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
deleted file mode 100644
index e22e3af02c5c..000000000000
--- a/clang/test/SemaCXX/lambda-capture-type-deduction.cpp
+++ /dev/null
@@ -1,243 +0,0 @@
-// 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))>) {}()));
-}
-
-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<const int &, decltype((x))> {} ();
- [x = 1]() mutable requires is_same<int &, decltype((x))> {} ();
-}
-
-void err() {
- int y, z;
- (void)[x = 1]<typename T>
- requires(is_same<const int &, decltype((x))>) {};
-
- (void)[x = 1]<typename T = decltype((x))>{};
-
- (void)[=]<typename T = decltype((y))>{};
-
- (void)[z]<typename T = decltype((z))>{};
-}
-
-void gnu_attributes() {
- int y;
- (void)[=]() __attribute__((diagnose_if(!is_same<decltype((y)), const int &>, "wrong type", "warning"))){}();
- // expected-warning at -1 {{wrong type}} expected-note at -1{{'diagnose_if' attribute on 'operator()'}}
- (void)[=]() __attribute__((diagnose_if(!is_same<decltype((y)), int &>, "wrong type", "warning"))){}();
-
- (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 at -1 {{wrong type}} expected-note at -1{{'diagnose_if' attribute on 'operator()'}}
-
-
- (void)[x=1]() __attribute__((diagnose_if(!is_same<decltype((x)), const int &>, "wrong type", "warning"))){}();
- // expected-warning at -1 {{wrong type}} expected-note at -1{{'diagnose_if' attribute on 'operator()'}}
- (void)[x=1]() __attribute__((diagnose_if(!is_same<decltype((x)), int &>, "wrong type", "warning"))){}();
-
- (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 at -1 {{wrong type}} expected-note at -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([&]<decltype(z)> {})) {})) {})) {})){};
-}
-
-template <typename T, typename U>
-void dependent(U&& u) {
- [&]() requires is_same<decltype(u), T> {}();
-}
-
-template <typename T>
-void dependent_init_capture(T x = 0) {
- [ y = x + 1, x ]() mutable -> decltype(y + x)
- requires(is_same<decltype((y)), int &>
- && is_same<decltype((x)), int &>) {
- return y;
- }
- ();
- [ y = x + 1, x ]() -> decltype(y + x)
- requires(is_same<decltype((y)), const int &>
- && is_same<decltype((x)), const int &>) {
- return y;
- }
- ();
-}
-
-template <typename T, typename...>
-struct extract_type {
- using type = T;
-};
-
-template <typename... T>
-void dependent_variadic_capture(T... x) {
- [... y = x, x... ](auto...) mutable -> typename extract_type<decltype(y)...>::type requires((is_same<decltype((y)), int &> && ...) && (is_same<decltype((x)), int &> && ...)) {
- return 0;
- }
- (x...);
- [... y = x, x... ](auto...) -> typename extract_type<decltype(y)...>::type requires((is_same<decltype((y)), const int &> && ...) && (is_same<decltype((x)), const int &> && ...)) {
- return 0;
- }
- (x...);
-}
-
-void test_dependent() {
- int v = 0;
- int & r = v;
- const int & cr = v;
- dependent<int&>(v);
- dependent<int&>(r);
- dependent<const int&>(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<const int&, decltype((j))>);
- static_assert(is_same<const int &, decltype((i))>);
- static_assert(is_same<int &, decltype((jp))>);
- static_assert(is_same<int &, decltype((ip))>);
- };
-
- (void)[=](decltype((j)) jp, decltype((i)) ip) mutable {
- static_assert(is_same<int &, decltype((j))>);
- static_assert(is_same<int &, decltype((i))>);
- static_assert(is_same<int &, decltype((jp))>);
- static_assert(is_same<int &, decltype((ip))>);
- static_assert(is_same<int &, decltype(jp)>);
- static_assert(is_same<int &, decltype(ip)>);
- };
-
- (void)[a = 0](decltype((a)) ap) mutable {
- static_assert(is_same<int &, decltype((a))>);
- static_assert(is_same<int, decltype(a)>);
- static_assert(is_same<int &, decltype(ap)>);
- };
- (void)[a = 0](decltype((a)) ap) {
- static_assert(is_same<const int &, decltype((a))>);
- static_assert(is_same<int, decltype(a)>);
- static_assert(is_same<int&, decltype((ap))>);
- };
-}
-
-template <typename T>
-void check_params_tpl() {
- T i = 0;
- T &j = i;
- (void)[=](decltype((j)) jp, decltype((i)) ip) {
- static_assert(is_same<const int&, decltype((j))>);
- static_assert(is_same<const int &, decltype((i))>);
- static_assert(is_same<const int &, decltype((jp))>);
- static_assert(is_same<const int &, decltype((ip))>);
- };
-
- (void)[=](decltype((j)) jp, decltype((i)) ip) mutable {
- static_assert(is_same<int &, decltype((j))>);
- static_assert(is_same<int &, decltype((i))>);
- static_assert(is_same<int &, decltype((jp))>);
- static_assert(is_same<int &, decltype((ip))>);
- static_assert(is_same<int &, decltype(jp)>);
- static_assert(is_same<int &, decltype(ip)>);
- };
-
- (void)[a = 0](decltype((a)) ap) mutable {
- static_assert(is_same<int &, decltype((a))>);
- static_assert(is_same<int, decltype(a)>);
- static_assert(is_same<int &, decltype(ap)>);
- };
- (void)[a = 0](decltype((a)) ap) {
- static_assert(is_same<const int &, decltype((a))>);
- static_assert(is_same<int, decltype(a)>);
- static_assert(is_same<int&, decltype((ap))>);
- };
-}
diff --git a/clang/test/SemaCXX/warn-shadow-in-lambdas.cpp b/clang/test/SemaCXX/warn-shadow-in-lambdas.cpp
index 9dddbf8b5f3b..a772af049ab4 100644
--- a/clang/test/SemaCXX/warn-shadow-in-lambdas.cpp
+++ b/clang/test/SemaCXX/warn-shadow-in-lambdas.cpp
@@ -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 2{{declaration shadows a local variable}}
+ auto l4 = [var = param] (int param) { ; }; // expected-warning {{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
index b64c2cf20da0..c9df1ed37350 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -1358,7 +1358,7 @@ <h2 id="cxx23">C++2b implementation status</h2>
<tr>
<td rowspan=2>Change scope of lambda trailing-return-type</td>
<td><a href="https://wg21.link/P2036R3">P2036R3</a></td>
- <td rowspan=2 class="unreleased" align="center">Clang 17</td>
+ <td rowspan=2 class="none" align="center">No</td>
</tr>
<tr>
<td><a href="https://wg21.link/P2579R0">P2579R0</a></td>
More information about the cfe-commits
mailing list