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