llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Younan Zhang (zyn0217) <details> <summary>Changes</summary> Todo: - [x] Adapt the bytecode interpreter to the new evaluation model - [x] Tests for CG & Constant evaluation - [x] Release notes With this patch, the example shown in https://gcc.godbolt.org/z/3h74oq8zW (the R2 semantics) works now. --- Patch is 41.80 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/130228.diff 23 Files Affected: - (modified) clang/docs/ReleaseNotes.rst (+2) - (modified) clang/include/clang/AST/DeclCXX.h (+19-8) - (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+6-2) - (modified) clang/lib/AST/ASTImporter.cpp (+4-3) - (modified) clang/lib/AST/ByteCode/Compiler.cpp (+39-6) - (modified) clang/lib/AST/ByteCode/Compiler.h (+1) - (modified) clang/lib/AST/DeclCXX.cpp (+12-9) - (modified) clang/lib/AST/ExprConstant.cpp (+26-4) - (modified) clang/lib/CodeGen/CGDecl.cpp (+9-4) - (modified) clang/lib/CodeGen/CGStmt.cpp (+35-3) - (modified) clang/lib/CodeGen/CodeGenFunction.cpp (+6-1) - (modified) clang/lib/CodeGen/CodeGenFunction.h (+4-1) - (modified) clang/lib/Sema/SemaDecl.cpp (+4-3) - (modified) clang/lib/Sema/SemaDeclCXX.cpp (+10-7) - (modified) clang/lib/Sema/SemaTemplateInstantiateDecl.cpp (+5-3) - (modified) clang/lib/Serialization/ASTReaderDecl.cpp (+6-3) - (modified) clang/lib/Serialization/ASTWriterDecl.cpp (+1) - (modified) clang/test/AST/ByteCode/if.cpp (+1-1) - (added) clang/test/CodeGen/p0963r3.cpp (+212) - (modified) clang/test/Parser/cxx1z-decomposition.cpp (+6-6) - (modified) clang/test/Parser/decomposed-condition.cpp (+12-12) - (modified) clang/test/SemaCXX/decomposed-condition.cpp (+11-5) - (modified) clang/www/cxx_status.html (+1-1) ``````````diff 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 dbd02ef7f8011..d82d9900945a1 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,12 @@ 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/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/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/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..898f8a2fce769 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(); } @@ -5335,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/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..0a49eadbf33d1 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); } @@ -1099,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); @@ -1332,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) @@ -2229,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. @@ -2260,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/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::BasicB... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/130228 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits