Author: Yeoul Na Date: 2026-09-17T09:47:32-07:00 New Revision: f873bf351c19c711451a80df6bea7c76f17727be
URL: https://github.com/llvm/llvm-project/commit/f873bf351c19c711451a80df6bea7c76f17727be DIFF: https://github.com/llvm/llvm-project/commit/f873bf351c19c711451a80df6bea7c76f17727be.diff LOG: [BoundsSafety][NFC] Allow CountAttributedType's count to be filled in later (#223267) Prepare CountAttributedType so its count expression can be supplied after the node is created, which the new late-parsed counted_by mechanism needs: the type is built when the attribute is seen, but its argument isn't parsed until the enclosing record is complete. - Drop the TrailingObjects coupled-decl storage in favour of an ASTContext-allocated array held by the ArrayRef the base class already has, so the decls can be attached after construction. - Add CountAttributedType::setCountExpr for in-place completion. - Add ASTContext::getIncompleteCountAttributedType (count-less, not uniqued) and completeCountAttributedType. Incomplete nodes are not registered in ASTContext.Types until completed, so a node abandoned with a null count is never reachable. No functional change: getCountAttributedType still builds a fully-formed, count-carrying type as before. This is an adjusted direction from the previous attempt to create a place holder type (LateParsedAttrType) and rebuild it once late parsing is done (https://github.com/llvm/llvm-project/pull/212906) based on the feedback from @cor3ntin https://github.com/llvm/llvm-project/pull/212906#issuecomment-5532012528. Creating a CountAttributedType in the first pass and filling in the count expression in the second pass has a benefit that the types (including the enclosing types) won't need to be reconstructed, allowing to drop TreeTransform. Added: Modified: clang/include/clang/AST/ASTContext.h clang/include/clang/AST/TypeBase.h clang/lib/AST/ASTContext.cpp clang/lib/AST/Type.cpp clang/lib/Sema/SemaBoundsSafety.cpp Removed: ################################################################################ diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 2f7d39599c477..b2d407e412b3d 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -1681,6 +1681,22 @@ class ASTContext : public RefCountedBase<ASTContext> { bool OrNull, ArrayRef<TypeCoupledDeclRefInfo> DependentDecls) const; + /// Return a `CountAttributedType` whose count expression has not been parsed + /// yet, for use by a late-parsed bounds attribute. The result is *not* + /// uniqued, and must be completed with `completeCountAttributedType` once the + /// argument becomes parseable. Returns the node rather than a `QualType` so + /// the caller can retain it for completion. + CountAttributedType *getIncompleteCountAttributedType(QualType WrappedTy, + bool CountInBytes, + bool OrNull) const; + + /// Supply the count expression and coupled declarations for a type created by + /// `getIncompleteCountAttributedType`. Enclosing types keep pointing at the + /// same node, so nothing above it needs rebuilding. + void completeCountAttributedType( + CountAttributedType *CATy, Expr *CountExpr, + ArrayRef<TypeCoupledDeclRefInfo> DependentDecls) const; + /// Return a placeholder type for a late-parsed type attribute. /// This type wraps another type and holds the LateParsedAttribute /// that will be parsed later. diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h index 424a2afee84da..28f102fdaf534 100644 --- a/clang/include/clang/AST/TypeBase.h +++ b/clang/include/clang/AST/TypeBase.h @@ -3456,6 +3456,13 @@ class BoundsAttributedType : public Type, public llvm::FoldingSetNode { BoundsAttributedType(TypeClass TC, QualType Wrapped, QualType Canon); public: + enum BoundsAttrKind { + CountedBy = 0, + SizedBy, + CountedByOrNull, + SizedByOrNull, + }; + bool isSugared() const { return true; } QualType desugar() const { return WrappedTy; } @@ -3492,10 +3499,7 @@ class BoundsAttributedType : public Type, public llvm::FoldingSetNode { /// Represents a sugar type with `__counted_by` or `__sized_by` annotations, /// including their `_or_null` variants. -class CountAttributedType final - : public BoundsAttributedType, - public llvm::TrailingObjects<CountAttributedType, - TypeCoupledDeclRefInfo> { +class CountAttributedType final : public BoundsAttributedType { friend class ASTContext; Expr *CountExpr; @@ -3505,27 +3509,36 @@ class CountAttributedType final /// __counted_by_or_null or __sized_by_or_null) \p CoupledDecls contains the /// list of declarations referenced by \p CountExpr, which the type depends on /// for the bounds information. + /// + /// \p CountExpr may be null, and \p CoupledDecls empty, for a type created by + /// a late-parsed attribute whose argument has not been parsed yet; such a + /// type is completed by \c complete once the enclosing scope is known. See + /// \c Parser::CompleteLateParsedTypeAttributes. CountAttributedType(QualType Wrapped, QualType Canon, Expr *CountExpr, bool CountInBytes, bool OrNull, ArrayRef<TypeCoupledDeclRefInfo> CoupledDecls); - unsigned numTrailingObjects(OverloadToken<TypeCoupledDeclRefInfo>) const { - return CountAttributedTypeBits.NumCoupledDecls; - } + /// Allocate and construct a \c CountAttributedType in \p Ctx, including its + /// coupled-declaration array. \p CountExpr may be null (with \p CoupledDecls + /// empty) for a late-parsed attribute whose argument is not yet parsed; + /// complete such a node later with \c complete. + static CountAttributedType * + Create(const ASTContext &Ctx, QualType Wrapped, QualType Canon, + Expr *CountExpr, bool CountInBytes, bool OrNull, + ArrayRef<TypeCoupledDeclRefInfo> CoupledDecls); + + /// Supply the count expression and coupled declarations for a node created by + /// \c Create with a null count -- a late-parsed attribute whose argument has + /// now been parsed. Allocates the decl array in \p Ctx, so the node owns it. + void complete(const ASTContext &Ctx, Expr *E, + ArrayRef<TypeCoupledDeclRefInfo> CoupledDecls); public: - enum DynamicCountPointerKind { - CountedBy = 0, - SizedBy, - CountedByOrNull, - SizedByOrNull, - }; - Expr *getCountExpr() const { return CountExpr; } bool isCountInBytes() const { return CountAttributedTypeBits.CountInBytes; } bool isOrNull() const { return CountAttributedTypeBits.OrNull; } - DynamicCountPointerKind getKind() const { + BoundsAttrKind getKind() const { if (isOrNull()) return isCountInBytes() ? SizedByOrNull : CountedByOrNull; return isCountInBytes() ? SizedBy : CountedBy; diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 650a3206e3d48..ee8663e6906af 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -3758,7 +3758,13 @@ QualType ASTContext::getCountAttributedType( QualType WrappedTy, Expr *CountExpr, bool CountInBytes, bool OrNull, ArrayRef<TypeCoupledDeclRefInfo> DependentDecls) const { assert(WrappedTy->isPointerType() || WrappedTy->isArrayType()); + assert(CountExpr && "use getIncompleteCountAttributedType for a null count"); + // Complete (non-late-parsed) path: the count expression is known up front. + // This deliberately preserves the pre-existing uniquing behavior -- the + // FoldingSet lookup/insert below is unchanged by late-parse support. Only + // getIncompleteCountAttributedType (count filled in later) opts out of + // uniquing. llvm::FoldingSetNodeID ID; CountAttributedType::Profile(ID, WrappedTy, CountExpr, CountInBytes, OrNull); @@ -3768,17 +3774,53 @@ QualType ASTContext::getCountAttributedType( return QualType(CATy, 0); QualType CanonTy = getCanonicalType(WrappedTy); - size_t Size = CountAttributedType::totalSizeToAlloc<TypeCoupledDeclRefInfo>( - DependentDecls.size()); - CATy = (CountAttributedType *)Allocate(Size, TypeAlignment); - new (CATy) CountAttributedType(WrappedTy, CanonTy, CountExpr, CountInBytes, - OrNull, DependentDecls); + CATy = CountAttributedType::Create(*this, WrappedTy, CanonTy, CountExpr, + CountInBytes, OrNull, DependentDecls); Types.push_back(CATy); CountAttributedTypes.insert(CATy, Token); return QualType(CATy, 0); } +CountAttributedType *ASTContext::getIncompleteCountAttributedType( + QualType WrappedTy, bool CountInBytes, bool OrNull) const { + assert(WrappedTy->isPointerType() || WrappedTy->isArrayType()); + + // Deliberately opts out of the uniquing that `getCountAttributedType` does: + // `CountAttributedType::Profile` keys on the `CountExpr` pointer, which is + // null here, so every incomplete node would profile identically as + // `(WrappedTy, flags, nullptr)` and two fields with diff erent counts would + // collide. The node stays un-uniqued even after completion; see + // `completeCountAttributedType`. + // + // Also deliberately not in `Types` yet. An incomplete node can be abandoned + // without ever being completed (a nested counted_by, or an argument that + // fails to parse), and a null-count node must not be reachable by anything + // that scans `Types`. `completeCountAttributedType` registers it once the + // count is in place. + return CountAttributedType::Create( + *this, WrappedTy, getCanonicalType(WrappedTy), + /*CountExpr=*/nullptr, CountInBytes, OrNull, + /*CoupledDecls=*/{}); +} + +void ASTContext::completeCountAttributedType( + CountAttributedType *CATy, Expr *CountExpr, + ArrayRef<TypeCoupledDeclRefInfo> DependentDecls) const { + CATy->complete(*this, CountExpr, DependentDecls); + // Safe for `Types` scanners now that the count is in place; see + // `getIncompleteCountAttributedType` for why it was held back. + // + // It stays out of the `CountAttributedTypes` FoldingSet permanently, unlike + // an eagerly built node: this pointer is already embedded in the enclosing + // types and handed out, so an equal node that happens to exist cannot be + // merged into. The only cost is that a completed node is never + // pointer-shared with an equal eager one, which does not affect semantic + // type equality -- `hasSameType` compares canonical types, and this sugar's + // canonical type is the wrapped type's. + Types.push_back(CATy); +} + QualType ASTContext::getLateParsedAttrType( QualType WrappedTy, LateParsedTypeAttribute *LateParsedAttr) const { QualType CanonTy = getCanonicalType(WrappedTy); diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index cbacca1c44d91..98901502cf37a 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -4178,9 +4178,45 @@ CountAttributedType::CountAttributedType( CountAttributedTypeBits.NumCoupledDecls = CoupledDecls.size(); CountAttributedTypeBits.CountInBytes = CountInBytes; CountAttributedTypeBits.OrNull = OrNull; - auto *DeclSlot = getTrailingObjects(); - llvm::copy(CoupledDecls, DeclSlot); - Decls = llvm::ArrayRef(DeclSlot, CoupledDecls.size()); + // `CoupledDecls` is already allocated by the caller (Create), so it + // can be retained by reference. This lets a type created by a late-parsed + // attribute start out with no decls and gain them later via `complete`, + // which a trailing-object array could not accommodate. + Decls = CoupledDecls; +} + +/// Copy \p Decls into \p Ctx so a \c CountAttributedType can retain it by +/// reference. The node owns this allocation rather than its callers, so both +/// \c Create and \c complete route through here. +static ArrayRef<TypeCoupledDeclRefInfo> +allocateCoupledDecls(const ASTContext &Ctx, + ArrayRef<TypeCoupledDeclRefInfo> Decls) { + if (Decls.empty()) + return {}; + auto *Slots = Ctx.Allocate<TypeCoupledDeclRefInfo>(Decls.size()); + llvm::copy(Decls, Slots); + return ArrayRef(Slots, Decls.size()); +} + +CountAttributedType * +CountAttributedType::Create(const ASTContext &Ctx, QualType Wrapped, + QualType Canon, Expr *CountExpr, bool CountInBytes, + bool OrNull, + ArrayRef<TypeCoupledDeclRefInfo> CoupledDecls) { + ArrayRef<TypeCoupledDeclRefInfo> Decls = + allocateCoupledDecls(Ctx, CoupledDecls); + return new (Ctx, alignof(CountAttributedType)) CountAttributedType( + Wrapped, Canon, CountExpr, CountInBytes, OrNull, Decls); +} + +void CountAttributedType::complete( + const ASTContext &Ctx, Expr *E, + ArrayRef<TypeCoupledDeclRefInfo> CoupledDecls) { + assert(!CountExpr && "count expression is already set"); + assert(E && "completing with a null count expression"); + CountExpr = E; + Decls = allocateCoupledDecls(Ctx, CoupledDecls); + CountAttributedTypeBits.NumCoupledDecls = Decls.size(); } StringRef CountAttributedType::getAttributeName(bool WithMacroPrefix) const { diff --git a/clang/lib/Sema/SemaBoundsSafety.cpp b/clang/lib/Sema/SemaBoundsSafety.cpp index 066dab2f0bef2..75041c801b6ff 100644 --- a/clang/lib/Sema/SemaBoundsSafety.cpp +++ b/clang/lib/Sema/SemaBoundsSafety.cpp @@ -17,8 +17,8 @@ namespace clang { -static CountAttributedType::DynamicCountPointerKind -getCountAttrKind(bool CountInBytes, bool OrNull) { +static CountAttributedType::BoundsAttrKind getCountAttrKind(bool CountInBytes, + bool OrNull) { if (CountInBytes) return OrNull ? CountAttributedType::SizedByOrNull : CountAttributedType::SizedBy; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
