[clang] [Clang] Implement P0963R3 "Structured binding declaration as a condition" (PR #130228)
Younan Zhang via cfe-commits
cfe-commits at lists.llvm.org
Mon Mar 10 20:14:47 PDT 2025
https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/130228
>From 295b8173b6913d9014c5786eb4af0112384afa65 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 01/11] [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 ++--
18 files changed, 136 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;
>From 5cd91f8adf02d633e7503b144a2d2b264d50dcaf Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sun, 9 Mar 2025 23:29:02 +0800
Subject: [PATCH 02/11] Add a simple CG test, release note, comments for
decision variable [dcl.struct.bind]
---
clang/docs/ReleaseNotes.rst | 2 +
clang/include/clang/AST/DeclCXX.h | 4 ++
clang/lib/CodeGen/CGStmt.cpp | 21 ++++++
clang/lib/Sema/SemaDecl.cpp | 3 +-
clang/test/CodeGen/p0963r3.cpp | 108 ++++++++++++++++++++++++++++++
clang/www/cxx_status.html | 2 +-
6 files changed, 138 insertions(+), 2 deletions(-)
create mode 100644 clang/test/CodeGen/p0963r3.cpp
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 37ea963bf337d..bea43ebcedb2f 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -76,6 +76,8 @@ C++2c Feature Support
- Implemented `P1061R10 Structured Bindings can introduce a Pack <https://wg21.link/P1061R10>`_.
+- Implemented `P0963R3 Structured binding declaration as a condition <https://wg21.link/P0963R3>`_.
+
C++23 Feature Support
^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index 414a5aea32566..d82d9900945a1 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -4290,6 +4290,10 @@ class DecompositionDecl final
std::move(Bindings));
}
+ /// The decision variable of a condition that is a structured binding
+ /// declaration is specified in [dcl.struct.bind]p4:
+ /// If a structured binding declaration appears as a condition, the decision
+ /// variable of the condition is e.
bool isDecisionVariable() const { return IsDecisionVariable; }
void printName(raw_ostream &OS, const PrintingPolicy &Policy) const override;
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 1d71f22dc2d47..0a49eadbf33d1 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -1110,6 +1110,11 @@ void CodeGenFunction::EmitWhileStmt(const WhileStmt &S,
// execution of the loop body.
llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond());
+ if (auto *DD =
+ dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable());
+ DD && DD->isDecisionVariable())
+ EmitDecompositionVarInit(*DD);
+
// while(1) is common, avoid extra exit blocks. Be sure
// to correctly handle break/continue though.
llvm::ConstantInt *C = dyn_cast<llvm::ConstantInt>(BoolCondVal);
@@ -1343,6 +1348,12 @@ void CodeGenFunction::EmitForStmt(const ForStmt &S,
// C99 6.8.5p2/p4: The first substatement is executed if the expression
// compares unequal to 0. The condition must be a scalar type.
llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond());
+
+ if (auto *DD =
+ dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable());
+ DD && DD->isDecisionVariable())
+ EmitDecompositionVarInit(*DD);
+
llvm::MDNode *Weights =
createProfileWeightsForLoop(S.getCond(), getProfileCount(S.getBody()));
if (!Weights && CGM.getCodeGenOpts().OptimizationLevel)
@@ -2240,6 +2251,11 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) {
if (S.getConditionVariable())
EmitDecl(*S.getConditionVariable());
+ if (auto *DD =
+ dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable());
+ DD && DD->isDecisionVariable())
+ EmitDecompositionVarInit(*DD);
+
// At this point, we are no longer "within" a switch instance, so
// we can temporarily enforce this to ensure that any embedded case
// statements are not emitted.
@@ -2271,6 +2287,11 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) {
EmitDecl(*S.getConditionVariable());
llvm::Value *CondV = EmitScalarExpr(S.getCond());
+ if (auto *DD =
+ dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable());
+ DD && DD->isDecisionVariable())
+ EmitDecompositionVarInit(*DD);
+
// Create basic block to hold stuff that comes after switch
// statement. We also need to create a default block now so that
// explicit case ranges tests can have a place to jump to on
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 28215332f5ca3..675a928551fcb 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -7770,7 +7770,8 @@ NamedDecl *Sema::ActOnVariableDeclarator(
} else if (D.isDecompositionDeclarator()) {
NewVD = DecompositionDecl::Create(
Context, DC, D.getBeginLoc(), D.getIdentifierLoc(), R, TInfo, SC,
- Bindings, D.getContext() == DeclaratorContext::Condition);
+ Bindings, /*IsDecisionVariable=*/D.getContext() ==
+ DeclaratorContext::Condition);
} else
NewVD = VarDecl::Create(Context, DC, D.getBeginLoc(),
D.getIdentifierLoc(), II, R, TInfo, SC);
diff --git a/clang/test/CodeGen/p0963r3.cpp b/clang/test/CodeGen/p0963r3.cpp
new file mode 100644
index 0000000000000..64f97feb28ae1
--- /dev/null
+++ b/clang/test/CodeGen/p0963r3.cpp
@@ -0,0 +1,108 @@
+// RUN: %clang_cc1 -std=c++2c -verify -emit-llvm %s -o - | FileCheck %s
+// expected-no-diagnostics
+
+namespace std {
+
+template <typename T> struct tuple_size;
+
+template <int, typename> struct tuple_element;
+
+} // namespace std
+
+namespace Case1 {
+
+struct S {
+ int a, b;
+ bool called_operator_bool = false;
+
+ operator bool() {
+ called_operator_bool = true;
+ return a != b;
+ }
+
+ template <int I> int get() {
+ if (!called_operator_bool)
+ return a + b;
+ return I == 0 ? a : b;
+ }
+};
+
+} // namespace Case1
+
+template <> struct std::tuple_size<Case1::S> {
+ static const int value = 2;
+};
+
+template <int I> struct std::tuple_element<I, Case1::S> {
+ using type = int;
+};
+
+namespace Case1 {
+
+void foo() {
+ if (S s(1, 2); auto [a, b] = s) {
+ __builtin_assume(a == 1);
+ __builtin_assume(b == 2);
+ }
+// CHECK: %[[call:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
+// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
+// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
+// CHECK: br i1 %[[call]], label {{.*}}, label {{.*}}
+
+ if (auto [a, b] = S(1, 2)) {
+ __builtin_assume(a == 1);
+ __builtin_assume(b == 2);
+ }
+// CHECK: %[[call2:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
+// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
+// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
+// CHECK: br i1 %[[call2]], label {{.*}}, label {{.*}}
+
+ if (S s(3, 4); auto& [a, b] = s) {
+ __builtin_assume(a == 3);
+ __builtin_assume(b == 4);
+ }
+// CHECK: %[[call3:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
+// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
+// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
+// CHECK: br i1 %[[call3]], label {{.*}}, label {{.*}}
+
+ while (auto [i, j] = S(5, 6))
+ break;
+
+// CHECK: while.cond{{.*}}:
+// CHECK: %[[call4:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
+// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
+// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
+// CHECK: br i1 %[[call4]], label {{.*}}, label {{.*}}
+
+ S s(7, 8);
+ while (auto& [i, j] = s)
+ break;
+
+// CHECK: while.cond{{.*}}:
+// CHECK: %[[call5:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
+// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
+// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
+// CHECK: br i1 %[[call5]], label {{.*}}, label {{.*}}
+
+ for (int k = 0; auto [i, j] = S(24, 42); ++k)
+ break;
+
+// CHECK: for.cond{{.*}}:
+// CHECK: %[[call6:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
+// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
+// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
+// CHECK: br i1 %[[call6]], label {{.*}}, label {{.*}}
+
+ for (S s(114, 514); auto& [i, j] = s; ++i)
+ break;
+
+// CHECK: for.cond{{.*}}:
+// CHECK: %[[call7:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
+// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
+// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
+// CHECK: br i1 %[[call7]], label {{.*}}, label {{.*}}
+}
+
+} // namespace Case1
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index 8570592101cc5..3f652c4291ddd 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -223,7 +223,7 @@ <h2 id="cxx26">C++2c implementation status</h2>
<tr>
<td>Structured binding declaration as a condition</td>
<td><a href="https://wg21.link/P0963R3">P0963R3</a></td>
- <td class="none" align="center">No</td>
+ <td class="unreleased" align="center">Clang 21</td>
</tr>
<!--Poland, Fall 2024-->
<tr>
>From ffe3922efc996e017b9425f22fe7953f352e12b6 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sun, 9 Mar 2025 23:38:06 +0800
Subject: [PATCH 03/11] Test -Wpre-c++26-compat
---
clang/test/SemaCXX/decomposed-condition.cpp | 16 +++++++++++-----
1 file changed, 11 insertions(+), 5 deletions(-)
diff --git a/clang/test/SemaCXX/decomposed-condition.cpp b/clang/test/SemaCXX/decomposed-condition.cpp
index 32ab3a34f567b..14a23af3f5658 100644
--- a/clang/test/SemaCXX/decomposed-condition.cpp
+++ b/clang/test/SemaCXX/decomposed-condition.cpp
@@ -1,5 +1,7 @@
-// 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
+// RUN: %clang_cc1 -std=c++17 -Wno-c++26-extensions -verify %s
+// RUN: %clang_cc1 -std=c++17 -Wno-c++26-extensions -verify %s -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 -std=c++2c -Wpre-c++26-compat -verify=cxx26,expected %s
+// RUN: %clang_cc1 -std=c++2c -Wpre-c++26-compat -verify=cxx26,expected %s -fexperimental-new-constant-interpreter
struct X {
bool flag;
@@ -14,7 +16,7 @@ struct X {
namespace CondInIf {
constexpr int f(X x) {
- if (auto [ok, d] = x)
+ if (auto [ok, d] = x) // cxx26-warning {{structured binding declaration in a condition is incompatible with C++ standards before C++2c}}
return d + int(ok);
else
return d * int(ok);
@@ -26,12 +28,13 @@ static_assert(f({true, 2}) == 3);
static_assert(f({false, 2}) == 0);
constexpr char g(char const (&x)[2]) {
- if (auto &[a, b] = x)
+ if (auto &[a, b] = x) // cxx26-warning {{structured binding declaration in a condition is incompatible with C++ standards before C++2c}}
return a;
else
return b;
- if (auto [a, b] = x) // expected-error {{an array type is not allowed here}}
+ if (auto [a, b] = x) // expected-error {{an array type is not allowed here}} \
+ // cxx26-warning {{structured binding declaration in a condition is incompatible with C++ standards before C++2c}}
;
}
@@ -41,6 +44,7 @@ static_assert(g("x") == 'x');
namespace CondInSwitch {
constexpr int f(int n) {
switch (X s = {true, n}; auto [ok, d] = s) {
+ // cxx26-warning at -1 {{structured binding declaration in a condition is incompatible with C++ standards before C++2c}}
s = {};
case 0:
return int(ok);
@@ -65,6 +69,7 @@ namespace CondInWhile {
constexpr int f(int n) {
int m = 1;
while (auto [ok, d] = X{n > 1, n}) {
+ // cxx26-warning at -1 {{structured binding declaration in a condition is incompatible with C++ standards before C++2c}}
m *= d;
--n;
}
@@ -81,6 +86,7 @@ namespace CondInFor {
constexpr int f(int n) {
int a = 1, b = 1;
for (X x = {true, n}; auto &[ok, d] = x; --d) {
+ // cxx26-warning at -1 {{structured binding declaration in a condition is incompatible with C++ standards before C++2c}}
if (d < 2)
ok = false;
else {
>From 2ae72b4710e036a3b19ac06b0feee96d13a68092 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 10 Mar 2025 13:56:32 +0800
Subject: [PATCH 04/11] Add more tests; handle switch (..) case .. in constant
evaluator
---
clang/lib/AST/ExprConstant.cpp | 6 ++
clang/test/CodeGen/p0963r3.cpp | 145 ++++++++++++++++++++++++++++-----
2 files changed, 130 insertions(+), 21 deletions(-)
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 564a47a839336..898f8a2fce769 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -5351,6 +5351,12 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info,
if (!EvaluateInteger(SS->getCond(), Value, Info))
return ESR_Failed;
+ if (auto *DD =
+ dyn_cast_if_present<DecompositionDecl>(SS->getConditionVariable());
+ DD && DD->isDecisionVariable() &&
+ !EvaluateDecompositionDeclInit(Info, DD))
+ return ESR_Failed;
+
if (!CondScope.destroy())
return ESR_Failed;
}
diff --git a/clang/test/CodeGen/p0963r3.cpp b/clang/test/CodeGen/p0963r3.cpp
index 64f97feb28ae1..b356e78adfa83 100644
--- a/clang/test/CodeGen/p0963r3.cpp
+++ b/clang/test/CodeGen/p0963r3.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++2c -verify -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -std=c++2c -verify -emit-llvm -triple=x86_64-pc-linux-gnu %s -o - | FileCheck %s
// expected-no-diagnostics
namespace std {
@@ -13,16 +13,26 @@ namespace Case1 {
struct S {
int a, b;
- bool called_operator_bool = false;
+ bool flag = false;
- operator bool() {
- called_operator_bool = true;
+ constexpr explicit operator bool() {
+ flag = true;
return a != b;
}
- template <int I> int get() {
- if (!called_operator_bool)
- return a + b;
+ constexpr operator int() {
+ flag = true;
+ return a * b;
+ }
+
+ constexpr bool operator==(S rhs) const {
+ return a == rhs.a && b == rhs.b;
+ }
+
+ template <int I>
+ constexpr int& get() {
+ if (!flag)
+ return a = a + b;
return I == 0 ? a : b;
}
};
@@ -45,8 +55,8 @@ void foo() {
__builtin_assume(b == 2);
}
// CHECK: %[[call:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
-// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
-// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv
// CHECK: br i1 %[[call]], label {{.*}}, label {{.*}}
if (auto [a, b] = S(1, 2)) {
@@ -54,8 +64,8 @@ void foo() {
__builtin_assume(b == 2);
}
// CHECK: %[[call2:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
-// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
-// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv
// CHECK: br i1 %[[call2]], label {{.*}}, label {{.*}}
if (S s(3, 4); auto& [a, b] = s) {
@@ -63,8 +73,8 @@ void foo() {
__builtin_assume(b == 4);
}
// CHECK: %[[call3:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
-// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
-// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv
// CHECK: br i1 %[[call3]], label {{.*}}, label {{.*}}
while (auto [i, j] = S(5, 6))
@@ -72,8 +82,8 @@ void foo() {
// CHECK: while.cond{{.*}}:
// CHECK: %[[call4:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
-// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
-// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv
// CHECK: br i1 %[[call4]], label {{.*}}, label {{.*}}
S s(7, 8);
@@ -82,8 +92,8 @@ void foo() {
// CHECK: while.cond{{.*}}:
// CHECK: %[[call5:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
-// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
-// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv
// CHECK: br i1 %[[call5]], label {{.*}}, label {{.*}}
for (int k = 0; auto [i, j] = S(24, 42); ++k)
@@ -91,8 +101,8 @@ void foo() {
// CHECK: for.cond{{.*}}:
// CHECK: %[[call6:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
-// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
-// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv
// CHECK: br i1 %[[call6]], label {{.*}}, label {{.*}}
for (S s(114, 514); auto& [i, j] = s; ++i)
@@ -100,9 +110,102 @@ void foo() {
// CHECK: for.cond{{.*}}:
// CHECK: %[[call7:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
-// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi0EEEiv
-// CHECK: %{{.*}} = call {{.*}} i32 @_ZN5Case11S3getILi1EEEiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv
// CHECK: br i1 %[[call7]], label {{.*}}, label {{.*}}
+
+ switch (S s(10, 11); auto& [i, j] = s) {
+ case 10 * 11:
+ __builtin_assume(i == 10);
+ __builtin_assume(j == 11);
+ break;
+ default:
+ break;
+ }
+
+// CHECK: %[[call8:.+]] = call {{.*}} i32 @_ZN5Case11ScviEv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv
+// CHECK: switch i32 %[[call8]], label {{.*}}
+
}
+constexpr int bar(auto) {
+ constexpr auto value = [] {
+ if (S s(1, 2); auto [i, j] = s)
+ return S(i, j);
+ return S(0, 0);
+ }();
+ static_assert(value == S(1, 2));
+
+ // FIXME: The diagnostic message adds a trailing comma "static assertion failed due to requirement 'value == Case1::S((0, 1, ))'"
+ // static_assert(value == S(0, 1));
+
+ constexpr auto value2 = [] {
+ if (auto [a, b] = S(1, 2))
+ return S(a, b);
+ return S(0, 0);
+ }();
+ static_assert(value2 == S(1, 2));
+
+ constexpr auto value3 = [] {
+ if (auto&& [a, b] = S(3, 4))
+ return S(a, b);
+ return S(0, 0);
+ }();
+ static_assert(value3 == S(3, 4));
+
+ constexpr auto value4 = [] {
+ S s(7, 8);
+ int cnt = 0;
+ while (auto& [i, j] = s) {
+ s.flag = false;
+ ++i, ++j;
+ if (++cnt == 10)
+ break;
+ }
+ return s;
+ }();
+ static_assert(value4 == S(17, 18));
+
+ constexpr auto value5 = [] {
+ S s(3, 4);
+ for (int cnt = 0; auto& [x, y] = s; s.flag = false, ++cnt) {
+ if (cnt == 3)
+ break;
+ ++x, ++y;
+ }
+ return s;
+ }();
+ static_assert(value5 == S(6, 7));
+
+ constexpr auto value6 = [] {
+ switch (auto [x, y] = S(3, 4)) {
+ case 3 * 4:
+ return S(x, y);
+ default:
+ return S(y, x);
+ }
+ }();
+ static_assert(value6 == S(3, 4));
+
+ return 42;
+}
+
+constexpr int value = bar(1);
+
+#if 0
+
+// FIXME: This causes clang to ICE, though this is not a regression.
+constexpr int ice(auto) {
+ if constexpr (S s(1, 2); auto [i, j] = s) {
+ static_assert(i == 1);
+ }
+ return 42;
+}
+
+constexpr int value2 = ice(1);
+
+#endif
+
} // namespace Case1
>From 8dd6f8245c7aed0d0f6fb40e05227160db3ebbed Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 10 Mar 2025 16:06:21 +0800
Subject: [PATCH 05/11] Enable fexperimental-new-constant-interpreter tests
---
clang/lib/AST/ByteCode/Compiler.cpp | 45 +++++++++++++++++++++++++----
clang/lib/AST/ByteCode/Compiler.h | 1 +
clang/test/CodeGen/p0963r3.cpp | 1 +
3 files changed, 41 insertions(+), 6 deletions(-)
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 394e39e99a106..213e1a965ff44 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -5104,6 +5104,16 @@ bool Compiler<Emitter>::visitCompoundStmt(const CompoundStmt *S) {
return Scope.destroyLocals();
}
+template <class Emitter>
+bool Compiler<Emitter>::emitDecompositionVarInit(const DecompositionDecl *DD) {
+ for (auto *BD : DD->bindings())
+ if (auto *KD = BD->getHoldingVar()) {
+ if (!this->visitVarDecl(KD))
+ return false;
+ }
+ return true;
+}
+
template <class Emitter>
bool Compiler<Emitter>::visitDeclStmt(const DeclStmt *DS) {
for (const auto *D : DS->decls()) {
@@ -5118,12 +5128,10 @@ bool Compiler<Emitter>::visitDeclStmt(const DeclStmt *DS) {
return false;
// Register decomposition decl holding vars.
- if (const auto *DD = dyn_cast<DecompositionDecl>(VD)) {
- for (auto *BD : DD->bindings())
- if (auto *KD = BD->getHoldingVar()) {
- if (!this->visitVarDecl(KD))
- return false;
- }
+ if (const auto *DD = dyn_cast<DecompositionDecl>(VD);
+ DD && !DD->isDecisionVariable()) {
+ if (!this->emitDecompositionVarInit(DD))
+ return false;
}
}
@@ -5189,6 +5197,12 @@ template <class Emitter> bool Compiler<Emitter>::visitIfStmt(const IfStmt *IS) {
return false;
}
+ if (auto *DD =
+ dyn_cast_if_present<DecompositionDecl>(IS->getConditionVariable());
+ DD && DD->isDecisionVariable())
+ if (!this->emitDecompositionVarInit(DD))
+ return false;
+
if (const Stmt *Else = IS->getElse()) {
LabelTy LabelElse = this->getLabel();
LabelTy LabelEnd = this->getLabel();
@@ -5249,6 +5263,13 @@ bool Compiler<Emitter>::visitWhileStmt(const WhileStmt *S) {
if (!this->visitBool(Cond))
return false;
+
+ if (auto *DD =
+ dyn_cast_if_present<DecompositionDecl>(S->getConditionVariable());
+ DD && DD->isDecisionVariable())
+ if (!this->emitDecompositionVarInit(DD))
+ return false;
+
if (!this->jumpFalse(EndLabel))
return false;
@@ -5330,6 +5351,12 @@ bool Compiler<Emitter>::visitForStmt(const ForStmt *S) {
return false;
}
+ if (auto *DD =
+ dyn_cast_if_present<DecompositionDecl>(S->getConditionVariable());
+ DD && DD->isDecisionVariable())
+ if (!this->emitDecompositionVarInit(DD))
+ return false;
+
if (Body && !this->visitStmt(Body))
return false;
@@ -5452,6 +5479,12 @@ bool Compiler<Emitter>::visitSwitchStmt(const SwitchStmt *S) {
if (!this->emitSetLocal(CondT, CondVar, S))
return false;
+ if (auto *DD =
+ dyn_cast_if_present<DecompositionDecl>(S->getConditionVariable());
+ DD && DD->isDecisionVariable())
+ if (!this->emitDecompositionVarInit(DD))
+ return false;
+
CaseMap CaseLabels;
// Create labels and comparison ops for all case statements.
for (const SwitchCase *SC = S->getSwitchCaseList(); SC;
diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h
index 77fcc3d1b41ce..9bb0ba6e52f16 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -386,6 +386,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
bool compileUnionAssignmentOperator(const CXXMethodDecl *MD);
bool checkLiteralType(const Expr *E);
+ bool emitDecompositionVarInit(const DecompositionDecl *DD);
protected:
/// Variable to storage mapping.
diff --git a/clang/test/CodeGen/p0963r3.cpp b/clang/test/CodeGen/p0963r3.cpp
index b356e78adfa83..b48b5294e093e 100644
--- a/clang/test/CodeGen/p0963r3.cpp
+++ b/clang/test/CodeGen/p0963r3.cpp
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -std=c++2c -verify -emit-llvm -triple=x86_64-pc-linux-gnu %s -o - | FileCheck %s
+// RUN: %clang_cc1 -std=c++2c -verify -fsyntax-only -fexperimental-new-constant-interpreter -triple=x86_64-pc-linux-gnu %s
// expected-no-diagnostics
namespace std {
>From da94831f805f1a6ef0f71cfa89910878c93ead80 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 10 Mar 2025 19:33:52 +0800
Subject: [PATCH 06/11] Remove isDecisionVariable flag
---
clang/docs/LanguageExtensions.rst | 1 +
clang/include/clang/AST/DeclCXX.h | 26 +++++----------
clang/lib/AST/ASTImporter.cpp | 7 ++--
clang/lib/AST/ByteCode/Compiler.cpp | 27 +++++++--------
clang/lib/AST/ByteCode/Compiler.h | 2 +-
clang/lib/AST/DeclCXX.cpp | 21 +++++-------
clang/lib/AST/ExprConstant.cpp | 25 +++++++-------
clang/lib/CodeGen/CGDecl.cpp | 6 ++--
clang/lib/CodeGen/CGStmt.cpp | 33 ++++++++-----------
clang/lib/CodeGen/CodeGenFunction.cpp | 3 +-
clang/lib/CodeGen/CodeGenFunction.h | 2 +-
clang/lib/Sema/SemaDecl.cpp | 7 ++--
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 8 ++---
clang/lib/Serialization/ASTReaderDecl.cpp | 9 ++---
clang/lib/Serialization/ASTWriterDecl.cpp | 1 -
15 files changed, 74 insertions(+), 104 deletions(-)
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 578ee02f09b9b..8fbbee0f1586f 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1641,6 +1641,7 @@ Conditional ``explicit`` __cpp_conditional_explicit C+
Attributes on Lambda-Expressions C++23 C++11
Attributes on Structured Bindings __cpp_structured_bindings C++26 C++03
Packs in Structured Bindings __cpp_structured_bindings C++26 C++03
+Structured binding declaration as a condition C++26 C++11
Static assert with user-generated message __cpp_static_assert >= 202306L C++26 C++11
Pack Indexing __cpp_pack_indexing C++26 C++03
``= delete ("should have a reason");`` __cpp_deleted_function C++26 C++03
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index d82d9900945a1..61be7fb50ff87 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -4224,18 +4224,15 @@ class DecompositionDecl final
: public VarDecl,
private llvm::TrailingObjects<DecompositionDecl, BindingDecl *> {
/// The number of BindingDecl*s following this object.
- unsigned NumBindings : 31;
-
- LLVM_PREFERRED_TYPE(bool)
- unsigned IsDecisionVariable : 1;
+ unsigned NumBindings;
DecompositionDecl(ASTContext &C, DeclContext *DC, SourceLocation StartLoc,
SourceLocation LSquareLoc, QualType T,
TypeSourceInfo *TInfo, StorageClass SC,
- ArrayRef<BindingDecl *> Bindings, bool IsDecisionVariable)
+ ArrayRef<BindingDecl *> Bindings)
: VarDecl(Decomposition, C, DC, StartLoc, LSquareLoc, nullptr, T, TInfo,
SC),
- NumBindings(Bindings.size()), IsDecisionVariable(IsDecisionVariable) {
+ NumBindings(Bindings.size()) {
std::uninitialized_copy(Bindings.begin(), Bindings.end(),
getTrailingObjects<BindingDecl *>());
for (auto *B : Bindings) {
@@ -4256,14 +4253,13 @@ class DecompositionDecl final
static DecompositionDecl *Create(ASTContext &C, DeclContext *DC,
SourceLocation StartLoc,
- SourceLocation LSquareLoc, QualType T,
- TypeSourceInfo *TInfo, StorageClass S,
- ArrayRef<BindingDecl *> Bindings,
- bool IsDecisionVariable);
+ SourceLocation LSquareLoc,
+ QualType T, TypeSourceInfo *TInfo,
+ StorageClass S,
+ ArrayRef<BindingDecl *> Bindings);
static DecompositionDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID,
- unsigned NumBindings,
- bool IsDecisionVariable);
+ unsigned NumBindings);
// Provide the range of bindings which may have a nested pack.
llvm::ArrayRef<BindingDecl *> bindings() const {
@@ -4290,12 +4286,6 @@ class DecompositionDecl final
std::move(Bindings));
}
- /// The decision variable of a condition that is a structured binding
- /// declaration is specified in [dcl.struct.bind]p4:
- /// If a structured binding declaration appears as a condition, the decision
- /// variable of the condition is e.
- 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/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 95ca085c5b746..82180486f3c7c 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -4614,10 +4614,9 @@ 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, FromDecomp->isDecisionVariable()))
+ if (GetImportedOrCreateDecl(
+ ToDecomp, FromDecomp, Importer.getToContext(), DC, ToInnerLocStart,
+ Loc, ToType, ToTypeSourceInfo, D->getStorageClass(), Bindings))
return ToDecomp;
ToVar = ToDecomp;
} else {
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 213e1a965ff44..4848d6a6e012e 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -5051,7 +5051,7 @@ template <class Emitter> bool Compiler<Emitter>::visitStmt(const Stmt *S) {
case Stmt::CompoundStmtClass:
return visitCompoundStmt(cast<CompoundStmt>(S));
case Stmt::DeclStmtClass:
- return visitDeclStmt(cast<DeclStmt>(S));
+ return visitDeclStmt(cast<DeclStmt>(S), /*EvaluateConditionDecl=*/true);
case Stmt::ReturnStmtClass:
return visitReturnStmt(cast<ReturnStmt>(S));
case Stmt::IfStmtClass:
@@ -5115,7 +5115,8 @@ bool Compiler<Emitter>::emitDecompositionVarInit(const DecompositionDecl *DD) {
}
template <class Emitter>
-bool Compiler<Emitter>::visitDeclStmt(const DeclStmt *DS) {
+bool Compiler<Emitter>::visitDeclStmt(const DeclStmt *DS,
+ bool EvaluateConditionDecl) {
for (const auto *D : DS->decls()) {
if (isa<StaticAssertDecl, TagDecl, TypedefNameDecl, BaseUsingDecl,
FunctionDecl, NamespaceAliasDecl, UsingDirectiveDecl>(D))
@@ -5129,7 +5130,7 @@ bool Compiler<Emitter>::visitDeclStmt(const DeclStmt *DS) {
// Register decomposition decl holding vars.
if (const auto *DD = dyn_cast<DecompositionDecl>(VD);
- DD && !DD->isDecisionVariable()) {
+ EvaluateConditionDecl && DD) {
if (!this->emitDecompositionVarInit(DD))
return false;
}
@@ -5180,7 +5181,7 @@ template <class Emitter> bool Compiler<Emitter>::visitIfStmt(const IfStmt *IS) {
return false;
if (const DeclStmt *CondDecl = IS->getConditionVariableDeclStmt())
- if (!visitDeclStmt(CondDecl))
+ if (!visitDeclStmt(CondDecl, /*EvaluateConditionDecl=*/false))
return false;
// Compile condition.
@@ -5198,8 +5199,7 @@ template <class Emitter> bool Compiler<Emitter>::visitIfStmt(const IfStmt *IS) {
}
if (auto *DD =
- dyn_cast_if_present<DecompositionDecl>(IS->getConditionVariable());
- DD && DD->isDecisionVariable())
+ dyn_cast_if_present<DecompositionDecl>(IS->getConditionVariable()))
if (!this->emitDecompositionVarInit(DD))
return false;
@@ -5258,15 +5258,14 @@ bool Compiler<Emitter>::visitWhileStmt(const WhileStmt *S) {
{
LocalScope<Emitter> CondScope(this);
if (const DeclStmt *CondDecl = S->getConditionVariableDeclStmt())
- if (!visitDeclStmt(CondDecl))
+ if (!visitDeclStmt(CondDecl, /*EvaluateConditionDecl=*/false))
return false;
if (!this->visitBool(Cond))
return false;
if (auto *DD =
- dyn_cast_if_present<DecompositionDecl>(S->getConditionVariable());
- DD && DD->isDecisionVariable())
+ dyn_cast_if_present<DecompositionDecl>(S->getConditionVariable()))
if (!this->emitDecompositionVarInit(DD))
return false;
@@ -5341,7 +5340,7 @@ bool Compiler<Emitter>::visitForStmt(const ForStmt *S) {
{
LocalScope<Emitter> CondScope(this);
if (const DeclStmt *CondDecl = S->getConditionVariableDeclStmt())
- if (!visitDeclStmt(CondDecl))
+ if (!visitDeclStmt(CondDecl, /*EvaluateConditionDecl=*/false))
return false;
if (Cond) {
@@ -5352,8 +5351,7 @@ bool Compiler<Emitter>::visitForStmt(const ForStmt *S) {
}
if (auto *DD =
- dyn_cast_if_present<DecompositionDecl>(S->getConditionVariable());
- DD && DD->isDecisionVariable())
+ dyn_cast_if_present<DecompositionDecl>(S->getConditionVariable()))
if (!this->emitDecompositionVarInit(DD))
return false;
@@ -5470,7 +5468,7 @@ bool Compiler<Emitter>::visitSwitchStmt(const SwitchStmt *S) {
return false;
if (const DeclStmt *CondDecl = S->getConditionVariableDeclStmt())
- if (!visitDeclStmt(CondDecl))
+ if (!visitDeclStmt(CondDecl, /*EvaluateConditionDecl=*/false))
return false;
// Initialize condition variable.
@@ -5480,8 +5478,7 @@ bool Compiler<Emitter>::visitSwitchStmt(const SwitchStmt *S) {
return false;
if (auto *DD =
- dyn_cast_if_present<DecompositionDecl>(S->getConditionVariable());
- DD && DD->isDecisionVariable())
+ dyn_cast_if_present<DecompositionDecl>(S->getConditionVariable()))
if (!this->emitDecompositionVarInit(DD))
return false;
diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h
index 9bb0ba6e52f16..f59f5a3b91d59 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -213,7 +213,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
// Statements.
bool visitCompoundStmt(const CompoundStmt *S);
- bool visitDeclStmt(const DeclStmt *DS);
+ bool visitDeclStmt(const DeclStmt *DS, bool EvaluateConditionDecl);
bool visitReturnStmt(const ReturnStmt *RS);
bool visitIfStmt(const IfStmt *IS);
bool visitWhileStmt(const WhileStmt *S);
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index fdad1150c8b1a..7eff776882629 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -3520,24 +3520,21 @@ DecompositionDecl *DecompositionDecl::Create(ASTContext &C, DeclContext *DC,
SourceLocation LSquareLoc,
QualType T, TypeSourceInfo *TInfo,
StorageClass SC,
- ArrayRef<BindingDecl *> Bindings,
- bool IsDecisionVariable) {
+ ArrayRef<BindingDecl *> Bindings) {
size_t Extra = additionalSizeToAlloc<BindingDecl *>(Bindings.size());
- return new (C, DC, Extra) DecompositionDecl(
- C, DC, StartLoc, LSquareLoc, T, TInfo, SC, Bindings, IsDecisionVariable);
+ return new (C, DC, Extra)
+ DecompositionDecl(C, DC, StartLoc, LSquareLoc, T, TInfo, SC, Bindings);
}
-DecompositionDecl *
-DecompositionDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID,
- unsigned NumBindings,
- bool IsDecisionVariable) {
+DecompositionDecl *DecompositionDecl::CreateDeserialized(ASTContext &C,
+ GlobalDeclID ID,
+ unsigned NumBindings) {
size_t Extra = additionalSizeToAlloc<BindingDecl *>(NumBindings);
- auto *Result = new (C, ID, Extra) DecompositionDecl(
- C, nullptr, SourceLocation(), SourceLocation(), QualType(), nullptr,
- StorageClass(), {}, IsDecisionVariable);
+ auto *Result = new (C, ID, Extra)
+ DecompositionDecl(C, nullptr, SourceLocation(), SourceLocation(),
+ QualType(), nullptr, StorageClass(), {});
// 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 898f8a2fce769..32652aaef226e 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -5221,14 +5221,15 @@ static bool EvaluateVarDecl(EvalInfo &Info, const VarDecl *VD) {
static bool EvaluateDecompositionDeclInit(EvalInfo &Info,
const DecompositionDecl *DD);
-static bool EvaluateDecl(EvalInfo &Info, const Decl *D) {
+static bool EvaluateDecl(EvalInfo &Info, const Decl *D,
+ bool EvaluateConditionDecl) {
bool OK = true;
if (const VarDecl *VD = dyn_cast<VarDecl>(D))
OK &= EvaluateVarDecl(Info, VD);
if (const DecompositionDecl *DD = dyn_cast<DecompositionDecl>(D);
- DD && !DD->isDecisionVariable())
+ EvaluateConditionDecl && DD)
OK &= EvaluateDecompositionDeclInit(Info, DD);
return OK;
@@ -5236,12 +5237,12 @@ static bool EvaluateDecl(EvalInfo &Info, const Decl *D) {
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);
+ if (!EvaluateDecl(Info, VD, /*EvaluateConditionDecl=*/true))
+ return false;
- return OK;
+ return true;
}
static bool EvaluateDependentExpr(const Expr *E, EvalInfo &Info) {
@@ -5259,13 +5260,12 @@ static bool EvaluateCond(EvalInfo &Info, const VarDecl *CondDecl,
if (Cond->isValueDependent())
return false;
FullExpressionRAII Scope(Info);
- if (CondDecl && !EvaluateDecl(Info, CondDecl))
+ if (CondDecl && !EvaluateDecl(Info, CondDecl, /*EvaluateConditionDecl=*/false))
return false;
if (!EvaluateAsBooleanCondition(Cond, Result, Info))
return false;
if (auto *DD = dyn_cast_if_present<DecompositionDecl>(CondDecl);
- DD && DD->isDecisionVariable() &&
- !EvaluateDecompositionDeclInit(Info, DD))
+ DD && !EvaluateDecompositionDeclInit(Info, DD))
return false;
return Scope.destroy();
}
@@ -5341,7 +5341,8 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info,
FullExpressionRAII CondScope(Info);
if (SS->getConditionVariable() &&
- !EvaluateDecl(Info, SS->getConditionVariable()))
+ !EvaluateDecl(Info, SS->getConditionVariable(),
+ /*EvaluateConditionDecl=*/false))
return ESR_Failed;
if (SS->getCond()->isValueDependent()) {
// We don't know what the value is, and which branch should jump to.
@@ -5353,8 +5354,7 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info,
if (auto *DD =
dyn_cast_if_present<DecompositionDecl>(SS->getConditionVariable());
- DD && DD->isDecisionVariable() &&
- !EvaluateDecompositionDeclInit(Info, DD))
+ DD && !EvaluateDecompositionDeclInit(Info, DD))
return ESR_Failed;
if (!CondScope.destroy())
@@ -5581,7 +5581,8 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
return ESR_Failed;
// Each declaration initialization is its own full-expression.
FullExpressionRAII Scope(Info);
- if (!EvaluateDecl(Info, D) && !Info.noteFailure())
+ if (!EvaluateDecl(Info, D, /*EvaluateConditionDecl=*/true) &&
+ !Info.noteFailure())
return ESR_Failed;
if (!Scope.destroy())
return ESR_Failed;
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 44d787a85f691..8de51c3af39f3 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -48,7 +48,7 @@ using namespace CodeGen;
static_assert(clang::Sema::MaximumAlignment <= llvm::Value::MaximumAlignment,
"Clang max alignment greater than what LLVM supports?");
-void CodeGenFunction::EmitDecl(const Decl &D) {
+void CodeGenFunction::EmitDecl(const Decl &D, bool EvaluateConditionDecl) {
switch (D.getKind()) {
case Decl::BuiltinTemplate:
case Decl::TranslationUnit:
@@ -152,7 +152,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
return;
case Decl::UsingPack:
for (auto *Using : cast<UsingPackDecl>(D).expansions())
- EmitDecl(*Using);
+ EmitDecl(*Using, /*EvaluateConditionDecl=*/EvaluateConditionDecl);
return;
case Decl::UsingDirective: // using namespace X; [C++]
if (CGDebugInfo *DI = getDebugInfo())
@@ -165,7 +165,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
"Should not see file-scope variables inside a function!");
EmitVarDecl(VD);
if (auto *DD = dyn_cast<DecompositionDecl>(&VD);
- DD && !DD->isDecisionVariable())
+ EvaluateConditionDecl && DD)
EmitDecompositionVarInit(*DD);
return;
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 0a49eadbf33d1..08dd38b23ddef 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -894,7 +894,7 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
EmitStmt(S.getInit());
if (S.getConditionVariable())
- EmitDecl(*S.getConditionVariable());
+ EmitDecl(*S.getConditionVariable(), /*EvaluateConditionDecl=*/false);
// If the condition constant folds and can be elided, try to avoid emitting
// the condition and the dead arm of the if/else.
@@ -914,10 +914,8 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
incrementProfileCounter(&S);
if (Executed) {
if (auto *DD = dyn_cast_if_present<DecompositionDecl>(
- S.getConditionVariable())) {
- assert(DD->isDecisionVariable());
+ S.getConditionVariable()))
EmitDecompositionVarInit(*DD);
- }
RunCleanupsScope ExecutedScope(*this);
EmitStmt(Executed);
}
@@ -959,14 +957,13 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
// updates are kept linear and consistent.
if (!CGM.getCodeGenOpts().MCDCCoverage) {
EmitBranchOnBoolExpr(S.getCond(), ThenBlock, ElseBlock, ThenCount, LH,
- nullptr, S.getConditionVariable());
+ /*ConditionalOp=*/nullptr,
+ /*ConditionalDecl=*/S.getConditionVariable());
} else {
llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond());
if (auto *DD =
- dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable())) {
- assert(DD->isDecisionVariable());
+ dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable()))
EmitDecompositionVarInit(*DD);
- }
Builder.CreateCondBr(BoolCondVal, ThenBlock, ElseBlock);
}
@@ -1103,7 +1100,7 @@ void CodeGenFunction::EmitWhileStmt(const WhileStmt &S,
RunCleanupsScope ConditionScope(*this);
if (S.getConditionVariable())
- EmitDecl(*S.getConditionVariable());
+ EmitDecl(*S.getConditionVariable(), /*EvaluateConditionDecl=*/false);
// Evaluate the conditional in the while header. C99 6.8.5.1: The
// evaluation of the controlling expression takes place before each
@@ -1111,8 +1108,7 @@ void CodeGenFunction::EmitWhileStmt(const WhileStmt &S,
llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond());
if (auto *DD =
- dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable());
- DD && DD->isDecisionVariable())
+ dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable()))
EmitDecompositionVarInit(*DD);
// while(1) is common, avoid extra exit blocks. Be sure
@@ -1323,7 +1319,7 @@ void CodeGenFunction::EmitForStmt(const ForStmt &S,
// If the for statement has a condition scope, emit the local variable
// declaration.
if (S.getConditionVariable()) {
- EmitDecl(*S.getConditionVariable());
+ EmitDecl(*S.getConditionVariable(), /*EvaluateConditionDecl=*/false);
// We have entered the condition variable's scope, so we're now able to
// jump to the continue block.
@@ -1350,8 +1346,7 @@ void CodeGenFunction::EmitForStmt(const ForStmt &S,
llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond());
if (auto *DD =
- dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable());
- DD && DD->isDecisionVariable())
+ dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable()))
EmitDecompositionVarInit(*DD);
llvm::MDNode *Weights =
@@ -2249,11 +2244,10 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) {
// Emit the condition variable if needed inside the entire cleanup scope
// used by this special case for constant folded switches.
if (S.getConditionVariable())
- EmitDecl(*S.getConditionVariable());
+ EmitDecl(*S.getConditionVariable(), /*EvaluateConditionDecl=*/false);
if (auto *DD =
- dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable());
- DD && DD->isDecisionVariable())
+ dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable()))
EmitDecompositionVarInit(*DD);
// At this point, we are no longer "within" a switch instance, so
@@ -2284,12 +2278,11 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) {
EmitStmt(S.getInit());
if (S.getConditionVariable())
- EmitDecl(*S.getConditionVariable());
+ EmitDecl(*S.getConditionVariable(), /*EvaluateConditionDecl=*/false);
llvm::Value *CondV = EmitScalarExpr(S.getCond());
if (auto *DD =
- dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable());
- DD && DD->isDecisionVariable())
+ dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable()))
EmitDecompositionVarInit(*DD);
// Create basic block to hold stuff that comes after switch
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index c17a7d308bca8..8c4edb809ebc8 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -2048,8 +2048,7 @@ void CodeGenFunction::EmitBranchOnBoolExpr(
CondV = EvaluateExprAsBool(Cond);
}
- if (auto *DD = dyn_cast_if_present<DecompositionDecl>(ConditionalDecl);
- DD && DD->isDecisionVariable())
+ if (auto *DD = dyn_cast_if_present<DecompositionDecl>(ConditionalDecl))
EmitDecompositionVarInit(*DD);
// If not at the top of the logical operator nest, update MCDC temp with the
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 3586594429da8..d1be95d0b982b 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -3377,7 +3377,7 @@ class CodeGenFunction : public CodeGenTypeCache {
/// EmitDecl - Emit a declaration.
///
/// This function can be called with a null (unreachable) insert point.
- void EmitDecl(const Decl &D);
+ void EmitDecl(const Decl &D, bool EvaluateConditionDecl = true);
/// EmitVarDecl - Emit a local variable declaration.
///
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 675a928551fcb..36de02d91e151 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -7768,10 +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, /*IsDecisionVariable=*/D.getContext() ==
- DeclaratorContext::Condition);
+ NewVD = DecompositionDecl::Create(Context, DC, D.getBeginLoc(),
+ D.getIdentifierLoc(), R, TInfo, SC,
+ Bindings);
} else
NewVD = VarDecl::Create(Context, DC, D.getBeginLoc(),
D.getIdentifierLoc(), II, R, TInfo, SC);
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index c87ce2ae15ae9..dd894df851488 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -14,7 +14,6 @@
#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"
@@ -1474,10 +1473,9 @@ 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,
- cast<DecompositionDecl>(D)->isDecisionVariable());
+ Var = DecompositionDecl::Create(SemaRef.Context, DC, D->getInnerLocStart(),
+ D->getLocation(), DI->getType(), DI,
+ D->getStorageClass(), *Bindings);
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 113ed96ea2021..2a580c44b94e5 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -4115,12 +4115,9 @@ Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) {
case DECL_PARM_VAR:
D = ParmVarDecl::CreateDeserialized(Context, ID);
break;
- case DECL_DECOMPOSITION: {
- unsigned NumBindings = Record.readInt();
- bool IsDecisionVariable = Record.readBool();
- D = DecompositionDecl::CreateDeserialized(Context, ID, NumBindings,
- IsDecisionVariable);
- } break;
+ case DECL_DECOMPOSITION:
+ D = DecompositionDecl::CreateDeserialized(Context, ID, Record.readInt());
+ 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 c11866319f2b0..a1810003f5425 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -1327,7 +1327,6 @@ 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())
>From c37a324aa39c37f1162b0d4a157d47d5dae55b33 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 10 Mar 2025 19:36:27 +0800
Subject: [PATCH 07/11] Early returns
---
clang/lib/AST/ExprConstant.cpp | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 32652aaef226e..edb840e9fed89 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -5223,16 +5223,16 @@ static bool EvaluateDecompositionDeclInit(EvalInfo &Info,
static bool EvaluateDecl(EvalInfo &Info, const Decl *D,
bool EvaluateConditionDecl) {
- bool OK = true;
-
if (const VarDecl *VD = dyn_cast<VarDecl>(D))
- OK &= EvaluateVarDecl(Info, VD);
+ if (!EvaluateVarDecl(Info, VD))
+ return false;
if (const DecompositionDecl *DD = dyn_cast<DecompositionDecl>(D);
EvaluateConditionDecl && DD)
- OK &= EvaluateDecompositionDeclInit(Info, DD);
+ if (!EvaluateDecompositionDeclInit(Info, DD))
+ return false;
- return OK;
+ return true;
}
static bool EvaluateDecompositionDeclInit(EvalInfo &Info,
@@ -5260,7 +5260,8 @@ static bool EvaluateCond(EvalInfo &Info, const VarDecl *CondDecl,
if (Cond->isValueDependent())
return false;
FullExpressionRAII Scope(Info);
- if (CondDecl && !EvaluateDecl(Info, CondDecl, /*EvaluateConditionDecl=*/false))
+ if (CondDecl &&
+ !EvaluateDecl(Info, CondDecl, /*EvaluateConditionDecl=*/false))
return false;
if (!EvaluateAsBooleanCondition(Cond, Result, Info))
return false;
>From 7ca3012be794b65324b62d7eaf8bebaf326bcd5e Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 10 Mar 2025 19:48:38 +0800
Subject: [PATCH 08/11] Fix documents
---
clang/docs/LanguageExtensions.rst | 96 +++++++++++++++----------------
1 file changed, 48 insertions(+), 48 deletions(-)
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 8fbbee0f1586f..8d20217ee2c28 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1604,54 +1604,54 @@ More information could be found `here <https://clang.llvm.org/docs/Modules.html>
Language Extensions Back-ported to Previous Standards
=====================================================
-============================================ ================================ ============= =============
-Feature Feature Test Macro Introduced In Backported To
-============================================ ================================ ============= =============
-variadic templates __cpp_variadic_templates C++11 C++03
-Alias templates __cpp_alias_templates C++11 C++03
-Non-static data member initializers __cpp_nsdmi C++11 C++03
-Range-based ``for`` loop __cpp_range_based_for C++11 C++03
-RValue references __cpp_rvalue_references C++11 C++03
-Attributes __cpp_attributes C++11 C++03
-Lambdas __cpp_lambdas C++11 C++03
-Generalized lambda captures __cpp_init_captures C++14 C++03
-Generic lambda expressions __cpp_generic_lambdas C++14 C++03
-variable templates __cpp_variable_templates C++14 C++03
-Binary literals __cpp_binary_literals C++14 C++03
-Relaxed constexpr __cpp_constexpr C++14 C++11
-Static assert with no message __cpp_static_assert >= 201411L C++17 C++11
-Pack expansion in generalized lambda-capture __cpp_init_captures C++17 C++03
-``if constexpr`` __cpp_if_constexpr C++17 C++11
-fold expressions __cpp_fold_expressions C++17 C++03
-Lambda capture of \*this by value __cpp_capture_star_this C++17 C++03
-Attributes on enums __cpp_enumerator_attributes C++17 C++03
-Guaranteed copy elision __cpp_guaranteed_copy_elision C++17 C++03
-Hexadecimal floating literals __cpp_hex_float C++17 C++03
-``inline`` variables __cpp_inline_variables C++17 C++03
-Attributes on namespaces __cpp_namespace_attributes C++17 C++11
-Structured bindings __cpp_structured_bindings C++17 C++03
-template template arguments __cpp_template_template_args C++17 C++03
-Familiar template syntax for generic lambdas __cpp_generic_lambdas C++20 C++03
-``static operator[]`` __cpp_multidimensional_subscript C++20 C++03
-Designated initializers __cpp_designated_initializers C++20 C++03
-Conditional ``explicit`` __cpp_conditional_explicit C++20 C++03
-``using enum`` __cpp_using_enum C++20 C++03
-``if consteval`` __cpp_if_consteval C++23 C++20
-``static operator()`` __cpp_static_call_operator C++23 C++03
-Attributes on Lambda-Expressions C++23 C++11
-Attributes on Structured Bindings __cpp_structured_bindings C++26 C++03
-Packs in Structured Bindings __cpp_structured_bindings C++26 C++03
-Structured binding declaration as a condition C++26 C++11
-Static assert with user-generated message __cpp_static_assert >= 202306L C++26 C++11
-Pack Indexing __cpp_pack_indexing C++26 C++03
-``= delete ("should have a reason");`` __cpp_deleted_function C++26 C++03
-Variadic Friends __cpp_variadic_friend C++26 C++03
--------------------------------------------- -------------------------------- ------------- -------------
-Designated initializers (N494) C99 C89
-Array & element qualification (N2607) C23 C89
-Attributes (N2335) C23 C89
-``#embed`` (N3017) C23 C89, C++
-============================================ ================================ ============= =============
+============================================= ================================ ============= =============
+Feature Feature Test Macro Introduced In Backported To
+============================================= ================================ ============= =============
+variadic templates __cpp_variadic_templates C++11 C++03
+Alias templates __cpp_alias_templates C++11 C++03
+Non-static data member initializers __cpp_nsdmi C++11 C++03
+Range-based ``for`` loop __cpp_range_based_for C++11 C++03
+RValue references __cpp_rvalue_references C++11 C++03
+Attributes __cpp_attributes C++11 C++03
+Lambdas __cpp_lambdas C++11 C++03
+Generalized lambda captures __cpp_init_captures C++14 C++03
+Generic lambda expressions __cpp_generic_lambdas C++14 C++03
+variable templates __cpp_variable_templates C++14 C++03
+Binary literals __cpp_binary_literals C++14 C++03
+Relaxed constexpr __cpp_constexpr C++14 C++11
+Static assert with no message __cpp_static_assert >= 201411L C++17 C++11
+Pack expansion in generalized lambda-capture __cpp_init_captures C++17 C++03
+``if constexpr`` __cpp_if_constexpr C++17 C++11
+fold expressions __cpp_fold_expressions C++17 C++03
+Lambda capture of \*this by value __cpp_capture_star_this C++17 C++03
+Attributes on enums __cpp_enumerator_attributes C++17 C++03
+Guaranteed copy elision __cpp_guaranteed_copy_elision C++17 C++03
+Hexadecimal floating literals __cpp_hex_float C++17 C++03
+``inline`` variables __cpp_inline_variables C++17 C++03
+Attributes on namespaces __cpp_namespace_attributes C++17 C++11
+Structured bindings __cpp_structured_bindings C++17 C++03
+template template arguments __cpp_template_template_args C++17 C++03
+Familiar template syntax for generic lambdas __cpp_generic_lambdas C++20 C++03
+``static operator[]`` __cpp_multidimensional_subscript C++20 C++03
+Designated initializers __cpp_designated_initializers C++20 C++03
+Conditional ``explicit`` __cpp_conditional_explicit C++20 C++03
+``using enum`` __cpp_using_enum C++20 C++03
+``if consteval`` __cpp_if_consteval C++23 C++20
+``static operator()`` __cpp_static_call_operator C++23 C++03
+Attributes on Lambda-Expressions C++23 C++11
+Attributes on Structured Bindings __cpp_structured_bindings C++26 C++03
+Packs in Structured Bindings __cpp_structured_bindings C++26 C++03
+Structured binding declaration as a condition C++26 C++11
+Static assert with user-generated message __cpp_static_assert >= 202306L C++26 C++11
+Pack Indexing __cpp_pack_indexing C++26 C++03
+``= delete ("should have a reason");`` __cpp_deleted_function C++26 C++03
+Variadic Friends __cpp_variadic_friend C++26 C++03
+--------------------------------------------- -------------------------------- ------------- -------------
+Designated initializers (N494) C99 C89
+Array & element qualification (N2607) C23 C89
+Attributes (N2335) C23 C89
+``#embed`` (N3017) C23 C89, C++
+============================================= ================================ ============= =============
Builtin type aliases
====================
>From ecb03db718cf4c62635f7f6d11d10f395f9eceb3 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 10 Mar 2025 20:16:53 +0800
Subject: [PATCH 09/11] Default evaluation flag to false
---
clang/lib/AST/ByteCode/Compiler.cpp | 8 ++++----
clang/lib/AST/ByteCode/Compiler.h | 2 +-
clang/lib/AST/ExprConstant.cpp | 8 +++-----
clang/lib/CodeGen/CGStmt.cpp | 12 ++++++------
clang/lib/CodeGen/CodeGenFunction.h | 2 +-
5 files changed, 15 insertions(+), 17 deletions(-)
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 4848d6a6e012e..fde601f2b5418 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -5181,7 +5181,7 @@ template <class Emitter> bool Compiler<Emitter>::visitIfStmt(const IfStmt *IS) {
return false;
if (const DeclStmt *CondDecl = IS->getConditionVariableDeclStmt())
- if (!visitDeclStmt(CondDecl, /*EvaluateConditionDecl=*/false))
+ if (!visitDeclStmt(CondDecl))
return false;
// Compile condition.
@@ -5258,7 +5258,7 @@ bool Compiler<Emitter>::visitWhileStmt(const WhileStmt *S) {
{
LocalScope<Emitter> CondScope(this);
if (const DeclStmt *CondDecl = S->getConditionVariableDeclStmt())
- if (!visitDeclStmt(CondDecl, /*EvaluateConditionDecl=*/false))
+ if (!visitDeclStmt(CondDecl))
return false;
if (!this->visitBool(Cond))
@@ -5340,7 +5340,7 @@ bool Compiler<Emitter>::visitForStmt(const ForStmt *S) {
{
LocalScope<Emitter> CondScope(this);
if (const DeclStmt *CondDecl = S->getConditionVariableDeclStmt())
- if (!visitDeclStmt(CondDecl, /*EvaluateConditionDecl=*/false))
+ if (!visitDeclStmt(CondDecl))
return false;
if (Cond) {
@@ -5468,7 +5468,7 @@ bool Compiler<Emitter>::visitSwitchStmt(const SwitchStmt *S) {
return false;
if (const DeclStmt *CondDecl = S->getConditionVariableDeclStmt())
- if (!visitDeclStmt(CondDecl, /*EvaluateConditionDecl=*/false))
+ if (!visitDeclStmt(CondDecl))
return false;
// Initialize condition variable.
diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h
index f59f5a3b91d59..bc991535bddfc 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -213,7 +213,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
// Statements.
bool visitCompoundStmt(const CompoundStmt *S);
- bool visitDeclStmt(const DeclStmt *DS, bool EvaluateConditionDecl);
+ bool visitDeclStmt(const DeclStmt *DS, bool EvaluateConditionDecl = false);
bool visitReturnStmt(const ReturnStmt *RS);
bool visitIfStmt(const IfStmt *IS);
bool visitWhileStmt(const WhileStmt *S);
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index edb840e9fed89..cd2a196454677 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -5222,7 +5222,7 @@ static bool EvaluateDecompositionDeclInit(EvalInfo &Info,
const DecompositionDecl *DD);
static bool EvaluateDecl(EvalInfo &Info, const Decl *D,
- bool EvaluateConditionDecl) {
+ bool EvaluateConditionDecl = false) {
if (const VarDecl *VD = dyn_cast<VarDecl>(D))
if (!EvaluateVarDecl(Info, VD))
return false;
@@ -5260,8 +5260,7 @@ static bool EvaluateCond(EvalInfo &Info, const VarDecl *CondDecl,
if (Cond->isValueDependent())
return false;
FullExpressionRAII Scope(Info);
- if (CondDecl &&
- !EvaluateDecl(Info, CondDecl, /*EvaluateConditionDecl=*/false))
+ if (CondDecl && !EvaluateDecl(Info, CondDecl))
return false;
if (!EvaluateAsBooleanCondition(Cond, Result, Info))
return false;
@@ -5342,8 +5341,7 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info,
FullExpressionRAII CondScope(Info);
if (SS->getConditionVariable() &&
- !EvaluateDecl(Info, SS->getConditionVariable(),
- /*EvaluateConditionDecl=*/false))
+ !EvaluateDecl(Info, SS->getConditionVariable()))
return ESR_Failed;
if (SS->getCond()->isValueDependent()) {
// We don't know what the value is, and which branch should jump to.
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 08dd38b23ddef..809d0fbebe255 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -894,7 +894,7 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
EmitStmt(S.getInit());
if (S.getConditionVariable())
- EmitDecl(*S.getConditionVariable(), /*EvaluateConditionDecl=*/false);
+ EmitDecl(*S.getConditionVariable());
// If the condition constant folds and can be elided, try to avoid emitting
// the condition and the dead arm of the if/else.
@@ -1100,7 +1100,7 @@ void CodeGenFunction::EmitWhileStmt(const WhileStmt &S,
RunCleanupsScope ConditionScope(*this);
if (S.getConditionVariable())
- EmitDecl(*S.getConditionVariable(), /*EvaluateConditionDecl=*/false);
+ EmitDecl(*S.getConditionVariable());
// Evaluate the conditional in the while header. C99 6.8.5.1: The
// evaluation of the controlling expression takes place before each
@@ -1319,7 +1319,7 @@ void CodeGenFunction::EmitForStmt(const ForStmt &S,
// If the for statement has a condition scope, emit the local variable
// declaration.
if (S.getConditionVariable()) {
- EmitDecl(*S.getConditionVariable(), /*EvaluateConditionDecl=*/false);
+ EmitDecl(*S.getConditionVariable());
// We have entered the condition variable's scope, so we're now able to
// jump to the continue block.
@@ -1679,7 +1679,7 @@ void CodeGenFunction::EmitDeclStmt(const DeclStmt &S) {
EmitStopPoint(&S);
for (const auto *I : S.decls())
- EmitDecl(*I);
+ EmitDecl(*I, /*EvaluateConditionDecl=*/true);
}
void CodeGenFunction::EmitBreakStmt(const BreakStmt &S) {
@@ -2244,7 +2244,7 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) {
// Emit the condition variable if needed inside the entire cleanup scope
// used by this special case for constant folded switches.
if (S.getConditionVariable())
- EmitDecl(*S.getConditionVariable(), /*EvaluateConditionDecl=*/false);
+ EmitDecl(*S.getConditionVariable());
if (auto *DD =
dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable()))
@@ -2278,7 +2278,7 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) {
EmitStmt(S.getInit());
if (S.getConditionVariable())
- EmitDecl(*S.getConditionVariable(), /*EvaluateConditionDecl=*/false);
+ EmitDecl(*S.getConditionVariable());
llvm::Value *CondV = EmitScalarExpr(S.getCond());
if (auto *DD =
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index d1be95d0b982b..07560666d1b40 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -3377,7 +3377,7 @@ class CodeGenFunction : public CodeGenTypeCache {
/// EmitDecl - Emit a declaration.
///
/// This function can be called with a null (unreachable) insert point.
- void EmitDecl(const Decl &D, bool EvaluateConditionDecl = true);
+ void EmitDecl(const Decl &D, bool EvaluateConditionDecl = false);
/// EmitVarDecl - Emit a local variable declaration.
///
>From 50e5ef65cea171e351ed61e5963b42ce6e366a44 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 11 Mar 2025 00:07:01 +0800
Subject: [PATCH 10/11] Keep going on evaluating the initializer
---
clang/lib/AST/ExprConstant.cpp | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index cd2a196454677..73ec99e20e43f 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -5223,26 +5223,25 @@ static bool EvaluateDecompositionDeclInit(EvalInfo &Info,
static bool EvaluateDecl(EvalInfo &Info, const Decl *D,
bool EvaluateConditionDecl = false) {
+ bool OK = true;
if (const VarDecl *VD = dyn_cast<VarDecl>(D))
- if (!EvaluateVarDecl(Info, VD))
- return false;
+ OK &= EvaluateVarDecl(Info, VD);
if (const DecompositionDecl *DD = dyn_cast<DecompositionDecl>(D);
EvaluateConditionDecl && DD)
- if (!EvaluateDecompositionDeclInit(Info, DD))
- return false;
+ OK &= EvaluateDecompositionDeclInit(Info, DD);
- return true;
+ return OK;
}
static bool EvaluateDecompositionDeclInit(EvalInfo &Info,
const DecompositionDecl *DD) {
+ bool OK = true;
for (auto *BD : DD->flat_bindings())
if (auto *VD = BD->getHoldingVar())
- if (!EvaluateDecl(Info, VD, /*EvaluateConditionDecl=*/true))
- return false;
+ OK &= EvaluateDecl(Info, VD, /*EvaluateConditionDecl=*/true);
- return true;
+ return OK;
}
static bool EvaluateDependentExpr(const Expr *E, EvalInfo &Info) {
>From 1a3e41f906b8df876e8e4ab29dc17ef882863d7c Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 11 Mar 2025 10:33:49 +0800
Subject: [PATCH 11/11] Address comments from mizvekov
---
clang/lib/AST/ByteCode/Compiler.cpp | 41 ++++++++++-----------------
clang/lib/AST/ByteCode/Compiler.h | 2 +-
clang/lib/AST/ExprConstant.cpp | 16 +++++++----
clang/lib/CodeGen/CGDecl.cpp | 15 +++++-----
clang/lib/CodeGen/CGStmt.cpp | 27 ++++--------------
clang/lib/CodeGen/CodeGenFunction.cpp | 3 +-
clang/lib/CodeGen/CodeGenFunction.h | 2 +-
clang/lib/Sema/SemaDeclCXX.cpp | 4 +--
8 files changed, 45 insertions(+), 65 deletions(-)
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index fde601f2b5418..5d5672e88232b 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -5105,12 +5105,12 @@ bool Compiler<Emitter>::visitCompoundStmt(const CompoundStmt *S) {
}
template <class Emitter>
-bool Compiler<Emitter>::emitDecompositionVarInit(const DecompositionDecl *DD) {
- for (auto *BD : DD->bindings())
- if (auto *KD = BD->getHoldingVar()) {
- if (!this->visitVarDecl(KD))
+bool Compiler<Emitter>::maybeEmitDeferredVarInit(const VarDecl *VD) {
+ if (auto *DD = dyn_cast_if_present<DecompositionDecl>(VD)) {
+ for (auto *BD : DD->bindings())
+ if (auto *KD = BD->getHoldingVar(); KD && !this->visitVarDecl(KD))
return false;
- }
+ }
return true;
}
@@ -5129,11 +5129,8 @@ bool Compiler<Emitter>::visitDeclStmt(const DeclStmt *DS,
return false;
// Register decomposition decl holding vars.
- if (const auto *DD = dyn_cast<DecompositionDecl>(VD);
- EvaluateConditionDecl && DD) {
- if (!this->emitDecompositionVarInit(DD))
- return false;
- }
+ if (EvaluateConditionDecl && !this->maybeEmitDeferredVarInit(VD))
+ return false;
}
return true;
@@ -5198,10 +5195,8 @@ template <class Emitter> bool Compiler<Emitter>::visitIfStmt(const IfStmt *IS) {
return false;
}
- if (auto *DD =
- dyn_cast_if_present<DecompositionDecl>(IS->getConditionVariable()))
- if (!this->emitDecompositionVarInit(DD))
- return false;
+ if (!this->maybeEmitDeferredVarInit(IS->getConditionVariable()))
+ return false;
if (const Stmt *Else = IS->getElse()) {
LabelTy LabelElse = this->getLabel();
@@ -5264,10 +5259,8 @@ bool Compiler<Emitter>::visitWhileStmt(const WhileStmt *S) {
if (!this->visitBool(Cond))
return false;
- if (auto *DD =
- dyn_cast_if_present<DecompositionDecl>(S->getConditionVariable()))
- if (!this->emitDecompositionVarInit(DD))
- return false;
+ if (!this->maybeEmitDeferredVarInit(S->getConditionVariable()))
+ return false;
if (!this->jumpFalse(EndLabel))
return false;
@@ -5350,10 +5343,8 @@ bool Compiler<Emitter>::visitForStmt(const ForStmt *S) {
return false;
}
- if (auto *DD =
- dyn_cast_if_present<DecompositionDecl>(S->getConditionVariable()))
- if (!this->emitDecompositionVarInit(DD))
- return false;
+ if (!this->maybeEmitDeferredVarInit(S->getConditionVariable()))
+ return false;
if (Body && !this->visitStmt(Body))
return false;
@@ -5477,10 +5468,8 @@ bool Compiler<Emitter>::visitSwitchStmt(const SwitchStmt *S) {
if (!this->emitSetLocal(CondT, CondVar, S))
return false;
- if (auto *DD =
- dyn_cast_if_present<DecompositionDecl>(S->getConditionVariable()))
- if (!this->emitDecompositionVarInit(DD))
- return false;
+ if (!this->maybeEmitDeferredVarInit(S->getConditionVariable()))
+ return false;
CaseMap CaseLabels;
// Create labels and comparison ops for all case statements.
diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h
index bc991535bddfc..e6db700eac68c 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -386,7 +386,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
bool compileUnionAssignmentOperator(const CXXMethodDecl *MD);
bool checkLiteralType(const Expr *E);
- bool emitDecompositionVarInit(const DecompositionDecl *DD);
+ bool maybeEmitDeferredVarInit(const VarDecl *VD);
protected:
/// Variable to storage mapping.
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 73ec99e20e43f..5e8ddafd68655 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -5244,6 +5244,15 @@ static bool EvaluateDecompositionDeclInit(EvalInfo &Info,
return OK;
}
+static bool MaybeEvaluateDeferredVarDeclInit(EvalInfo &Info,
+ const VarDecl *VD) {
+ if (auto *DD = dyn_cast_if_present<DecompositionDecl>(VD)) {
+ if (!EvaluateDecompositionDeclInit(Info, DD))
+ return false;
+ }
+ return true;
+}
+
static bool EvaluateDependentExpr(const Expr *E, EvalInfo &Info) {
assert(E->isValueDependent());
if (Info.noteSideEffect())
@@ -5263,8 +5272,7 @@ 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 && !EvaluateDecompositionDeclInit(Info, DD))
+ if (!MaybeEvaluateDeferredVarDeclInit(Info, CondDecl))
return false;
return Scope.destroy();
}
@@ -5350,9 +5358,7 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info,
if (!EvaluateInteger(SS->getCond(), Value, Info))
return ESR_Failed;
- if (auto *DD =
- dyn_cast_if_present<DecompositionDecl>(SS->getConditionVariable());
- DD && !EvaluateDecompositionDeclInit(Info, DD))
+ if (!MaybeEvaluateDeferredVarDeclInit(Info, SS->getConditionVariable()))
return ESR_Failed;
if (!CondScope.destroy())
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 8de51c3af39f3..bdad74a9c9c0b 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -164,9 +164,8 @@ void CodeGenFunction::EmitDecl(const Decl &D, bool EvaluateConditionDecl) {
assert(VD.isLocalVarDecl() &&
"Should not see file-scope variables inside a function!");
EmitVarDecl(VD);
- if (auto *DD = dyn_cast<DecompositionDecl>(&VD);
- EvaluateConditionDecl && DD)
- EmitDecompositionVarInit(*DD);
+ if (EvaluateConditionDecl)
+ MaybeEmitDeferredVarDeclInit(&VD);
return;
}
@@ -2056,10 +2055,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);
+void CodeGenFunction::MaybeEmitDeferredVarDeclInit(const VarDecl *VD) {
+ if (auto *DD = dyn_cast_if_present<DecompositionDecl>(VD)) {
+ 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.)
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 809d0fbebe255..99b6f563d7c82 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -913,9 +913,7 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
if (CondConstant)
incrementProfileCounter(&S);
if (Executed) {
- if (auto *DD = dyn_cast_if_present<DecompositionDecl>(
- S.getConditionVariable()))
- EmitDecompositionVarInit(*DD);
+ MaybeEmitDeferredVarDeclInit(S.getConditionVariable());
RunCleanupsScope ExecutedScope(*this);
EmitStmt(Executed);
}
@@ -961,9 +959,7 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
/*ConditionalDecl=*/S.getConditionVariable());
} else {
llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond());
- if (auto *DD =
- dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable()))
- EmitDecompositionVarInit(*DD);
+ MaybeEmitDeferredVarDeclInit(S.getConditionVariable());
Builder.CreateCondBr(BoolCondVal, ThenBlock, ElseBlock);
}
@@ -1107,9 +1103,7 @@ void CodeGenFunction::EmitWhileStmt(const WhileStmt &S,
// execution of the loop body.
llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond());
- if (auto *DD =
- dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable()))
- EmitDecompositionVarInit(*DD);
+ MaybeEmitDeferredVarDeclInit(S.getConditionVariable());
// while(1) is common, avoid extra exit blocks. Be sure
// to correctly handle break/continue though.
@@ -1345,9 +1339,7 @@ void CodeGenFunction::EmitForStmt(const ForStmt &S,
// compares unequal to 0. The condition must be a scalar type.
llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond());
- if (auto *DD =
- dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable()))
- EmitDecompositionVarInit(*DD);
+ MaybeEmitDeferredVarDeclInit(S.getConditionVariable());
llvm::MDNode *Weights =
createProfileWeightsForLoop(S.getCond(), getProfileCount(S.getBody()));
@@ -2244,11 +2236,7 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) {
// Emit the condition variable if needed inside the entire cleanup scope
// used by this special case for constant folded switches.
if (S.getConditionVariable())
- EmitDecl(*S.getConditionVariable());
-
- if (auto *DD =
- dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable()))
- EmitDecompositionVarInit(*DD);
+ EmitDecl(*S.getConditionVariable(), /*EvaluateConditionDecl=*/true);
// At this point, we are no longer "within" a switch instance, so
// we can temporarily enforce this to ensure that any embedded case
@@ -2280,10 +2268,7 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) {
if (S.getConditionVariable())
EmitDecl(*S.getConditionVariable());
llvm::Value *CondV = EmitScalarExpr(S.getCond());
-
- if (auto *DD =
- dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable()))
- EmitDecompositionVarInit(*DD);
+ MaybeEmitDeferredVarDeclInit(S.getConditionVariable());
// Create basic block to hold stuff that comes after switch
// statement. We also need to create a default block now so that
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 8c4edb809ebc8..0feefa017060e 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -2048,8 +2048,7 @@ void CodeGenFunction::EmitBranchOnBoolExpr(
CondV = EvaluateExprAsBool(Cond);
}
- if (auto *DD = dyn_cast_if_present<DecompositionDecl>(ConditionalDecl))
- EmitDecompositionVarInit(*DD);
+ MaybeEmitDeferredVarDeclInit(ConditionalDecl);
// If not at the top of the logical operator nest, update MCDC temp with the
// boolean result of the evaluated condition.
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 07560666d1b40..ca00a0e8c6cf4 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -3474,7 +3474,7 @@ class CodeGenFunction : public CodeGenTypeCache {
void emitAutoVarTypeCleanup(const AutoVarEmission &emission,
QualType::DestructionKind dtorKind);
- void EmitDecompositionVarInit(const DecompositionDecl &var);
+ void MaybeEmitDeferredVarDeclInit(const VarDecl *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)
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 0e6017b75cfd8..3cc6ab6807b05 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -743,10 +743,10 @@ Sema::ActOnDecompositionDeclarator(Scope *S, Declarator &D,
unsigned DiagID;
if (!getLangOpts().CPlusPlus17)
DiagID = diag::ext_decomp_decl;
- else if (D.getContext() == DeclaratorContext::Condition) {
+ else if (D.getContext() == DeclaratorContext::Condition)
DiagID = getLangOpts().CPlusPlus26 ? diag::warn_cxx26_decomp_decl_cond
: diag::ext_decomp_decl_cond;
- } else
+ else
DiagID = diag::warn_cxx14_compat_decomp_decl;
Diag(Decomp.getLSquareLoc(), DiagID) << Decomp.getSourceRange();
More information about the cfe-commits
mailing list