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 <zyn7...@gmail.com> Date: Fri, 7 Mar 2025 11:38:11 +0800 Subject: [PATCH 1/6] [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@-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 <zyn7...@gmail.com> Date: Sun, 9 Mar 2025 23:29:02 +0800 Subject: [PATCH 2/6] 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 <zyn7...@gmail.com> Date: Sun, 9 Mar 2025 23:38:06 +0800 Subject: [PATCH 3/6] 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@-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@-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@-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 <zyn7...@gmail.com> Date: Mon, 10 Mar 2025 13:56:32 +0800 Subject: [PATCH 4/6] 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 <zyn7...@gmail.com> Date: Mon, 10 Mar 2025 16:06:21 +0800 Subject: [PATCH 5/6] 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 <zyn7...@gmail.com> Date: Mon, 10 Mar 2025 19:33:52 +0800 Subject: [PATCH 6/6] 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()) _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits