https://github.com/MaskRay updated https://github.com/llvm/llvm-project/pull/220168
>From 1f777e49c3e3423f158cf5f9a876fb9f9762c43e Mon Sep 17 00:00:00 2001 From: Fangrui Song <[email protected]> Date: Mon, 31 Aug 2026 23:42:33 -0700 Subject: [PATCH 1/2] [clang] Fix FoldingSet lookups that disagree with the node's Profile Some clang code calls FoldingSetImpl::insert with a node whose profile doesn't match its insert token, leading to missing hash-consing. * getObjCTypeParamType keys on Decl->getUnderlyingType() where Profile() reports getCanonicalTypeInternal(). Canonicalize before the lookup: the decl's underlying type can be updated later. * getUnaryTransformType drops UnderlyingType for a dependent base after building the ID. * getFunctionTypeInternal keys canonically where Profile() passes isCanonicalUnqualified(), which differs for a computed noexcept. * getConstantArrayType keys on the full index qualifiers; only the three CVR bits are stored. * LoanManager keys a PlaceholderBase on the bare decl pointer where Profile() reports the PointerUnion's opaque value. These issues would be caught by #220166 Aided by Opus 5 --- .../Analysis/Analyses/LifetimeSafety/Loans.h | 6 +++- clang/lib/AST/ASTContext.cpp | 31 ++++++++++++------- clang/lib/AST/Type.cpp | 4 ++- clang/lib/Analysis/LifetimeSafety/Loans.cpp | 4 +-- 4 files changed, 30 insertions(+), 15 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h index 8137a207290d7..784968789d40a 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h @@ -89,9 +89,13 @@ class PlaceholderBase : public llvm::FoldingSetNode { return ParamOrMethod.dyn_cast<const CXXMethodDecl *>(); } - void Profile(llvm::FoldingSetNodeID &ID) const { + using KeyTy = llvm::PointerUnion<const ParmVarDecl *, const CXXMethodDecl *>; + + static void Profile(llvm::FoldingSetNodeID &ID, KeyTy ParamOrMethod) { ID.AddPointer(ParamOrMethod.getOpaqueValue()); } + + void Profile(llvm::FoldingSetNodeID &ID) const { Profile(ID, ParamOrMethod); } }; /// Represents the storage location being borrowed, e.g., a specific stack diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index b7e771595e86e..819e2527a26d5 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -4287,6 +4287,10 @@ QualType ASTContext::getConstantArrayType(QualType EltTy, llvm::APInt ArySize(ArySizeIn); ArySize = ArySize.zextOrTrunc(Target->getMaxPointerWidth()); + // The type stores only the CVR bits of the index qualifiers, so key on + // those. + IndexTypeQuals &= Qualifiers::CVRMask; + llvm::FoldingSetNodeID ID; ConstantArrayType::Profile(ID, *this, EltTy, ArySize.getZExtValue(), SizeExpr, ASM, IndexTypeQuals); @@ -5701,8 +5705,8 @@ UnresolvedUsingType *ASTContext::getUnresolvedUsingTypeInternal( auto *T = new (Mem) UnresolvedUsingType(Keyword, Qualifier, D, CanonicalType); if (Token) { auto *Placeholder = new (T->getFoldingSetPlaceholder()) - FoldingSetPlaceholder<TypedefType>(); - TypedefTypes.insert(Placeholder, Token); + FoldingSetPlaceholder<UnresolvedUsingType>(); + UnresolvedUsingTypes.insert(Placeholder, Token); } Types.push_back(T); return T; @@ -6534,13 +6538,6 @@ ASTContext::applyObjCProtocolQualifiers(QualType type, QualType ASTContext::getObjCTypeParamType(const ObjCTypeParamDecl *Decl, ArrayRef<ObjCProtocolDecl *> protocols) const { - // Look in the folding set for an existing type. - llvm::FoldingSetNodeID ID; - ObjCTypeParamType::Profile(ID, Decl, Decl->getUnderlyingType(), protocols); - llvm::FoldingSetInsertToken Token; - if (ObjCTypeParamType *TypeParam = ObjCTypeParamTypes.lookup(ID, Token)) - return QualType(TypeParam, 0); - // We canonicalize to the underlying type. QualType Canonical = getCanonicalType(Decl->getUnderlyingType()); if (!protocols.empty()) { @@ -6551,6 +6548,14 @@ ASTContext::getObjCTypeParamType(const ObjCTypeParamDecl *Decl, assert(!hasError && "Error when apply protocol qualifier to bound type"); } + // Key on the canonical type the node is constructed with, which is what + // Profile() reports; the decl's underlying type can be updated later. + llvm::FoldingSetNodeID ID; + ObjCTypeParamType::Profile(ID, Decl, Canonical, protocols); + llvm::FoldingSetInsertToken Token; + if (ObjCTypeParamType *TypeParam = ObjCTypeParamTypes.lookup(ID, Token)) + return QualType(TypeParam, 0); + unsigned size = sizeof(ObjCTypeParamType); size += protocols.size() * sizeof(ObjCProtocolDecl *); void *mem = Allocate(size, alignof(ObjCTypeParamType)); @@ -6837,6 +6842,12 @@ QualType ASTContext::getPackIndexingType(QualType Pattern, Expr *IndexExpr, QualType ASTContext::getUnaryTransformType(QualType BaseType, QualType UnderlyingType, UnaryTransformType::UTTKind Kind) const { + // Clear UnderlyingType for a dependent base before building the ID: that is + // what the node is constructed with, and what Profile() reports. + if (BaseType->isDependentType()) { + assert(UnderlyingType.isNull() || BaseType == UnderlyingType); + UnderlyingType = QualType(); + } llvm::FoldingSetNodeID ID; UnaryTransformType::Profile(ID, BaseType, UnderlyingType, Kind); @@ -6849,8 +6860,6 @@ ASTContext::getUnaryTransformType(QualType BaseType, QualType UnderlyingType, if (!BaseType->isDependentType()) { CanonType = UnderlyingType.getCanonicalType(); } else { - assert(UnderlyingType.isNull() || BaseType == UnderlyingType); - UnderlyingType = QualType(); if (QualType CanonBase = BaseType.getCanonicalType(); BaseType != CanonBase) { CanonType = getUnaryTransformType(CanonBase, QualType(), Kind); diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index df296eb4e28e5..f1f8d79fba957 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -4115,8 +4115,10 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result, void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Ctx) { + // getFunctionTypeInternal keys every node canonically and compares noexcept + // expressions after the lookup, so report that same key. Profile(ID, getReturnType(), param_type_begin(), getNumParams(), - getExtProtoInfo(), Ctx, isCanonicalUnqualified()); + getExtProtoInfo(), Ctx, /*Canonical=*/true); } TypeCoupledDeclRefInfo::TypeCoupledDeclRefInfo(ValueDecl *D, bool Deref) diff --git a/clang/lib/Analysis/LifetimeSafety/Loans.cpp b/clang/lib/Analysis/LifetimeSafety/Loans.cpp index 3a850ca8a9b74..e6cbd922d20c7 100644 --- a/clang/lib/Analysis/LifetimeSafety/Loans.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Loans.cpp @@ -38,7 +38,7 @@ void Loan::dump(llvm::raw_ostream &OS) const { const PlaceholderBase * LoanManager::getOrCreatePlaceholderBase(const ParmVarDecl *PVD) { llvm::FoldingSetNodeID ID; - ID.AddPointer(PVD); + PlaceholderBase::Profile(ID, PVD); llvm::FoldingSetInsertToken InsertToken; if (PlaceholderBase *Existing = PlaceholderBases.lookup(ID, InsertToken)) return Existing; @@ -52,7 +52,7 @@ LoanManager::getOrCreatePlaceholderBase(const ParmVarDecl *PVD) { const PlaceholderBase * LoanManager::getOrCreatePlaceholderBase(const CXXMethodDecl *MD) { llvm::FoldingSetNodeID ID; - ID.AddPointer(MD); + PlaceholderBase::Profile(ID, MD); llvm::FoldingSetInsertToken InsertToken; if (PlaceholderBase *Existing = PlaceholderBases.lookup(ID, InsertToken)) return Existing; >From 6c01976709f0d063f91053362b99fd6bd2eaf1e8 Mon Sep 17 00:00:00 2001 From: Fangrui Song <[email protected]> Date: Wed, 2 Sep 2026 22:57:25 -0700 Subject: [PATCH 2/2] simplify FunctionProtoType::Profile; remove `bool Canonical` --- clang/include/clang/AST/TypeBase.h | 3 +-- clang/lib/AST/ASTContext.cpp | 2 +- clang/lib/AST/Type.cpp | 10 +++++----- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h index e69173a1fdd6c..cdbd20b62bac5 100644 --- a/clang/include/clang/AST/TypeBase.h +++ b/clang/include/clang/AST/TypeBase.h @@ -6011,8 +6011,7 @@ class FunctionProtoType final void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Ctx); static void Profile(llvm::FoldingSetNodeID &ID, QualType Result, param_type_iterator ArgTys, unsigned NumArgs, - const ExtProtoInfo &EPI, const ASTContext &Context, - bool Canonical); + const ExtProtoInfo &EPI, const ASTContext &Context); }; /// The elaboration keyword that precedes a qualified type name or diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 819e2527a26d5..f1f51a7976321 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -5070,7 +5070,7 @@ QualType ASTContext::getFunctionTypeInternal( // structure. llvm::FoldingSetNodeID ID; FunctionProtoType::Profile(ID, ResultTy, ArgArray.begin(), NumArgs, EPI, - *this, true); + *this); QualType Canonical; bool Unique = false; diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index f1f8d79fba957..4b539b7c2b1f6 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -4047,7 +4047,7 @@ bool FunctionProtoType::isTemplateVariadic() const { void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result, const QualType *ArgTys, unsigned NumParams, const ExtProtoInfo &epi, - const ASTContext &Context, bool Canonical) { + const ASTContext &Context) { // We have to be careful not to get ambiguous profile encodings. // Note that valid type pointers are never ambiguous with anything else. // @@ -4086,7 +4086,9 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result, for (QualType Ex : epi.ExceptionSpec.Exceptions) ID.AddPointer(Ex.getAsOpaquePtr()); } else if (isComputedNoexcept(epi.ExceptionSpec.Type)) { - epi.ExceptionSpec.NoexceptExpr->Profile(ID, Context, Canonical); + // getFunctionTypeInternal compares noexcept expressions after the lookup, + // so the key only needs their canonical form. + epi.ExceptionSpec.NoexceptExpr->Profile(ID, Context, /*Canonical=*/true); } else if (epi.ExceptionSpec.Type == EST_Uninstantiated || epi.ExceptionSpec.Type == EST_Unevaluated) { ID.AddPointer(epi.ExceptionSpec.SourceDecl->getCanonicalDecl()); @@ -4115,10 +4117,8 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result, void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Ctx) { - // getFunctionTypeInternal keys every node canonically and compares noexcept - // expressions after the lookup, so report that same key. Profile(ID, getReturnType(), param_type_begin(), getNumParams(), - getExtProtoInfo(), Ctx, /*Canonical=*/true); + getExtProtoInfo(), Ctx); } TypeCoupledDeclRefInfo::TypeCoupledDeclRefInfo(ValueDecl *D, bool Deref) _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
