llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Yeoul Na (rapidsna) <details> <summary>Changes</summary> 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. --- Full diff: https://github.com/llvm/llvm-project/pull/223267.diff 5 Files Affected: - (modified) clang/include/clang/AST/ASTContext.h (+16) - (modified) clang/include/clang/AST/TypeBase.h (+25-14) - (modified) clang/lib/AST/ASTContext.cpp (+52-5) - (modified) clang/lib/AST/Type.cpp (+5-3) - (modified) clang/lib/Sema/SemaBoundsSafety.cpp (+2-2) ``````````diff diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index f875ae365b892..4fef0d57fd1e4 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -1663,6 +1663,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 cdbd20b62bac5..afcb0172623df 100644 --- a/clang/include/clang/AST/TypeBase.h +++ b/clang/include/clang/AST/TypeBase.h @@ -3474,6 +3474,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; } @@ -3510,10 +3517,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; @@ -3523,27 +3527,34 @@ 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 setCountExpr 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; + /// Supply the count expression and its coupled declarations for a type that + /// was created without them by a late-parsed attribute. \p CoupledDecls must + /// already be allocated in the \c ASTContext, since it is retained by + /// reference. + void setCountExpr(Expr *E, ArrayRef<TypeCoupledDeclRefInfo> CoupledDecls) { + assert(!CountExpr && "count expression is already set"); + assert(E && "completing with a null count expression"); + CountExpr = E; + Decls = CoupledDecls; + CountAttributedTypeBits.NumCoupledDecls = CoupledDecls.size(); } 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 84455fb6396bd..c58ad654e0050 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -3754,11 +3754,29 @@ QualType ASTContext::removePtrSizeAddrSpace(QualType T) const { return T; } +/// Allocate \p Decls in this context so a CountAttributedType can retain it by +/// reference. +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()); +} + 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 +3786,46 @@ 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); + ArrayRef<TypeCoupledDeclRefInfo> Decls = + allocateCoupledDecls(*this, DependentDecls); + CATy = new (*this, alignof(CountAttributedType)) CountAttributedType( + WrappedTy, CanonTy, CountExpr, CountInBytes, OrNull, Decls); 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 not uniqued. `CountAttributedType::Profile` keys on the + // `CountExpr` pointer, so every incomplete node would hash identically as + // `(WrappedTy, flags, nullptr)` and two fields with different counts would + // share a node. This is fine because expressions are not shared anyway. + // `getVariableArrayType` declines to unique for the same + // underlying reason: expressions themselves are not uniqued. + // + // Not added to `Types` yet: an incomplete node whose position turns out to be + // invalid (a nested counted_by, or a rejected argument) is abandoned without + // completion, and a node with a null count must never be reachable by + // anything that iterates `Types`. It is registered in + // `completeCountAttributedType` instead. + return new (*this, alignof(CountAttributedType)) CountAttributedType( + WrappedTy, getCanonicalType(WrappedTy), /*CountExpr=*/nullptr, + CountInBytes, OrNull, /*CoupledDecls=*/{}); +} + +void ASTContext::completeCountAttributedType( + CountAttributedType *CATy, Expr *CountExpr, + ArrayRef<TypeCoupledDeclRefInfo> DependentDecls) const { + ArrayRef<TypeCoupledDeclRefInfo> Decls = + allocateCoupledDecls(*this, DependentDecls); + CATy->setCountExpr(CountExpr, Decls); + 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 4b539b7c2b1f6..7254835e47390 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -4158,9 +4158,11 @@ 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 allocated in the ASTContext by the caller, 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 `setCountExpr`, which a + // trailing-object array could not accommodate. + Decls = CoupledDecls; } 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; `````````` </details> https://github.com/llvm/llvm-project/pull/223267 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
