[clang] [Clang] Implement P0963R3 "Structured binding declaration as a condition" (PR #130228)
Younan Zhang via cfe-commits
cfe-commits at lists.llvm.org
Thu Mar 6 19:44:53 PST 2025
https://github.com/zyn0217 created https://github.com/llvm/llvm-project/pull/130228
Todo:
- [ ] Adapt the bytecode interpreter to the new evaluation model
- [ ] Tests for CG & Constant evaluation
- [ ] Release notes
With this patch, the example shown in https://godbolt.org/z/b64x65716 (the R2 semantics) works now.
>From 12ac5199c34a7e6811a2f1cf7b5750b9e352a4c8 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Fri, 7 Mar 2025 11:38:11 +0800
Subject: [PATCH] [Clang] Implement P0963R3 "Structured binding declaration as
a condition"
---
clang/include/clang/AST/DeclCXX.h | 23 +++++++++++-------
.../clang/Basic/DiagnosticSemaKinds.td | 8 +++++--
clang/lib/AST/ASTImporter.cpp | 7 +++---
clang/lib/AST/DeclCXX.cpp | 21 +++++++++-------
clang/lib/AST/ExprConstant.cpp | 24 +++++++++++++++----
clang/lib/CodeGen/CGDecl.cpp | 13 ++++++----
clang/lib/CodeGen/CGStmt.cpp | 17 ++++++++++---
clang/lib/CodeGen/CodeGenFunction.cpp | 7 +++++-
clang/lib/CodeGen/CodeGenFunction.h | 5 +++-
clang/lib/Sema/SemaDecl.cpp | 6 ++---
clang/lib/Sema/SemaDeclCXX.cpp | 17 +++++++------
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 8 ++++---
clang/lib/Serialization/ASTReaderDecl.cpp | 9 ++++---
clang/lib/Serialization/ASTWriterDecl.cpp | 1 +
clang/test/AST/ByteCode/if.cpp | 2 +-
clang/test/Parser/cxx1z-decomposition.cpp | 12 +++++-----
clang/test/Parser/decomposed-condition.cpp | 24 +++++++++----------
clang/test/SemaCXX/decomposed-condition.cpp | 4 ++--
.../SemaCXX/lambda-capture-type-deduction.cpp | 24 +++++++++++++++++++
19 files changed, 160 insertions(+), 72 deletions(-)
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index dbd02ef7f8011..414a5aea32566 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -4224,15 +4224,18 @@ class DecompositionDecl final
: public VarDecl,
private llvm::TrailingObjects<DecompositionDecl, BindingDecl *> {
/// The number of BindingDecl*s following this object.
- unsigned NumBindings;
+ unsigned NumBindings : 31;
+
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned IsDecisionVariable : 1;
DecompositionDecl(ASTContext &C, DeclContext *DC, SourceLocation StartLoc,
SourceLocation LSquareLoc, QualType T,
TypeSourceInfo *TInfo, StorageClass SC,
- ArrayRef<BindingDecl *> Bindings)
+ ArrayRef<BindingDecl *> Bindings, bool IsDecisionVariable)
: VarDecl(Decomposition, C, DC, StartLoc, LSquareLoc, nullptr, T, TInfo,
SC),
- NumBindings(Bindings.size()) {
+ NumBindings(Bindings.size()), IsDecisionVariable(IsDecisionVariable) {
std::uninitialized_copy(Bindings.begin(), Bindings.end(),
getTrailingObjects<BindingDecl *>());
for (auto *B : Bindings) {
@@ -4253,12 +4256,14 @@ class DecompositionDecl final
static DecompositionDecl *Create(ASTContext &C, DeclContext *DC,
SourceLocation StartLoc,
- SourceLocation LSquareLoc,
- QualType T, TypeSourceInfo *TInfo,
- StorageClass S,
- ArrayRef<BindingDecl *> Bindings);
+ SourceLocation LSquareLoc, QualType T,
+ TypeSourceInfo *TInfo, StorageClass S,
+ ArrayRef<BindingDecl *> Bindings,
+ bool IsDecisionVariable);
+
static DecompositionDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID,
- unsigned NumBindings);
+ unsigned NumBindings,
+ bool IsDecisionVariable);
// Provide the range of bindings which may have a nested pack.
llvm::ArrayRef<BindingDecl *> bindings() const {
@@ -4285,6 +4290,8 @@ class DecompositionDecl final
std::move(Bindings));
}
+ bool isDecisionVariable() const { return IsDecisionVariable; }
+
void printName(raw_ostream &OS, const PrintingPolicy &Policy) const override;
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 0b121c04cd3c0..fbc91f2eb8dd6 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -529,8 +529,12 @@ def warn_cxx14_compat_decomp_decl : Warning<
def ext_decomp_decl : ExtWarn<
"decomposition declarations are a C++17 extension">, InGroup<CXX17>;
def ext_decomp_decl_cond : ExtWarn<
- "ISO C++17 does not permit structured binding declaration in a condition">,
- InGroup<DiagGroup<"binding-in-condition">>;
+ "structured binding declaration in a condition is a C++2c extenstion">,
+ InGroup<CXX26>;
+def warn_cxx26_decomp_decl_cond : Warning<
+ "structured binding declaration in a condition is incompatible with "
+ "C++ standards before C++2c">,
+ InGroup<CXXPre26Compat>, DefaultIgnore;
def err_decomp_decl_spec : Error<
"decomposition declaration cannot be declared "
"%plural{1:'%1'|:with '%1' specifiers}0">;
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 82180486f3c7c..95ca085c5b746 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -4614,9 +4614,10 @@ ExpectedDecl ASTNodeImporter::VisitVarDecl(VarDecl *D) {
ImportArrayChecked(FromDecomp->bindings(), Bindings.begin()))
return std::move(Err);
DecompositionDecl *ToDecomp;
- if (GetImportedOrCreateDecl(
- ToDecomp, FromDecomp, Importer.getToContext(), DC, ToInnerLocStart,
- Loc, ToType, ToTypeSourceInfo, D->getStorageClass(), Bindings))
+ if (GetImportedOrCreateDecl(ToDecomp, FromDecomp, Importer.getToContext(),
+ DC, ToInnerLocStart, Loc, ToType,
+ ToTypeSourceInfo, D->getStorageClass(),
+ Bindings, FromDecomp->isDecisionVariable()))
return ToDecomp;
ToVar = ToDecomp;
} else {
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 7eff776882629..fdad1150c8b1a 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -3520,21 +3520,24 @@ DecompositionDecl *DecompositionDecl::Create(ASTContext &C, DeclContext *DC,
SourceLocation LSquareLoc,
QualType T, TypeSourceInfo *TInfo,
StorageClass SC,
- ArrayRef<BindingDecl *> Bindings) {
+ ArrayRef<BindingDecl *> Bindings,
+ bool IsDecisionVariable) {
size_t Extra = additionalSizeToAlloc<BindingDecl *>(Bindings.size());
- return new (C, DC, Extra)
- DecompositionDecl(C, DC, StartLoc, LSquareLoc, T, TInfo, SC, Bindings);
+ return new (C, DC, Extra) DecompositionDecl(
+ C, DC, StartLoc, LSquareLoc, T, TInfo, SC, Bindings, IsDecisionVariable);
}
-DecompositionDecl *DecompositionDecl::CreateDeserialized(ASTContext &C,
- GlobalDeclID ID,
- unsigned NumBindings) {
+DecompositionDecl *
+DecompositionDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID,
+ unsigned NumBindings,
+ bool IsDecisionVariable) {
size_t Extra = additionalSizeToAlloc<BindingDecl *>(NumBindings);
- auto *Result = new (C, ID, Extra)
- DecompositionDecl(C, nullptr, SourceLocation(), SourceLocation(),
- QualType(), nullptr, StorageClass(), {});
+ auto *Result = new (C, ID, Extra) DecompositionDecl(
+ C, nullptr, SourceLocation(), SourceLocation(), QualType(), nullptr,
+ StorageClass(), {}, IsDecisionVariable);
// Set up and clean out the bindings array.
Result->NumBindings = NumBindings;
+ Result->IsDecisionVariable = IsDecisionVariable;
auto *Trail = Result->getTrailingObjects<BindingDecl *>();
for (unsigned I = 0; I != NumBindings; ++I)
new (Trail + I) BindingDecl*(nullptr);
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 7244120d1be51..564a47a839336 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -5218,16 +5218,28 @@ static bool EvaluateVarDecl(EvalInfo &Info, const VarDecl *VD) {
return true;
}
+static bool EvaluateDecompositionDeclInit(EvalInfo &Info,
+ const DecompositionDecl *DD);
+
static bool EvaluateDecl(EvalInfo &Info, const Decl *D) {
bool OK = true;
if (const VarDecl *VD = dyn_cast<VarDecl>(D))
OK &= EvaluateVarDecl(Info, VD);
- if (const DecompositionDecl *DD = dyn_cast<DecompositionDecl>(D))
- for (auto *BD : DD->flat_bindings())
- if (auto *VD = BD->getHoldingVar())
- OK &= EvaluateDecl(Info, VD);
+ if (const DecompositionDecl *DD = dyn_cast<DecompositionDecl>(D);
+ DD && !DD->isDecisionVariable())
+ OK &= EvaluateDecompositionDeclInit(Info, DD);
+
+ return OK;
+}
+
+static bool EvaluateDecompositionDeclInit(EvalInfo &Info,
+ const DecompositionDecl *DD) {
+ bool OK = true;
+ for (auto *BD : DD->flat_bindings())
+ if (auto *VD = BD->getHoldingVar())
+ OK &= EvaluateDecl(Info, VD);
return OK;
}
@@ -5251,6 +5263,10 @@ static bool EvaluateCond(EvalInfo &Info, const VarDecl *CondDecl,
return false;
if (!EvaluateAsBooleanCondition(Cond, Result, Info))
return false;
+ if (auto *DD = dyn_cast_if_present<DecompositionDecl>(CondDecl);
+ DD && DD->isDecisionVariable() &&
+ !EvaluateDecompositionDeclInit(Info, DD))
+ return false;
return Scope.destroy();
}
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 9cd5885aaae51..44d787a85f691 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -164,10 +164,9 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
assert(VD.isLocalVarDecl() &&
"Should not see file-scope variables inside a function!");
EmitVarDecl(VD);
- if (auto *DD = dyn_cast<DecompositionDecl>(&VD))
- for (auto *B : DD->flat_bindings())
- if (auto *HD = B->getHoldingVar())
- EmitVarDecl(*HD);
+ if (auto *DD = dyn_cast<DecompositionDecl>(&VD);
+ DD && !DD->isDecisionVariable())
+ EmitDecompositionVarInit(*DD);
return;
}
@@ -2057,6 +2056,12 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
/*IsAutoInit=*/false);
}
+void CodeGenFunction::EmitDecompositionVarInit(const DecompositionDecl &DD) {
+ for (auto *B : DD.flat_bindings())
+ if (auto *HD = B->getHoldingVar())
+ EmitVarDecl(*HD);
+}
+
/// Emit an expression as an initializer for an object (variable, field, etc.)
/// at the given location. The expression is not necessarily the normal
/// initializer for the object, and the address is not necessarily
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index abe799af32c6e..1d71f22dc2d47 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -913,6 +913,11 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
if (CondConstant)
incrementProfileCounter(&S);
if (Executed) {
+ if (auto *DD = dyn_cast_if_present<DecompositionDecl>(
+ S.getConditionVariable())) {
+ assert(DD->isDecisionVariable());
+ EmitDecompositionVarInit(*DD);
+ }
RunCleanupsScope ExecutedScope(*this);
EmitStmt(Executed);
}
@@ -952,10 +957,16 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
// there is a 'return' within the body, but this is particularly beneficial
// when one if-stmt is nested within another if-stmt so that all of the MC/DC
// updates are kept linear and consistent.
- if (!CGM.getCodeGenOpts().MCDCCoverage)
- EmitBranchOnBoolExpr(S.getCond(), ThenBlock, ElseBlock, ThenCount, LH);
- else {
+ if (!CGM.getCodeGenOpts().MCDCCoverage) {
+ EmitBranchOnBoolExpr(S.getCond(), ThenBlock, ElseBlock, ThenCount, LH,
+ nullptr, S.getConditionVariable());
+ } else {
llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond());
+ if (auto *DD =
+ dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable())) {
+ assert(DD->isDecisionVariable());
+ EmitDecompositionVarInit(*DD);
+ }
Builder.CreateCondBr(BoolCondVal, ThenBlock, ElseBlock);
}
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 08165e0b28406..c17a7d308bca8 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -1846,7 +1846,8 @@ void CodeGenFunction::EmitBranchToCounterBlock(
/// LHS and RHS nodes.
void CodeGenFunction::EmitBranchOnBoolExpr(
const Expr *Cond, llvm::BasicBlock *TrueBlock, llvm::BasicBlock *FalseBlock,
- uint64_t TrueCount, Stmt::Likelihood LH, const Expr *ConditionalOp) {
+ uint64_t TrueCount, Stmt::Likelihood LH, const Expr *ConditionalOp,
+ const VarDecl *ConditionalDecl) {
Cond = Cond->IgnoreParens();
if (const BinaryOperator *CondBOp = dyn_cast<BinaryOperator>(Cond)) {
@@ -2047,6 +2048,10 @@ void CodeGenFunction::EmitBranchOnBoolExpr(
CondV = EvaluateExprAsBool(Cond);
}
+ if (auto *DD = dyn_cast_if_present<DecompositionDecl>(ConditionalDecl);
+ DD && DD->isDecisionVariable())
+ EmitDecompositionVarInit(*DD);
+
// If not at the top of the logical operator nest, update MCDC temp with the
// boolean result of the evaluated condition.
if (!MCDCLogOpStack.empty()) {
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 018fc66b72a1e..3586594429da8 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -3474,6 +3474,8 @@ class CodeGenFunction : public CodeGenTypeCache {
void emitAutoVarTypeCleanup(const AutoVarEmission &emission,
QualType::DestructionKind dtorKind);
+ void EmitDecompositionVarInit(const DecompositionDecl &var);
+
/// Emits the alloca and debug information for the size expressions for each
/// dimension of an array. It registers the association of its (1-dimensional)
/// QualTypes and size expression's debug node, so that CGDebugInfo can
@@ -5180,7 +5182,8 @@ class CodeGenFunction : public CodeGenTypeCache {
void EmitBranchOnBoolExpr(const Expr *Cond, llvm::BasicBlock *TrueBlock,
llvm::BasicBlock *FalseBlock, uint64_t TrueCount,
Stmt::Likelihood LH = Stmt::LH_None,
- const Expr *ConditionalOp = nullptr);
+ const Expr *ConditionalOp = nullptr,
+ const VarDecl *ConditionalDecl = nullptr);
/// Given an assignment `*LHS = RHS`, emit a test that checks if \p RHS is
/// nonnull, if \p LHS is marked _Nonnull.
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 36de02d91e151..28215332f5ca3 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -7768,9 +7768,9 @@ NamedDecl *Sema::ActOnVariableDeclarator(
NewVD = cast<VarDecl>(Res.get());
AddToScope = false;
} else if (D.isDecompositionDeclarator()) {
- NewVD = DecompositionDecl::Create(Context, DC, D.getBeginLoc(),
- D.getIdentifierLoc(), R, TInfo, SC,
- Bindings);
+ NewVD = DecompositionDecl::Create(
+ Context, DC, D.getBeginLoc(), D.getIdentifierLoc(), R, TInfo, SC,
+ Bindings, D.getContext() == DeclaratorContext::Condition);
} else
NewVD = VarDecl::Create(Context, DC, D.getBeginLoc(),
D.getIdentifierLoc(), II, R, TInfo, SC);
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index a3a028b9485d6..0e6017b75cfd8 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -740,13 +740,16 @@ Sema::ActOnDecompositionDeclarator(Scope *S, Declarator &D,
return nullptr;
}
- Diag(Decomp.getLSquareLoc(),
- !getLangOpts().CPlusPlus17
- ? diag::ext_decomp_decl
- : D.getContext() == DeclaratorContext::Condition
- ? diag::ext_decomp_decl_cond
- : diag::warn_cxx14_compat_decomp_decl)
- << Decomp.getSourceRange();
+ unsigned DiagID;
+ if (!getLangOpts().CPlusPlus17)
+ DiagID = diag::ext_decomp_decl;
+ else if (D.getContext() == DeclaratorContext::Condition) {
+ DiagID = getLangOpts().CPlusPlus26 ? diag::warn_cxx26_decomp_decl_cond
+ : diag::ext_decomp_decl_cond;
+ } else
+ DiagID = diag::warn_cxx14_compat_decomp_decl;
+
+ Diag(Decomp.getLSquareLoc(), DiagID) << Decomp.getSourceRange();
// The semantic context is always just the current context.
DeclContext *const DC = CurContext;
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index dd894df851488..c87ce2ae15ae9 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -14,6 +14,7 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTLambda.h"
#include "clang/AST/ASTMutationListener.h"
+#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/DependentDiagnostic.h"
#include "clang/AST/Expr.h"
@@ -1473,9 +1474,10 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D,
// Build the instantiated declaration.
VarDecl *Var;
if (Bindings)
- Var = DecompositionDecl::Create(SemaRef.Context, DC, D->getInnerLocStart(),
- D->getLocation(), DI->getType(), DI,
- D->getStorageClass(), *Bindings);
+ Var = DecompositionDecl::Create(
+ SemaRef.Context, DC, D->getInnerLocStart(), D->getLocation(),
+ DI->getType(), DI, D->getStorageClass(), *Bindings,
+ cast<DecompositionDecl>(D)->isDecisionVariable());
else
Var = VarDecl::Create(SemaRef.Context, DC, D->getInnerLocStart(),
D->getLocation(), D->getIdentifier(), DI->getType(),
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 2a580c44b94e5..113ed96ea2021 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -4115,9 +4115,12 @@ Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) {
case DECL_PARM_VAR:
D = ParmVarDecl::CreateDeserialized(Context, ID);
break;
- case DECL_DECOMPOSITION:
- D = DecompositionDecl::CreateDeserialized(Context, ID, Record.readInt());
- break;
+ case DECL_DECOMPOSITION: {
+ unsigned NumBindings = Record.readInt();
+ bool IsDecisionVariable = Record.readBool();
+ D = DecompositionDecl::CreateDeserialized(Context, ID, NumBindings,
+ IsDecisionVariable);
+ } break;
case DECL_BINDING:
D = BindingDecl::CreateDeserialized(Context, ID);
break;
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index a1810003f5425..c11866319f2b0 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -1327,6 +1327,7 @@ void ASTDeclWriter::VisitParmVarDecl(ParmVarDecl *D) {
void ASTDeclWriter::VisitDecompositionDecl(DecompositionDecl *D) {
// Record the number of bindings first to simplify deserialization.
Record.push_back(D->bindings().size());
+ Record.push_back(D->isDecisionVariable());
VisitVarDecl(D);
for (auto *B : D->bindings())
diff --git a/clang/test/AST/ByteCode/if.cpp b/clang/test/AST/ByteCode/if.cpp
index c48b2b8d378c8..909e08a22a283 100644
--- a/clang/test/AST/ByteCode/if.cpp
+++ b/clang/test/AST/ByteCode/if.cpp
@@ -54,7 +54,7 @@ namespace InitDecl {
constexpr char g(char const (&x)[2]) {
return 'x';
if (auto [a, b] = x) // both-error {{an array type is not allowed here}} \
- // both-warning {{ISO C++17 does not permit structured binding declaration in a condition}}
+ // both-warning {{structured binding declaration in a condition is a C++2c extenstion}}
;
}
static_assert(g("x") == 'x');
diff --git a/clang/test/Parser/cxx1z-decomposition.cpp b/clang/test/Parser/cxx1z-decomposition.cpp
index acf3f99069185..96e25e8d9593b 100644
--- a/clang/test/Parser/cxx1z-decomposition.cpp
+++ b/clang/test/Parser/cxx1z-decomposition.cpp
@@ -37,12 +37,12 @@ namespace OtherDecl {
void g() {
// A condition is allowed as a Clang extension.
// See commentary in test/Parser/decomposed-condition.cpp
- for (; auto [a, b, c] = S(); ) {} // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
- if (auto [a, b, c] = S()) {} // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
- if (int n; auto [a, b, c] = S()) {} // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
- switch (auto [a, b, c] = S()) {} // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{statement requires expression of integer type ('S' invalid)}}
- switch (int n; auto [a, b, c] = S()) {} // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{statement requires expression of integer type ('S' invalid)}}
- while (auto [a, b, c] = S()) {} // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
+ for (; auto [a, b, c] = S(); ) {} // pre2c-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
+ if (auto [a, b, c] = S()) {} // pre2c-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
+ if (int n; auto [a, b, c] = S()) {} // pre2c-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
+ switch (auto [a, b, c] = S()) {} // pre2c-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{statement requires expression of integer type ('S' invalid)}}
+ switch (int n; auto [a, b, c] = S()) {} // pre2c-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{statement requires expression of integer type ('S' invalid)}}
+ while (auto [a, b, c] = S()) {} // pre2c-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
// An exception-declaration is not a simple-declaration.
try {}
diff --git a/clang/test/Parser/decomposed-condition.cpp b/clang/test/Parser/decomposed-condition.cpp
index 4c635c4735db8..37a24f8f95093 100644
--- a/clang/test/Parser/decomposed-condition.cpp
+++ b/clang/test/Parser/decomposed-condition.cpp
@@ -33,33 +33,33 @@ Na g();
namespace CondInIf {
int h() {
- if (auto [ok, d] = f()) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}}
+ if (auto [ok, d] = f()) // expected-warning {{structured binding declaration in a condition is a C++2c extenstion}}
;
- if (auto [ok, d] = g()) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{value of type 'Na' is not contextually convertible to 'bool'}}
+ if (auto [ok, d] = g()) // expected-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{value of type 'Na' is not contextually convertible to 'bool'}}
;
- if (auto [value] = Get()) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}}
+ if (auto [value] = Get()) // expected-warning {{structured binding declaration in a condition is a C++2c extenstion}}
return value;
}
} // namespace CondInIf
namespace CondInWhile {
int h() {
- while (auto [ok, d] = f()) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}}
+ while (auto [ok, d] = f()) // expected-warning {{structured binding declaration in a condition is a C++2c extenstion}}
;
- while (auto [ok, d] = g()) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{value of type 'Na' is not contextually convertible to 'bool'}}
+ while (auto [ok, d] = g()) // expected-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{value of type 'Na' is not contextually convertible to 'bool'}}
;
- while (auto [value] = Get()) // expected-warning{{ISO C++17 does not permit structured binding declaration in a condition}}
+ while (auto [value] = Get()) // expected-warning{{structured binding declaration in a condition is a C++2c extenstion}}
return value;
}
} // namespace CondInWhile
namespace CondInFor {
int h() {
- for (; auto [ok, d] = f();) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}}
+ for (; auto [ok, d] = f();) // expected-warning {{structured binding declaration in a condition is a C++2c extenstion}}
;
- for (; auto [ok, d] = g();) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{value of type 'Na' is not contextually convertible to 'bool'}}
+ for (; auto [ok, d] = g();) // expected-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{value of type 'Na' is not contextually convertible to 'bool'}}
;
- for (; auto [value] = Get();) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}}
+ for (; auto [value] = Get();) // expected-warning {{structured binding declaration in a condition is a C++2c extenstion}}
return value;
}
} // namespace CondInFor
@@ -74,11 +74,11 @@ struct IntegerLike {
namespace CondInSwitch {
int h(IntegerLike x) {
- switch (auto [ok, d] = x) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}}
+ switch (auto [ok, d] = x) // expected-warning {{structured binding declaration in a condition is a C++2c extenstion}}
;
- switch (auto [ok, d] = g()) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{statement requires expression of integer type ('Na' invalid)}}
+ switch (auto [ok, d] = g()) // expected-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{statement requires expression of integer type ('Na' invalid)}}
;
- switch (auto [value] = Get()) {// expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}}
+ switch (auto [value] = Get()) {// expected-warning {{structured binding declaration in a condition is a C++2c extenstion}}
// expected-warning at -1{{switch condition has boolean value}}
case 1:
return value;
diff --git a/clang/test/SemaCXX/decomposed-condition.cpp b/clang/test/SemaCXX/decomposed-condition.cpp
index e55bbee3134ca..32ab3a34f567b 100644
--- a/clang/test/SemaCXX/decomposed-condition.cpp
+++ b/clang/test/SemaCXX/decomposed-condition.cpp
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -std=c++1z -Wno-binding-in-condition -verify %s
-// RUN: %clang_cc1 -std=c++1z -Wno-binding-in-condition -verify %s -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 -std=c++1z -Wno-c++26-extensions -verify %s
+// RUN: %clang_cc1 -std=c++1z -Wno-c++26-extensions -verify %s -fexperimental-new-constant-interpreter
struct X {
bool flag;
diff --git a/clang/test/SemaCXX/lambda-capture-type-deduction.cpp b/clang/test/SemaCXX/lambda-capture-type-deduction.cpp
index b7a3d77cfc2f4..0e83be712052a 100644
--- a/clang/test/SemaCXX/lambda-capture-type-deduction.cpp
+++ b/clang/test/SemaCXX/lambda-capture-type-deduction.cpp
@@ -349,3 +349,27 @@ void h() {
}
} // namespace GH84961
+
+#if 0
+namespace GH58955 {
+
+template <int base> constexpr int foo(int exp) { return base * exp; }
+
+int bar(auto) {
+ constexpr int pack_size = 42;
+
+ [] {
+ return foo<10>(pack_size);
+ }();
+
+ int need_captures = 24; // expected-note {{declared here}}
+ [] { // expected-note {{lambda expression}} \
+ // expected-note 2{{by value}} \
+ // expected-note 2{{by reference}}
+ return foo<11>(need_captures);
+ // expected-error at -1 {{'need_captures' cannot be implicitly captured}}
+ }();
+}
+
+} // namespace GH58955
+#endif
More information about the cfe-commits
mailing list