diff options
Diffstat (limited to 'clang/lib')
27 files changed, 401 insertions, 76 deletions
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index cd1bcb3..4e387da 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -12841,11 +12841,12 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) { // Likewise, variables with tuple-like bindings are required if their // bindings have side-effects. - if (const auto *DD = dyn_cast<DecompositionDecl>(VD)) - for (const auto *BD : DD->bindings()) + if (const auto *DD = dyn_cast<DecompositionDecl>(VD)) { + for (const auto *BD : DD->flat_bindings()) if (const auto *BindingVD = BD->getHoldingVar()) if (DeclMustBeEmitted(BindingVD)) return true; + } return false; } diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 09fa10f..c9f2f90 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -2553,7 +2553,7 @@ ExpectedDecl ASTNodeImporter::VisitBindingDecl(BindingDecl *D) { BindingDecl *ToD; if (GetImportedOrCreateDecl(ToD, D, Importer.getToContext(), DC, Loc, - Name.getAsIdentifierInfo())) + Name.getAsIdentifierInfo(), D->getType())) return ToD; Error Err = Error::success(); diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index ba77c74..610207c 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -2659,10 +2659,6 @@ bool VarDecl::checkForConstantInitialization( return Eval->HasConstantInitialization; } -bool VarDecl::isParameterPack() const { - return isa<PackExpansionType>(getType()); -} - template<typename DeclT> static DeclT *getDefinitionOrSelf(DeclT *D) { assert(D); @@ -5421,6 +5417,13 @@ bool ValueDecl::isInitCapture() const { return false; } +bool ValueDecl::isParameterPack() const { + if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(this)) + return NTTP->isParameterPack(); + + return isa_and_nonnull<PackExpansionType>(getType().getTypePtrOrNull()); +} + void ImplicitParamDecl::anchor() {} ImplicitParamDecl *ImplicitParamDecl::Create(ASTContext &C, DeclContext *DC, diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index 8506b95..c0a331d 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -245,7 +245,7 @@ bool Decl::isTemplateParameterPack() const { } bool Decl::isParameterPack() const { - if (const auto *Var = dyn_cast<VarDecl>(this)) + if (const auto *Var = dyn_cast<ValueDecl>(this)) return Var->isParameterPack(); return isTemplateParameterPack(); diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index a023a9f4..e394e05 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -3462,19 +3462,21 @@ VarDecl *ValueDecl::getPotentiallyDecomposedVarDecl() { if (auto *Var = llvm::dyn_cast<VarDecl>(this)) return Var; if (auto *BD = llvm::dyn_cast<BindingDecl>(this)) - return llvm::dyn_cast<VarDecl>(BD->getDecomposedDecl()); + return llvm::dyn_cast_if_present<VarDecl>(BD->getDecomposedDecl()); return nullptr; } void BindingDecl::anchor() {} BindingDecl *BindingDecl::Create(ASTContext &C, DeclContext *DC, - SourceLocation IdLoc, IdentifierInfo *Id) { - return new (C, DC) BindingDecl(DC, IdLoc, Id); + SourceLocation IdLoc, IdentifierInfo *Id, + QualType T) { + return new (C, DC) BindingDecl(DC, IdLoc, Id, T); } BindingDecl *BindingDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) { - return new (C, ID) BindingDecl(nullptr, SourceLocation(), nullptr); + return new (C, ID) + BindingDecl(nullptr, SourceLocation(), nullptr, QualType()); } VarDecl *BindingDecl::getHoldingVar() const { @@ -3490,6 +3492,12 @@ VarDecl *BindingDecl::getHoldingVar() const { return VD; } +llvm::ArrayRef<Expr *> BindingDecl::getBindingPackExprs() const { + assert(Binding && "expecting a pack expr"); + auto *RP = cast<ResolvedUnexpandedPackExpr>(Binding); + return RP->getExprs(); +} + void DecompositionDecl::anchor() {} DecompositionDecl *DecompositionDecl::Create(ASTContext &C, DeclContext *DC, diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 06b0491..4fc6291 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -3659,6 +3659,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, case PackIndexingExprClass: case HLSLOutArgExprClass: case OpenACCAsteriskSizeExprClass: + case ResolvedUnexpandedPackExprClass: // These never have a side-effect. return false; diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index 5bf5d6a..d900af8 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -1965,3 +1965,52 @@ CXXFoldExpr::CXXFoldExpr(QualType T, UnresolvedLookupExpr *Callee, SubExprs[SubExpr::RHS] = RHS; setDependence(computeDependence(this)); } + +ResolvedUnexpandedPackExpr::ResolvedUnexpandedPackExpr(SourceLocation BL, + QualType QT, + unsigned NumExprs) + : Expr(ResolvedUnexpandedPackExprClass, QT, VK_PRValue, OK_Ordinary), + BeginLoc(BL), NumExprs(NumExprs) { + // C++ [temp.dep.expr]p3 + // An id-expression is type-dependent if it is + // - associated by name lookup with a pack + setDependence(ExprDependence::TypeValueInstantiation | + ExprDependence::UnexpandedPack); +} + +ResolvedUnexpandedPackExpr * +ResolvedUnexpandedPackExpr::CreateDeserialized(ASTContext &Ctx, + unsigned NumExprs) { + void *Mem = Ctx.Allocate(totalSizeToAlloc<Expr *>(NumExprs), + alignof(ResolvedUnexpandedPackExpr)); + return new (Mem) + ResolvedUnexpandedPackExpr(SourceLocation(), QualType(), NumExprs); +} + +ResolvedUnexpandedPackExpr * +ResolvedUnexpandedPackExpr::Create(ASTContext &Ctx, SourceLocation BL, + QualType T, unsigned NumExprs) { + void *Mem = Ctx.Allocate(totalSizeToAlloc<Expr *>(NumExprs), + alignof(ResolvedUnexpandedPackExpr)); + ResolvedUnexpandedPackExpr *New = + new (Mem) ResolvedUnexpandedPackExpr(BL, T, NumExprs); + + auto Exprs = New->getExprs(); + std::uninitialized_fill(Exprs.begin(), Exprs.end(), nullptr); + + return New; +} + +ResolvedUnexpandedPackExpr * +ResolvedUnexpandedPackExpr::Create(ASTContext &Ctx, SourceLocation BL, + QualType T, ArrayRef<Expr *> Exprs) { + auto *New = Create(Ctx, BL, T, Exprs.size()); + std::uninitialized_copy(Exprs.begin(), Exprs.end(), New->getExprs().begin()); + return New; +} + +ResolvedUnexpandedPackExpr *ResolvedUnexpandedPackExpr::getFromDecl(Decl *D) { + if (auto *BD = dyn_cast<BindingDecl>(D)) + return dyn_cast_if_present<ResolvedUnexpandedPackExpr>(BD->getBinding()); + return nullptr; +} diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp index 3f37d06..5225c3c 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -451,6 +451,13 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::PackExpansionExprClass: return ClassifyInternal(Ctx, cast<PackExpansionExpr>(E)->getPattern()); + case Expr::ResolvedUnexpandedPackExprClass: { + if (cast<ResolvedUnexpandedPackExpr>(E)->getNumExprs() > 0) + return ClassifyInternal( + Ctx, cast<ResolvedUnexpandedPackExpr>(E)->getExpansion(0)); + return Cl::CL_LValue; + } + case Expr::MaterializeTemporaryExprClass: return cast<MaterializeTemporaryExpr>(E)->isBoundToLvalueReference() ? Cl::CL_LValue diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 0e41e3d..37019b5 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -5225,7 +5225,7 @@ static bool EvaluateDecl(EvalInfo &Info, const Decl *D) { OK &= EvaluateVarDecl(Info, VD); if (const DecompositionDecl *DD = dyn_cast<DecompositionDecl>(D)) - for (auto *BD : DD->bindings()) + for (auto *BD : DD->flat_bindings()) if (auto *VD = BD->getHoldingVar()) OK &= EvaluateDecl(Info, VD); @@ -17253,6 +17253,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::SYCLUniqueStableNameExprClass: case Expr::CXXParenListInitExprClass: case Expr::HLSLOutArgExprClass: + case Expr::ResolvedUnexpandedPackExprClass: return ICEDiag(IK_NotICE, E->getBeginLoc()); case Expr::InitListExprClass: { diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 49089c0..e889b74 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -4933,7 +4933,7 @@ recurse: case Expr::SourceLocExprClass: case Expr::EmbedExprClass: case Expr::BuiltinBitCastExprClass: - { + case Expr::ResolvedUnexpandedPackExprClass: { NotPrimaryExpr(); if (!NullOut) { // As bad as this diagnostic is, it's better than crashing. diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 9efc884..84d4e1f 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -45,9 +45,11 @@ #include "clang/Basic/TypeTraits.h" #include "clang/Lex/Lexer.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" @@ -2587,6 +2589,15 @@ void StmtPrinter::VisitPackIndexingExpr(PackIndexingExpr *E) { OS << "]"; } +void StmtPrinter::VisitResolvedUnexpandedPackExpr( + ResolvedUnexpandedPackExpr *E) { + OS << "<<resolved pack("; + llvm::interleave( + E->getExprs().begin(), E->getExprs().end(), + [this](auto *X) { PrintExpr(X); }, [this] { OS << ", "; }); + OS << ")>>"; +} + void StmtPrinter::VisitSubstNonTypeTemplateParmPackExpr( SubstNonTypeTemplateParmPackExpr *Node) { OS << *Node->getParameterPack(); diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index 5d1f370..84985fcb 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -2280,6 +2280,10 @@ void StmtProfiler::VisitSizeOfPackExpr(const SizeOfPackExpr *S) { ID.AddInteger(0); } } +void StmtProfiler::VisitResolvedUnexpandedPackExpr( + const ResolvedUnexpandedPackExpr *S) { + VisitExpr(S); +} void StmtProfiler::VisitPackIndexingExpr(const PackIndexingExpr *E) { VisitExpr(E); diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index f0abfaa..db59579 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -5083,10 +5083,9 @@ CGDebugInfo::EmitDeclareOfAutoVariable(const VarDecl *VD, llvm::Value *Storage, assert(CGM.getCodeGenOpts().hasReducedDebugInfo()); if (auto *DD = dyn_cast<DecompositionDecl>(VD)) { - for (auto *B : DD->bindings()) { + for (BindingDecl *B : DD->flat_bindings()) EmitDeclare(B, Storage, std::nullopt, Builder, VD->getType()->isReferenceType()); - } // Don't emit an llvm.dbg.declare for the composite storage as it doesn't // correspond to a user variable. return nullptr; diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 747e420d..cc6815d 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -164,9 +164,10 @@ void CodeGenFunction::EmitDecl(const Decl &D) { "Should not see file-scope variables inside a function!"); EmitVarDecl(VD); if (auto *DD = dyn_cast<DecompositionDecl>(&VD)) - for (auto *B : DD->bindings()) + for (auto *B : DD->flat_bindings()) if (auto *HD = B->getHoldingVar()) EmitVarDecl(*HD); + return; } diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index eb8d3ce..a015d64 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -7014,9 +7014,10 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { case Decl::VarTemplateSpecialization: EmitGlobal(cast<VarDecl>(D)); if (auto *DD = dyn_cast<DecompositionDecl>(D)) - for (auto *B : DD->bindings()) + for (auto *B : DD->flat_bindings()) if (auto *HD = B->getHoldingVar()) EmitGlobal(HD); + break; // Indirect fields from global anonymous structs and unions can be diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index f136d50..75b5e11 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -7315,15 +7315,16 @@ void Parser::ParseDecompositionDeclarator(Declarator &D) { // If this doesn't look like a structured binding, maybe it's a misplaced // array declarator. - if (!(Tok.is(tok::identifier) && + if (!(Tok.isOneOf(tok::identifier, tok::ellipsis) && NextToken().isOneOf(tok::comma, tok::r_square, tok::kw_alignas, - tok::l_square)) && + tok::identifier, tok::l_square, tok::ellipsis)) && !(Tok.is(tok::r_square) && NextToken().isOneOf(tok::equal, tok::l_brace))) { PA.Revert(); return ParseMisplacedBracketDeclarator(D); } + SourceLocation PrevEllipsisLoc; SmallVector<DecompositionDeclarator::Binding, 32> Bindings; while (Tok.isNot(tok::r_square)) { if (!Bindings.empty()) { @@ -7338,11 +7339,11 @@ void Parser::ParseDecompositionDeclarator(Declarator &D) { Diag(Tok, diag::err_expected_comma_or_rsquare); } - SkipUntil(tok::r_square, tok::comma, tok::identifier, + SkipUntil({tok::r_square, tok::comma, tok::identifier, tok::ellipsis}, StopAtSemi | StopBeforeMatch); if (Tok.is(tok::comma)) ConsumeToken(); - else if (Tok.isNot(tok::identifier)) + else if (Tok.is(tok::r_square)) break; } } @@ -7350,6 +7351,21 @@ void Parser::ParseDecompositionDeclarator(Declarator &D) { if (isCXX11AttributeSpecifier()) DiagnoseAndSkipCXX11Attributes(); + SourceLocation EllipsisLoc; + + if (Tok.is(tok::ellipsis)) { + Diag(Tok, getLangOpts().CPlusPlus26 ? diag::warn_cxx23_compat_binding_pack + : diag::ext_cxx_binding_pack); + if (PrevEllipsisLoc.isValid()) { + Diag(Tok, diag::err_binding_multiple_ellipses); + Diag(PrevEllipsisLoc, diag::note_previous_ellipsis); + break; + } + EllipsisLoc = Tok.getLocation(); + PrevEllipsisLoc = EllipsisLoc; + ConsumeToken(); + } + if (Tok.isNot(tok::identifier)) { Diag(Tok, diag::err_expected) << tok::identifier; break; @@ -7359,6 +7375,13 @@ void Parser::ParseDecompositionDeclarator(Declarator &D) { SourceLocation Loc = Tok.getLocation(); ConsumeToken(); + if (Tok.is(tok::ellipsis) && !PrevEllipsisLoc.isValid()) { + DiagnoseMisplacedEllipsis(Tok.getLocation(), Loc, EllipsisLoc.isValid(), + true); + EllipsisLoc = Tok.getLocation(); + ConsumeToken(); + } + ParsedAttributes Attrs(AttrFactory); if (isCXX11AttributeSpecifier()) { Diag(Tok, getLangOpts().CPlusPlus26 @@ -7367,7 +7390,7 @@ void Parser::ParseDecompositionDeclarator(Declarator &D) { MaybeParseCXX11Attributes(Attrs); } - Bindings.push_back({II, Loc, std::move(Attrs)}); + Bindings.push_back({II, Loc, std::move(Attrs), EllipsisLoc}); } if (Tok.isNot(tok::r_square)) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index e4e3bba..0cf02fe 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -888,7 +888,15 @@ Sema::ActOnDecompositionDeclarator(Scope *S, Declarator &D, Previous.clear(); } - auto *BD = BindingDecl::Create(Context, DC, B.NameLoc, VarName); + QualType QT; + if (B.EllipsisLoc.isValid()) { + if (!cast<Decl>(DC)->isTemplated()) + Diag(B.EllipsisLoc, diag::err_pack_outside_template); + QT = Context.getPackExpansionType(Context.DependentTy, std::nullopt, + /*ExpectsPackInType=*/false); + } + + auto *BD = BindingDecl::Create(Context, DC, B.NameLoc, B.Name, QT); ProcessDeclAttributeList(S, BD, *B.Attrs); @@ -951,20 +959,68 @@ Sema::ActOnDecompositionDeclarator(Scope *S, Declarator &D, return New; } +// Check the arity of the structured bindings. +// Create the resolved pack expr if needed. +static bool CheckBindingsCount(Sema &S, DecompositionDecl *DD, + QualType DecompType, + ArrayRef<BindingDecl *> Bindings, + unsigned MemberCount) { + auto BindingWithPackItr = + std::find_if(Bindings.begin(), Bindings.end(), + [](BindingDecl *D) -> bool { return D->isParameterPack(); }); + bool HasPack = BindingWithPackItr != Bindings.end(); + bool IsValid; + if (!HasPack) { + IsValid = Bindings.size() == MemberCount; + } else { + // There may not be more members than non-pack bindings. + IsValid = MemberCount >= Bindings.size() - 1; + } + + if (IsValid && HasPack) { + // Create the pack expr and assign it to the binding. + unsigned PackSize = MemberCount - Bindings.size() + 1; + QualType PackType = S.Context.getPackExpansionType( + S.Context.DependentTy, std::nullopt, /*ExpectsPackInType=*/false); + BindingDecl *BD = (*BindingWithPackItr); + auto *RP = ResolvedUnexpandedPackExpr::Create(S.Context, DD->getBeginLoc(), + DecompType, PackSize); + BD->setDecomposedDecl(DD); + BD->setBinding(PackType, RP); + + BindingDecl *BPack = *BindingWithPackItr; + // Create the nested BindingDecls. + for (Expr *&E : RP->getExprs()) { + auto *NestedBD = BindingDecl::Create(S.Context, BPack->getDeclContext(), + BPack->getLocation(), + BPack->getIdentifier(), QualType()); + NestedBD->setDecomposedDecl(DD); + E = S.BuildDeclRefExpr(NestedBD, S.Context.DependentTy, VK_LValue, + BPack->getLocation()); + } + } + + if (IsValid) + return false; + + S.Diag(DD->getLocation(), diag::err_decomp_decl_wrong_number_bindings) + << DecompType << (unsigned)Bindings.size() << MemberCount << MemberCount + << (MemberCount < Bindings.size()); + return true; +} + static bool checkSimpleDecomposition( Sema &S, ArrayRef<BindingDecl *> Bindings, ValueDecl *Src, - QualType DecompType, const llvm::APSInt &NumElems, QualType ElemType, + QualType DecompType, const llvm::APSInt &NumElemsAPS, QualType ElemType, llvm::function_ref<ExprResult(SourceLocation, Expr *, unsigned)> GetInit) { - if ((int64_t)Bindings.size() != NumElems) { - S.Diag(Src->getLocation(), diag::err_decomp_decl_wrong_number_bindings) - << DecompType << (unsigned)Bindings.size() - << (unsigned)NumElems.getLimitedValue(UINT_MAX) - << toString(NumElems, 10) << (NumElems < Bindings.size()); + unsigned NumElems = (unsigned)NumElemsAPS.getLimitedValue(UINT_MAX); + auto *DD = cast<DecompositionDecl>(Src); + + if (CheckBindingsCount(S, DD, DecompType, Bindings, NumElems)) return true; - } unsigned I = 0; - for (auto *B : Bindings) { + for (auto *B : DD->flat_bindings()) { SourceLocation Loc = B->getLocation(); ExprResult E = S.BuildDeclRefExpr(Src, DecompType, VK_LValue, Loc); if (E.isInvalid()) @@ -1210,13 +1266,10 @@ static bool checkTupleLikeDecomposition(Sema &S, ArrayRef<BindingDecl *> Bindings, VarDecl *Src, QualType DecompType, const llvm::APSInt &TupleSize) { - if ((int64_t)Bindings.size() != TupleSize) { - S.Diag(Src->getLocation(), diag::err_decomp_decl_wrong_number_bindings) - << DecompType << (unsigned)Bindings.size() - << (unsigned)TupleSize.getLimitedValue(UINT_MAX) - << toString(TupleSize, 10) << (TupleSize < Bindings.size()); + auto *DD = cast<DecompositionDecl>(Src); + unsigned NumElems = (unsigned)TupleSize.getLimitedValue(UINT_MAX); + if (CheckBindingsCount(S, DD, DecompType, Bindings, NumElems)) return true; - } if (Bindings.empty()) return false; @@ -1250,7 +1303,7 @@ static bool checkTupleLikeDecomposition(Sema &S, } unsigned I = 0; - for (auto *B : Bindings) { + for (auto *B : DD->flat_bindings()) { InitializingBinding InitContext(S, B); SourceLocation Loc = B->getLocation(); @@ -1433,20 +1486,18 @@ static bool checkMemberDecomposition(Sema &S, ArrayRef<BindingDecl*> Bindings, QualType BaseType = S.Context.getQualifiedType(S.Context.getRecordType(RD), DecompType.getQualifiers()); - auto DiagnoseBadNumberOfBindings = [&]() -> bool { - unsigned NumFields = llvm::count_if( - RD->fields(), [](FieldDecl *FD) { return !FD->isUnnamedBitField(); }); - assert(Bindings.size() != NumFields); - S.Diag(Src->getLocation(), diag::err_decomp_decl_wrong_number_bindings) - << DecompType << (unsigned)Bindings.size() << NumFields << NumFields - << (NumFields < Bindings.size()); + auto *DD = cast<DecompositionDecl>(Src); + unsigned NumFields = llvm::count_if( + RD->fields(), [](FieldDecl *FD) { return !FD->isUnnamedBitField(); }); + if (CheckBindingsCount(S, DD, DecompType, Bindings, NumFields)) return true; - }; // all of E's non-static data members shall be [...] well-formed // when named as e.name in the context of the structured binding, // E shall not have an anonymous union member, ... - unsigned I = 0; + auto FlatBindings = DD->flat_bindings(); + assert(llvm::range_size(FlatBindings) == NumFields); + auto FlatBindingsItr = FlatBindings.begin(); for (auto *FD : RD->fields()) { if (FD->isUnnamedBitField()) continue; @@ -1471,9 +1522,8 @@ static bool checkMemberDecomposition(Sema &S, ArrayRef<BindingDecl*> Bindings, } // We have a real field to bind. - if (I >= Bindings.size()) - return DiagnoseBadNumberOfBindings(); - auto *B = Bindings[I++]; + assert(FlatBindingsItr != FlatBindings.end()); + BindingDecl *B = *(FlatBindingsItr++); SourceLocation Loc = B->getLocation(); // The field must be accessible in the context of the structured binding. @@ -1511,9 +1561,6 @@ static bool checkMemberDecomposition(Sema &S, ArrayRef<BindingDecl*> Bindings, B->setBinding(S.BuildQualifiedType(FD->getType(), Loc, Q), E.get()); } - if (I != Bindings.size()) - return DiagnoseBadNumberOfBindings(); - return false; } @@ -1523,8 +1570,12 @@ void Sema::CheckCompleteDecompositionDeclaration(DecompositionDecl *DD) { // If the type of the decomposition is dependent, then so is the type of // each binding. if (DecompType->isDependentType()) { - for (auto *B : DD->bindings()) - B->setType(Context.DependentTy); + // Note that all of the types are still Null or PackExpansionType. + for (auto *B : DD->bindings()) { + // Do not overwrite any pack type. + if (B->getType().isNull()) + B->setType(Context.DependentTy); + } return; } diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index 470d0d7..7b08a06 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1068,7 +1068,7 @@ static CanThrowResult canVarDeclThrow(Sema &Self, const VarDecl *VD) { // If this is a decomposition declaration, bindings might throw. if (auto *DD = dyn_cast<DecompositionDecl>(VD)) - for (auto *B : DD->bindings()) + for (auto *B : DD->flat_bindings()) if (auto *HD = B->getHoldingVar()) CT = mergeCanThrow(CT, canVarDeclThrow(Self, HD)); @@ -1286,6 +1286,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) { case Expr::ConvertVectorExprClass: case Expr::VAArgExprClass: case Expr::CXXParenListInitExprClass: + case Expr::ResolvedUnexpandedPackExprClass: return canSubStmtsThrow(*this, S); case Expr::CompoundLiteralExprClass: diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 947651d..ec38674 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -2716,8 +2716,10 @@ StmtResult Sema::BuildCXXForRangeStmt( // them in properly when we instantiate the loop. if (!LoopVar->isInvalidDecl() && Kind != BFRK_Check) { if (auto *DD = dyn_cast<DecompositionDecl>(LoopVar)) - for (auto *Binding : DD->bindings()) - Binding->setType(Context.DependentTy); + for (auto *Binding : DD->bindings()) { + if (!Binding->isParameterPack()) + Binding->setType(Context.DependentTy); + } LoopVar->setType(SubstAutoTypeDependent(LoopVar->getType())); } } else if (!BeginDeclStmt.get()) { diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index b4fa23d..12e98a3 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -1583,6 +1583,10 @@ namespace { /// pack. ExprResult TransformFunctionParmPackExpr(FunctionParmPackExpr *E); + // Transform a ResolvedUnexpandedPackExpr + ExprResult + TransformResolvedUnexpandedPackExpr(ResolvedUnexpandedPackExpr *E); + QualType TransformFunctionProtoType(TypeLocBuilder &TLB, FunctionProtoTypeLoc TL) { // Call the base version; it will forward to our overridden version below. @@ -1848,7 +1852,8 @@ bool TemplateInstantiator::AlreadyTransformed(QualType T) { if (T.isNull()) return true; - if (T->isInstantiationDependentType() || T->isVariablyModifiedType()) + if (T->isInstantiationDependentType() || T->isVariablyModifiedType() || + T->containsUnexpandedParameterPack()) return false; getSema().MarkDeclarationsReferencedInType(Loc, T); @@ -2473,6 +2478,15 @@ TemplateInstantiator::TransformDeclRefExpr(DeclRefExpr *E) { if (PD->isParameterPack()) return TransformFunctionParmPackRefExpr(E, PD); + if (BindingDecl *BD = dyn_cast<BindingDecl>(D); BD && BD->isParameterPack()) { + BD = cast_or_null<BindingDecl>(TransformDecl(BD->getLocation(), BD)); + if (!BD) + return ExprError(); + if (auto *RP = + dyn_cast_if_present<ResolvedUnexpandedPackExpr>(BD->getBinding())) + return TransformResolvedUnexpandedPackExpr(RP); + } + return inherited::TransformDeclRefExpr(E); } @@ -2637,6 +2651,19 @@ TemplateInstantiator::TransformTemplateTypeParmType(TypeLocBuilder &TLB, return Result; } +ExprResult TemplateInstantiator::TransformResolvedUnexpandedPackExpr( + ResolvedUnexpandedPackExpr *E) { + if (getSema().ArgumentPackSubstitutionIndex != -1) { + assert(static_cast<unsigned>(getSema().ArgumentPackSubstitutionIndex) < + E->getNumExprs() && + "ArgumentPackSubstitutionIndex is out of range"); + return TransformExpr( + E->getExpansion(getSema().ArgumentPackSubstitutionIndex)); + } + + return inherited::TransformResolvedUnexpandedPackExpr(E); +} + QualType TemplateInstantiator::TransformSubstTemplateTypeParmPackType( TypeLocBuilder &TLB, SubstTemplateTypeParmPackTypeLoc TL, bool SuppressObjCLifetime) { diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index f540431..d530ed0 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1168,26 +1168,57 @@ TemplateDeclInstantiator::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) { Decl *TemplateDeclInstantiator::VisitBindingDecl(BindingDecl *D) { auto *NewBD = BindingDecl::Create(SemaRef.Context, Owner, D->getLocation(), - D->getIdentifier()); + D->getIdentifier(), D->getType()); NewBD->setReferenced(D->isReferenced()); SemaRef.CurrentInstantiationScope->InstantiatedLocal(D, NewBD); + return NewBD; } Decl *TemplateDeclInstantiator::VisitDecompositionDecl(DecompositionDecl *D) { // Transform the bindings first. + // The transformed DD will have all of the concrete BindingDecls. SmallVector<BindingDecl*, 16> NewBindings; - for (auto *OldBD : D->bindings()) + ResolvedUnexpandedPackExpr *OldResolvedPack = nullptr; + for (auto *OldBD : D->bindings()) { + Expr *BindingExpr = OldBD->getBinding(); + if (auto *RP = + dyn_cast_if_present<ResolvedUnexpandedPackExpr>(BindingExpr)) { + assert(!OldResolvedPack && "no more than one pack is allowed"); + OldResolvedPack = RP; + } NewBindings.push_back(cast<BindingDecl>(VisitBindingDecl(OldBD))); + } ArrayRef<BindingDecl*> NewBindingArray = NewBindings; - auto *NewDD = cast_or_null<DecompositionDecl>( + auto *NewDD = cast_if_present<DecompositionDecl>( VisitVarDecl(D, /*InstantiatingVarTemplate=*/false, &NewBindingArray)); if (!NewDD || NewDD->isInvalidDecl()) for (auto *NewBD : NewBindings) NewBD->setInvalidDecl(); + if (OldResolvedPack) { + // Mark the holding vars (if any) in the pack as instantiated since + // they are created implicitly. + auto Bindings = NewDD->bindings(); + auto BPack = llvm::find_if( + Bindings, [](BindingDecl *D) -> bool { return D->isParameterPack(); }); + auto *NewResolvedPack = + cast<ResolvedUnexpandedPackExpr>((*BPack)->getBinding()); + auto OldExprs = OldResolvedPack->getExprs(); + auto NewExprs = NewResolvedPack->getExprs(); + assert(OldExprs.size() == NewExprs.size()); + for (unsigned I = 0; I < OldResolvedPack->getNumExprs(); I++) { + DeclRefExpr *OldDRE = cast<DeclRefExpr>(OldExprs[I]); + BindingDecl *OldNestedBD = cast<BindingDecl>(OldDRE->getDecl()); + DeclRefExpr *NewDRE = cast<DeclRefExpr>(NewExprs[I]); + BindingDecl *NewNestedBD = cast<BindingDecl>(NewDRE->getDecl()); + SemaRef.CurrentInstantiationScope->InstantiatedLocal(OldNestedBD, + NewNestedBD); + } + } + return NewDD; } @@ -6240,8 +6271,16 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D, // declarations to their instantiations. if (CurrentInstantiationScope) { if (auto Found = CurrentInstantiationScope->findInstantiationOf(D)) { - if (Decl *FD = Found->dyn_cast<Decl *>()) + if (Decl *FD = Found->dyn_cast<Decl *>()) { + if (auto *BD = dyn_cast<BindingDecl>(FD); + BD && BD->isParameterPack() && + ArgumentPackSubstitutionIndex != -1) { + auto *DRE = cast<DeclRefExpr>( + BD->getBindingPackExprs()[ArgumentPackSubstitutionIndex]); + return cast<NamedDecl>(DRE->getDecl()); + } return cast<NamedDecl>(FD); + } int PackIdx = ArgumentPackSubstitutionIndex; assert(PackIdx != -1 && diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp index c8452db..3c56794 100644 --- a/clang/lib/Sema/SemaTemplateVariadic.cpp +++ b/clang/lib/Sema/SemaTemplateVariadic.cpp @@ -50,17 +50,29 @@ class CollectUnexpandedParameterPacksVisitor auto *FTD = FD ? FD->getDescribedFunctionTemplate() : nullptr; if (FTD && FTD->getTemplateParameters()->getDepth() >= DepthLimit) return; - } else if (getDepthAndIndex(ND).first >= DepthLimit) + } else if (auto *BD = dyn_cast<BindingDecl>(ND)) { + Expr *E = BD->getBinding(); + if (auto *RP = cast_if_present<ResolvedUnexpandedPackExpr>(E)) { + addUnexpanded(RP); + return; + } + } else if (getDepthAndIndex(ND).first >= DepthLimit) { return; + } Unexpanded.push_back({ND, Loc}); } + void addUnexpanded(const TemplateTypeParmType *T, SourceLocation Loc = SourceLocation()) { if (T->getDepth() < DepthLimit) Unexpanded.push_back({T, Loc}); } + void addUnexpanded(ResolvedUnexpandedPackExpr *E) { + Unexpanded.push_back({E, E->getBeginLoc()}); + } + public: explicit CollectUnexpandedParameterPacksVisitor( SmallVectorImpl<UnexpandedParameterPack> &Unexpanded) @@ -103,6 +115,12 @@ class CollectUnexpandedParameterPacksVisitor return true; } + bool + VisitResolvedUnexpandedPackExpr(ResolvedUnexpandedPackExpr *E) override { + addUnexpanded(E); + return true; + } + /// Record occurrences of template template parameter packs. bool TraverseTemplateName(TemplateName Template) override { if (auto *TTP = dyn_cast_or_null<TemplateTemplateParmDecl>( @@ -422,8 +440,8 @@ Sema::DiagnoseUnexpandedParameterPacks(SourceLocation Loc, if (const TemplateTypeParmType *TTP = Unexpanded[I].first.dyn_cast<const TemplateTypeParmType *>()) Name = TTP->getIdentifier(); - else - Name = cast<NamedDecl *>(Unexpanded[I].first)->getIdentifier(); + else if (NamedDecl *ND = Unexpanded[I].first.dyn_cast<NamedDecl *>()) + Name = ND->getIdentifier(); if (Name && NamesKnown.insert(Name).second) Names.push_back(Name); @@ -757,23 +775,39 @@ bool Sema::CheckParameterPacksForExpansion( bool HaveFirstPack = false; std::optional<unsigned> NumPartialExpansions; SourceLocation PartiallySubstitutedPackLoc; + typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack; for (UnexpandedParameterPack ParmPack : Unexpanded) { // Compute the depth and index for this parameter pack. unsigned Depth = 0, Index = 0; IdentifierInfo *Name; bool IsVarDeclPack = false; + ResolvedUnexpandedPackExpr *ResolvedPack = nullptr; if (const TemplateTypeParmType *TTP = ParmPack.first.dyn_cast<const TemplateTypeParmType *>()) { Depth = TTP->getDepth(); Index = TTP->getIndex(); Name = TTP->getIdentifier(); + } else if (auto *RP = + ParmPack.first.dyn_cast<ResolvedUnexpandedPackExpr *>()) { + ResolvedPack = RP; } else { NamedDecl *ND = cast<NamedDecl *>(ParmPack.first); if (isa<VarDecl>(ND)) IsVarDeclPack = true; - else + else if (isa<BindingDecl>(ND)) { + // Find the instantiated BindingDecl and check it for a resolved pack. + llvm::PointerUnion<Decl *, DeclArgumentPack *> *Instantiation = + CurrentInstantiationScope->findInstantiationOf(ND); + Decl *B = cast<Decl *>(*Instantiation); + Expr *BindingExpr = cast<BindingDecl>(B)->getBinding(); + ResolvedPack = cast_if_present<ResolvedUnexpandedPackExpr>(BindingExpr); + if (!ResolvedPack) { + ShouldExpand = false; + continue; + } + } else std::tie(Depth, Index) = getDepthAndIndex(ND); Name = ND->getIdentifier(); @@ -783,8 +817,6 @@ bool Sema::CheckParameterPacksForExpansion( unsigned NewPackSize, PendingPackExpansionSize = 0; if (IsVarDeclPack) { // Figure out whether we're instantiating to an argument pack or not. - typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack; - llvm::PointerUnion<Decl *, DeclArgumentPack *> *Instantiation = CurrentInstantiationScope->findInstantiationOf( cast<NamedDecl *>(ParmPack.first)); @@ -797,6 +829,8 @@ bool Sema::CheckParameterPacksForExpansion( ShouldExpand = false; continue; } + } else if (ResolvedPack) { + NewPackSize = ResolvedPack->getNumExprs(); } else { // If we don't have a template argument at this depth/index, then we // cannot expand the pack expansion. Make a note of this, but we still @@ -833,7 +867,7 @@ bool Sema::CheckParameterPacksForExpansion( // Template argument deduction can extend the sequence of template // arguments corresponding to a template parameter pack, even when the // sequence contains explicitly specified template arguments. - if (!IsVarDeclPack && CurrentInstantiationScope) { + if (!IsVarDeclPack && !ResolvedPack && CurrentInstantiationScope) { if (NamedDecl *PartialPack = CurrentInstantiationScope->getPartiallySubstitutedPack()) { unsigned PartialDepth, PartialIndex; @@ -939,6 +973,12 @@ std::optional<unsigned> Sema::getNumArgumentsInExpansionFromUnexpanded( Unexpanded[I].first.dyn_cast<const TemplateTypeParmType *>()) { Depth = TTP->getDepth(); Index = TTP->getIndex(); + } else if (auto *PE = Unexpanded[I] + .first.dyn_cast<ResolvedUnexpandedPackExpr *>()) { + unsigned Size = PE->getNumExprs(); + assert((!Result || *Result == Size) && "inconsistent pack sizes"); + Result = Size; + continue; } else { NamedDecl *ND = cast<NamedDecl *>(Unexpanded[I].first); if (isa<VarDecl>(ND)) { @@ -1167,8 +1207,12 @@ ExprResult Sema::ActOnSizeofParameterPackExpr(Scope *S, MarkAnyDeclReferenced(OpLoc, ParameterPack, true); + std::optional<unsigned> Length; + if (auto *RP = ResolvedUnexpandedPackExpr::getFromDecl(ParameterPack)) + Length = RP->getNumExprs(); + return SizeOfPackExpr::Create(Context, OpLoc, ParameterPack, NameLoc, - RParenLoc); + RParenLoc, Length); } static bool isParameterPack(Expr *PackExpression) { diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 2a5e354..808b564 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -3680,6 +3680,13 @@ public: FullySubstituted); } + ExprResult RebuildResolvedUnexpandedPackExpr(SourceLocation BeginLoc, + QualType T, + ArrayRef<Expr *> Exprs) { + return ResolvedUnexpandedPackExpr::Create(SemaRef.Context, BeginLoc, T, + Exprs); + } + /// Build a new expression representing a call to a source location /// builtin. /// @@ -15427,12 +15434,11 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) { // The transform has determined that we should perform an expansion; // transform and capture each of the arguments. // expansion of the pattern. Do so. - auto *Pack = cast<VarDecl>(C->getCapturedVar()); + auto *Pack = cast<ValueDecl>(C->getCapturedVar()); for (unsigned I = 0; I != *NumExpansions; ++I) { Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), I); - VarDecl *CapturedVar - = cast_or_null<VarDecl>(getDerived().TransformDecl(C->getLocation(), - Pack)); + ValueDecl *CapturedVar = cast_if_present<ValueDecl>( + getDerived().TransformDecl(C->getLocation(), Pack)); if (!CapturedVar) { Invalid = true; continue; @@ -16127,6 +16133,24 @@ TreeTransform<Derived>::TransformFunctionParmPackExpr(FunctionParmPackExpr *E) { return E; } +template <typename Derived> +ExprResult TreeTransform<Derived>::TransformResolvedUnexpandedPackExpr( + ResolvedUnexpandedPackExpr *E) { + bool ArgumentChanged = false; + SmallVector<Expr *, 12> NewExprs; + if (TransformExprs(E->getExprs().begin(), E->getNumExprs(), + /*IsCall=*/false, NewExprs, &ArgumentChanged)) + return ExprError(); + + if (!AlwaysRebuild() && !ArgumentChanged) + return E; + + // NOTE: The type is just a superficial PackExpansionType + // that needs no substitution. + return RebuildResolvedUnexpandedPackExpr(E->getBeginLoc(), E->getType(), + NewExprs); +} + template<typename Derived> ExprResult TreeTransform<Derived>::TransformMaterializeTemporaryExpr( diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 990235a3..b15eca8 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -2208,6 +2208,16 @@ void ASTStmtReader::VisitPackIndexingExpr(PackIndexingExpr *E) { Exprs[I] = Record.readExpr(); } +void ASTStmtReader::VisitResolvedUnexpandedPackExpr( + ResolvedUnexpandedPackExpr *E) { + VisitExpr(E); + E->NumExprs = Record.readInt(); + E->BeginLoc = readSourceLocation(); + auto **Exprs = E->getTrailingObjects<Expr *>(); + for (unsigned I = 0; I < E->NumExprs; ++I) + Exprs[I] = Record.readExpr(); +} + void ASTStmtReader::VisitSubstNonTypeTemplateParmExpr( SubstNonTypeTemplateParmExpr *E) { VisitExpr(E); @@ -4291,6 +4301,12 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { /*TransformedExprs=*/Record[ASTStmtReader::NumExprFields]); break; + case EXPR_RESOLVED_UNEXPANDED_PACK: + S = ResolvedUnexpandedPackExpr::CreateDeserialized( + Context, + /*NumExprs=*/Record[ASTStmtReader::NumExprFields]); + break; + case EXPR_SUBST_NON_TYPE_TEMPLATE_PARM: S = new (Context) SubstNonTypeTemplateParmExpr(Empty); break; diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 2d0fae8..e81a441 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -874,6 +874,7 @@ static void AddStmtsExprs(llvm::BitstreamWriter &Stream, RECORD(EXPR_PACK_EXPANSION); RECORD(EXPR_SIZEOF_PACK); RECORD(EXPR_PACK_INDEXING); + RECORD(EXPR_RESOLVED_UNEXPANDED_PACK); RECORD(EXPR_SUBST_NON_TYPE_TEMPLATE_PARM); RECORD(EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK); RECORD(EXPR_FUNCTION_PARM_PACK); diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 2daf839..e6701c5 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -2210,6 +2210,16 @@ void ASTStmtWriter::VisitPackIndexingExpr(PackIndexingExpr *E) { Code = serialization::EXPR_PACK_INDEXING; } +void ASTStmtWriter::VisitResolvedUnexpandedPackExpr( + ResolvedUnexpandedPackExpr *E) { + VisitExpr(E); + Record.push_back(E->getNumExprs()); + Record.AddSourceLocation(E->getBeginLoc()); + for (Expr *Sub : E->getExprs()) + Record.AddStmt(Sub); + Code = serialization::EXPR_RESOLVED_UNEXPANDED_PACK; +} + void ASTStmtWriter::VisitSubstNonTypeTemplateParmExpr( SubstNonTypeTemplateParmExpr *E) { VisitExpr(E); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 140c7779..2b1872f 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1743,6 +1743,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::DependentCoawaitExprClass: case Stmt::CoreturnStmtClass: case Stmt::CoyieldExprClass: + case Stmt::ResolvedUnexpandedPackExprClass: case Stmt::SEHTryStmtClass: case Stmt::SEHExceptStmtClass: case Stmt::SEHLeaveStmtClass: |