Skip to content

Turn 'counted_by' into a type attribute and parse it into 'CountAttributedType' #78000

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Mar 20, 2024

Conversation

rapidsna
Copy link
Contributor

In -fbounds-safety, bounds annotations are considered type attributes rather than declaration attributes. Constructing them as type attributes allows us to extend the attribute to apply nested pointers, which is essential to annotate functions that involve out parameters: void foo(int *__counted_by(*out_count) *out_buf, int *out_count).

We introduce a new sugar type to support bounds annotated types, CountAttributedType. In order to maintain extra data (the bounds expression and the dependent declaration information) that is not trackable in AttributedType we create a new type dedicate to this functionality.

This patch also extends the parsing logic to parse the counted_by argument as an expression, which will allow us to extend the model to support arguments beyond an identifier, e.g., __counted_by(n + m) in the future as specified by -fbounds-safety.

This also adjusts __bdos and array-bounds sanitizer code that already uses CountedByAttr to check CountAttributedType instead to get the field referred to by the attribute.

@rapidsna rapidsna added clang Clang issues not falling into any other category clang:bounds-safety Issue/PR relating to the experimental -fbounds-safety feature in Clang labels Jan 13, 2024
Copy link

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be
notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write
permissions for the repository. In which case you can instead tag reviewers by
name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review
by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate
is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot
Copy link
Member

llvmbot commented Jan 13, 2024

@llvm/pr-subscribers-clang-modules
@llvm/pr-subscribers-clang-codegen
@llvm/pr-subscribers-debuginfo

@llvm/pr-subscribers-clang

Author: Yeoul Na (rapidsna)

Changes

In -fbounds-safety, bounds annotations are considered type attributes rather than declaration attributes. Constructing them as type attributes allows us to extend the attribute to apply nested pointers, which is essential to annotate functions that involve out parameters: void foo(int *__counted_by(*out_count) *out_buf, int *out_count).

We introduce a new sugar type to support bounds annotated types, CountAttributedType. In order to maintain extra data (the bounds expression and the dependent declaration information) that is not trackable in AttributedType we create a new type dedicate to this functionality.

This patch also extends the parsing logic to parse the counted_by argument as an expression, which will allow us to extend the model to support arguments beyond an identifier, e.g., __counted_by(n + m) in the future as specified by -fbounds-safety.

This also adjusts __bdos and array-bounds sanitizer code that already uses CountedByAttr to check CountAttributedType instead to get the field referred to by the attribute.


Patch is 50.71 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/78000.diff

33 Files Affected:

  • (modified) clang/include/clang/AST/ASTContext.h (+7)
  • (modified) clang/include/clang/AST/PropertiesBase.td (+1)
  • (modified) clang/include/clang/AST/RecursiveASTVisitor.h (+9)
  • (modified) clang/include/clang/AST/Type.h (+152)
  • (modified) clang/include/clang/AST/TypeLoc.h (+26)
  • (modified) clang/include/clang/AST/TypeProperties.td (+19)
  • (modified) clang/include/clang/Basic/Attr.td (+4-3)
  • (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+10-4)
  • (modified) clang/include/clang/Basic/TypeNodes.td (+2)
  • (modified) clang/include/clang/Parse/Parser.h (+7)
  • (modified) clang/include/clang/Sema/Sema.h (+9-3)
  • (modified) clang/include/clang/Serialization/ASTRecordReader.h (+2)
  • (modified) clang/include/clang/Serialization/ASTRecordWriter.h (+5)
  • (modified) clang/include/clang/Serialization/TypeBitCodes.def (+1)
  • (modified) clang/lib/AST/ASTContext.cpp (+56)
  • (modified) clang/lib/AST/ASTImporter.cpp (+12)
  • (modified) clang/lib/AST/ASTStructuralEquivalence.cpp (+7)
  • (modified) clang/lib/AST/ItaniumMangle.cpp (+1)
  • (modified) clang/lib/AST/Type.cpp (+65)
  • (modified) clang/lib/AST/TypeLoc.cpp (+4)
  • (modified) clang/lib/AST/TypePrinter.cpp (+23)
  • (modified) clang/lib/CodeGen/CGDebugInfo.cpp (+1)
  • (modified) clang/lib/CodeGen/CGExpr.cpp (+8-27)
  • (modified) clang/lib/CodeGen/CodeGenFunction.cpp (+1)
  • (modified) clang/lib/Parse/ParseDecl.cpp (+50)
  • (modified) clang/lib/Sema/SemaDecl.cpp (-6)
  • (modified) clang/lib/Sema/SemaDeclAttr.cpp (+71-102)
  • (modified) clang/lib/Sema/SemaExpr.cpp (+21-2)
  • (modified) clang/lib/Sema/SemaType.cpp (+11)
  • (modified) clang/lib/Sema/TreeTransform.h (+7)
  • (modified) clang/lib/Serialization/ASTReader.cpp (+8)
  • (modified) clang/lib/Serialization/ASTWriter.cpp (+4)
  • (modified) clang/tools/libclang/CIndex.cpp (+4)
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 3e46a5da3fc043..c8354fbb108a27 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -247,6 +247,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
       DependentBitIntTypes;
   llvm::FoldingSet<BTFTagAttributedType> BTFTagAttributedTypes;
 
+  mutable llvm::FoldingSet<CountAttributedType> CountAttributedTypes;
+
   mutable llvm::FoldingSet<QualifiedTemplateName> QualifiedTemplateNames;
   mutable llvm::FoldingSet<DependentTemplateName> DependentTemplateNames;
   mutable llvm::FoldingSet<SubstTemplateTemplateParmStorage>
@@ -1338,6 +1340,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
     return CanQualType::CreateUnsafe(getPointerType((QualType) T));
   }
 
+  QualType
+  getCountAttributedType(QualType T, Expr *CountExpr, bool CountInBytes,
+                         bool OrNull,
+                         ArrayRef<TypeCoupledDeclRefInfo> DependentDecls) const;
+
   /// Return the uniqued reference to a type adjusted from the original
   /// type to a new type.
   QualType getAdjustedType(QualType Orig, QualType New) const;
diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td
index d86c4eba6a2251..25ddfd105ab877 100644
--- a/clang/include/clang/AST/PropertiesBase.td
+++ b/clang/include/clang/AST/PropertiesBase.td
@@ -143,6 +143,7 @@ def UInt32 : CountPropertyType<"uint32_t">;
 def UInt64 : CountPropertyType<"uint64_t">;
 def UnaryTypeTransformKind : EnumPropertyType<"UnaryTransformType::UTTKind">;
 def VectorKind : EnumPropertyType<"VectorKind">;
+def TypeCoupledDeclRefInfo : PropertyType;
 
 def ExceptionSpecInfo : PropertyType<"FunctionProtoType::ExceptionSpecInfo"> {
   let BufferElementTypes = [ QualType ];
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 8f2714e142bbe3..b57ab36939d07c 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1099,6 +1099,12 @@ DEF_TRAVERSE_TYPE(InjectedClassNameType, {})
 DEF_TRAVERSE_TYPE(AttributedType,
                   { TRY_TO(TraverseType(T->getModifiedType())); })
 
+DEF_TRAVERSE_TYPE(CountAttributedType, {
+  if (T->getCountExpr())
+    TRY_TO(TraverseStmt(T->getCountExpr()));
+  TRY_TO(TraverseType(T->desugar()));
+})
+
 DEF_TRAVERSE_TYPE(BTFTagAttributedType,
                   { TRY_TO(TraverseType(T->getWrappedType())); })
 
@@ -1385,6 +1391,9 @@ DEF_TRAVERSE_TYPELOC(MacroQualifiedType,
 DEF_TRAVERSE_TYPELOC(AttributedType,
                      { TRY_TO(TraverseTypeLoc(TL.getModifiedLoc())); })
 
+DEF_TRAVERSE_TYPELOC(CountAttributedType,
+                     { TRY_TO(TraverseTypeLoc(TL.getInnerLoc())); })
+
 DEF_TRAVERSE_TYPELOC(BTFTagAttributedType,
                      { TRY_TO(TraverseTypeLoc(TL.getWrappedLoc())); })
 
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index d4e5310fb3abc6..489644ca5acf86 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -61,6 +61,7 @@ class BTFTypeTagAttr;
 class ExtQuals;
 class QualType;
 class ConceptDecl;
+class ValueDecl;
 class TagDecl;
 class TemplateParameterList;
 class Type;
@@ -2000,6 +2001,21 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
     unsigned NumExpansions;
   };
 
+  class CountAttributedTypeBitfields {
+    friend class CountAttributedType;
+
+    LLVM_PREFERRED_TYPE(TypeBitfields)
+    unsigned : NumTypeBits;
+
+    /// The limit is 15.
+    static constexpr unsigned NumCoupledDeclsBits = 4;
+    unsigned NumCoupledDecls : NumCoupledDeclsBits;
+    LLVM_PREFERRED_TYPE(bool)
+    unsigned CountInBytes : 1;
+    LLVM_PREFERRED_TYPE(bool)
+    unsigned OrNull : 1;
+  };
+
   union {
     TypeBitfields TypeBits;
     ArrayTypeBitfields ArrayTypeBits;
@@ -2022,6 +2038,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
     DependentTemplateSpecializationTypeBitfields
       DependentTemplateSpecializationTypeBits;
     PackExpansionTypeBitfields PackExpansionTypeBits;
+    CountAttributedTypeBitfields CountAttributedTypeBits;
   };
 
 private:
@@ -2719,6 +2736,14 @@ template <> const TemplateSpecializationType *Type::getAs() const;
 /// until it reaches an AttributedType or a non-sugared type.
 template <> const AttributedType *Type::getAs() const;
 
+/// This will check for a BoundsAttributedType by removing any existing
+/// sugar until it reaches an BoundsAttributedType or a non-sugared type.
+template <> const BoundsAttributedType *Type::getAs() const;
+
+/// This will check for a CountAttributedType by removing any existing
+/// sugar until it reaches an CountAttributedType or a non-sugared type.
+template <> const CountAttributedType *Type::getAs() const;
+
 // We can do canonical leaf types faster, because we don't have to
 // worry about preserving child type decoration.
 #define TYPE(Class, Base)
@@ -2917,6 +2942,133 @@ class PointerType : public Type, public llvm::FoldingSetNode {
   static bool classof(const Type *T) { return T->getTypeClass() == Pointer; }
 };
 
+/// [BoundsSafety] Represents information of declarations referenced by the
+/// arguments of the `counted_by` attribute and the likes.
+class TypeCoupledDeclRefInfo {
+public:
+  using BaseTy = llvm::PointerIntPair<ValueDecl *, 1, unsigned>;
+
+private:
+  enum {
+    DerefShift = 0,
+    DerefMask = 1,
+  };
+  BaseTy Data;
+
+public:
+  /// \p D is to a declaration referenced by the argument of attribute. \p Deref
+  /// indicates whether \p D is referenced as a dereferenced form, e.g., \p
+  /// Deref is true for `*n` in `int *__counted_by(*n)`.
+  TypeCoupledDeclRefInfo(ValueDecl *D = nullptr, bool Deref = false);
+
+  bool isDeref() const;
+  ValueDecl *getDecl() const;
+  unsigned getInt() const;
+  void *getOpaqueValue() const;
+  bool operator==(const TypeCoupledDeclRefInfo &Other) const;
+  void setFromOpaqueValue(void *V);
+};
+
+/// [BoundsSafety] Represents a parent type class for CountAttributedType and
+/// similar sugar types that will be introduced to represent a type with a
+/// bounds attribute.
+///
+/// Provides a common interface to navigate declarations referred to by the
+/// bounds expression.
+
+class BoundsAttributedType : public Type, public llvm::FoldingSetNode {
+  QualType WrappedTy;
+
+protected:
+  ArrayRef<TypeCoupledDeclRefInfo> Decls; // stored in trailing objects
+
+  BoundsAttributedType(TypeClass TC, QualType Wrapped, QualType Canon);
+
+public:
+  bool isSugared() const { return true; }
+  QualType desugar() const { return WrappedTy; }
+
+  using decl_iterator = const TypeCoupledDeclRefInfo *;
+  using decl_range = llvm::iterator_range<decl_iterator>;
+
+  decl_iterator dependent_decl_begin() const { return Decls.begin(); }
+  decl_iterator dependent_decl_end() const { return Decls.end(); }
+
+  unsigned getNumCoupledDecls() const { return Decls.size(); }
+
+  decl_range dependent_decls() const {
+    return decl_range(dependent_decl_begin(), dependent_decl_end());
+  }
+
+  ArrayRef<TypeCoupledDeclRefInfo> getCoupledDecls() const {
+    return {dependent_decl_begin(), dependent_decl_end()};
+  }
+
+  bool referencesFieldDecls() const;
+
+  static bool classof(const Type *T) {
+    switch (T->getTypeClass()) {
+    case CountAttributed:
+      return true;
+    default:
+      return false;
+    }
+  }
+};
+
+/// 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> {
+  friend class ASTContext;
+
+  Expr *CountExpr;
+  /// \p CountExpr represents the argument of __counted_by or the likes. \p
+  /// CountInBytes indicates that \p CountExpr is a byte count (i.e.,
+  /// __sized_by(_or_null)) \p OrNull means it's an or_null variant (i.e.,
+  /// __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.
+  CountAttributedType(QualType Wrapped, QualType Canon, Expr *CountExpr,
+                      bool CountInBytes, bool OrNull,
+                      ArrayRef<TypeCoupledDeclRefInfo> CoupledDecls);
+
+  unsigned numTrailingObjects(OverloadToken<TypeCoupledDeclRefInfo>) const {
+    return CountAttributedTypeBits.NumCoupledDecls;
+  }
+
+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 {
+    if (isOrNull())
+      return isCountInBytes() ? SizedByOrNull : CountedByOrNull;
+    return isCountInBytes() ? SizedBy : CountedBy;
+  }
+
+  void Profile(llvm::FoldingSetNodeID &ID) {
+    Profile(ID, desugar(), CountExpr, isCountInBytes(), isOrNull());
+  }
+
+  static void Profile(llvm::FoldingSetNodeID &ID, QualType WrappedTy,
+                      Expr *CountExpr, bool CountInBytes, bool Nullable);
+
+  static bool classof(const Type *T) {
+    return T->getTypeClass() == CountAttributed;
+  }
+};
+
 /// Represents a type which was implicitly adjusted by the semantic
 /// engine for arbitrary reasons.  For example, array and function types can
 /// decay, and function types can have their calling conventions adjusted.
diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h
index 471deb14aba51f..fa3cd46ffd3fc0 100644
--- a/clang/include/clang/AST/TypeLoc.h
+++ b/clang/include/clang/AST/TypeLoc.h
@@ -1110,6 +1110,32 @@ class ObjCInterfaceTypeLoc : public ConcreteTypeLoc<ObjCObjectTypeLoc,
   }
 };
 
+struct BoundsAttributedLocInfo {};
+class BoundsAttributedTypeLoc
+    : public ConcreteTypeLoc<UnqualTypeLoc, BoundsAttributedTypeLoc,
+                             BoundsAttributedType, BoundsAttributedLocInfo> {
+public:
+  TypeLoc getInnerLoc() const { return this->getInnerTypeLoc(); }
+  QualType getInnerType() const { return this->getTypePtr()->desugar(); }
+  void initializeLocal(ASTContext &Context, SourceLocation Loc) {
+    // nothing to do
+  }
+  unsigned getLocalDataAlignment() const { return 1; }
+  unsigned getLocalDataSize() const { return 0; }
+};
+
+class CountAttributedTypeLoc final
+    : public InheritingConcreteTypeLoc<BoundsAttributedTypeLoc,
+                                       CountAttributedTypeLoc,
+                                       CountAttributedType> {
+public:
+  Expr *getCountExpr() const { return getTypePtr()->getCountExpr(); }
+  bool isCountInBytes() const { return getTypePtr()->isCountInBytes(); }
+  bool isOrNull() const { return getTypePtr()->isOrNull(); }
+
+  SourceRange getLocalSourceRange() const;
+};
+
 struct MacroQualifiedLocInfo {
   SourceLocation ExpansionLoc;
 };
diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td
index 682c869b0c5847..8697903d8d7f6e 100644
--- a/clang/include/clang/AST/TypeProperties.td
+++ b/clang/include/clang/AST/TypeProperties.td
@@ -25,6 +25,25 @@ let Class = PointerType in {
   def : Creator<[{ return ctx.getPointerType(pointeeType); }]>;
 }
 
+let Class = CountAttributedType in {
+  def : Property<"WrappedTy", QualType> {
+    let Read = [{ node->desugar() }];
+  }
+  def : Property<"CountExpr", ExprRef> {
+    let Read = [{ node->getCountExpr() }];
+  }
+  def : Property<"CountInBytes", Bool> {
+    let Read = [{ node->isCountInBytes() }];
+  }
+  def : Property<"OrNull", Bool> {
+    let Read = [{ node->isOrNull() }];
+  }
+  def : Property<"CoupledDecls", Array<TypeCoupledDeclRefInfo>> {
+    let Read = [{ node->getCoupledDecls() }];
+  }
+  def : Creator<[{ return ctx.getCountAttributedType(WrappedTy, CountExpr, CountInBytes, OrNull, CoupledDecls); }]>;
+}
+
 let Class = AdjustedType in {
   def : Property<"originalType", QualType> {
     let Read = [{ node->getOriginalType() }];
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index a03b0e44e15f7d..af2680be0da906 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4373,10 +4373,11 @@ def CodeAlign: StmtAttr {
   }];
 }
 
-def CountedBy : InheritableAttr {
+def CountedBy : DeclOrTypeAttr {
   let Spellings = [Clang<"counted_by">];
-  let Subjects = SubjectList<[Field]>;
-  let Args = [IdentifierArgument<"CountedByField">];
+  let Subjects = SubjectList<[Field], ErrorDiag>;
+  let Args = [ExprArgument<"Count">, IntArgument<"NestedLevel">];
+  let ParseArgumentsAsUnevaluated = 1;
   let Documentation = [CountedByDocs];
   let LangOpts = [COnly];
   // FIXME: This is ugly. Let using a DeclArgument would be nice, but a Decl
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 1a79892e40030a..ae8b90964c3fc9 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -6445,12 +6445,18 @@ def err_flexible_array_count_not_in_same_struct : Error<
   "'counted_by' field %0 isn't within the same struct as the flexible array">;
 def err_counted_by_attr_not_on_flexible_array_member : Error<
   "'counted_by' only applies to C99 flexible array members">;
-def err_counted_by_attr_refers_to_flexible_array : Error<
-  "'counted_by' cannot refer to the flexible array %0">;
+def err_counted_by_attr_refer_to_itself : Error<
+  "'counted_by' cannot refer to the flexible array member %0">;
 def err_counted_by_must_be_in_structure : Error<
   "field %0 in 'counted_by' not inside structure">;
-def err_flexible_array_counted_by_attr_field_not_integer : Error<
-  "field %0 in 'counted_by' must be a non-boolean integer type">;
+def err_counted_by_attr_argument_not_integer : Error<
+  "'counted_by' requires a non-boolean integer type argument">;
+def err_counted_by_attr_only_support_simple_decl_reference : Error<
+  "'counted_by' argument must be a simple declaration reference">;
+def err_counted_by_attr_in_union : Error<
+  "'counted_by' cannot be applied to a union member">;
+def err_counted_by_attr_refer_to_union : Error<
+  "'counted_by' argument cannot refer to a union member">;
 def note_flexible_array_counted_by_attr_field : Note<
   "field %0 declared here">;
 
diff --git a/clang/include/clang/Basic/TypeNodes.td b/clang/include/clang/Basic/TypeNodes.td
index 649b071cebb940..da733716fa1f66 100644
--- a/clang/include/clang/Basic/TypeNodes.td
+++ b/clang/include/clang/Basic/TypeNodes.td
@@ -107,6 +107,8 @@ def ObjCTypeParamType : TypeNode<Type>, NeverCanonical;
 def ObjCObjectType : TypeNode<Type>;
 def ObjCInterfaceType : TypeNode<ObjCObjectType>, LeafType;
 def ObjCObjectPointerType : TypeNode<Type>;
+def BoundsAttributedType : TypeNode<Type, 1>;
+def CountAttributedType : TypeNode<BoundsAttributedType>, NeverCanonical;
 def PipeType : TypeNode<Type>;
 def AtomicType : TypeNode<Type>;
 def BitIntType : TypeNode<Type>;
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index e50a4d05b45991..f88191ef5f52c2 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -3054,6 +3054,13 @@ class Parser : public CodeCompletionHandler {
                                  SourceLocation ScopeLoc,
                                  ParsedAttr::Form Form);
 
+  void ParseBoundsAttribute(IdentifierInfo &AttrName,
+                            SourceLocation AttrNameLoc,
+                            ParsedAttributes &Attrs,
+                            IdentifierInfo *ScopeName,
+                            SourceLocation ScopeLoc,
+                            ParsedAttr::Form Form);
+
   void ParseTypeofSpecifier(DeclSpec &DS);
   SourceLocation ParseDecltypeSpecifier(DeclSpec &DS);
   void AnnotateExistingDecltypeSpecifier(const DeclSpec &DS,
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index cf2d4fbe6d3ba1..027afb5d703d1d 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1341,7 +1341,7 @@ class Sema final {
     /// \brief Describes whether we are in an expression constext which we have
     /// to handle differently.
     enum ExpressionKind {
-      EK_Decltype, EK_TemplateArgument, EK_Other
+      EK_Decltype, EK_TemplateArgument, EK_BoundsAttrArgument, EK_Other
     } ExprContext;
 
     // A context can be nested in both a discarded statement context and
@@ -1436,6 +1436,11 @@ class Sema final {
   std::tuple<MangleNumberingContext *, Decl *>
   getCurrentMangleNumberContext(const DeclContext *DC);
 
+  bool isBoundsAttrArgument() const {
+    return ExprEvalContexts.back().ExprContext == 
+        ExpressionEvaluationContextRecord::ExpressionKind::EK_BoundsAttrArgument; 
+  }
+
 
   /// SpecialMemberOverloadResult - The overloading result for a special member
   /// function.
@@ -2612,6 +2617,9 @@ class Sema final {
   QualType BuiltinChangeSignedness(QualType BaseType, UTTKind UKind,
                                    SourceLocation Loc);
 
+  QualType BuildCountAttributedArrayType(QualType WrappedTy, Expr *CountExpr,
+                              const llvm::SmallVector<TypeCoupledDeclRefInfo, 1> &Decls);
+
   //===--------------------------------------------------------------------===//
   // Symbol table / Decl tracking callbacks: SemaDecl.cpp.
   //
@@ -4799,8 +4807,6 @@ class Sema final {
   bool CheckAlwaysInlineAttr(const Stmt *OrigSt, const Stmt *CurSt,
                              const AttributeCommonInfo &A);
 
-  bool CheckCountedByAttr(Scope *Scope, const FieldDecl *FD);
-
   /// Adjust the calling convention of a method to be the ABI default if it
   /// wasn't specified explicitly.  This handles method types formed from
   /// function type typedefs and typename template arguments.
diff --git a/clang/include/clang/Serialization/ASTRecordReader.h b/clang/include/clang/Serialization/ASTRecordReader.h
index 80a1359fd16aa0..5d3e95cb5d630f 100644
--- a/clang/include/clang/Serialization/ASTRecordReader.h
+++ b/clang/include/clang/Serialization/ASTRecordReader.h
@@ -221,6 +221,8 @@ class ASTRecordReader
     return Reader->ReadSelector(*F, Record, Idx);
   }
 
+  TypeCoupledDeclRefInfo readTypeCoupledDeclRefInfo();
+
   /// Read a declaration name, advancing Idx.
   // DeclarationName readDeclarationName(); (inherited)
   DeclarationNameLoc readDeclarationNameLoc(DeclarationName Name);
diff --git a/clang/include/clang/Serialization/ASTRecordWriter.h b/clang/include/clang/Serialization/ASTRecordWriter.h
index 9a64735c9fa55d..e007d4a70843a1 100644
--- a/clang/include/clang/Serialization/ASTRecordWriter.h
+++ b/clang/include/clang/Serialization/ASTRecordWriter.h
@@ -141,6 +141,11 @@ class ASTRecordWriter
     AddSourceLocation(Loc);
   }
 
+  void writeTypeCoupledDeclRefInfo(TypeCoupledDeclRefInfo Info) {
+    writeDeclRef(Info.getDecl());
+    writeBool(Info.isDeref());
+  }
+
   /// Emit a source range.
   void AddSourceRange(SourceRange Range, LocSeq *Seq = nullptr) {
     return Writer->AddSourceRange(Range, *Record, Seq);
diff --git a/clang/include/clang/Serialization/TypeBitCodes.def b/clang/include/clang/Serialization/TypeBitCodes.def
index 89ae1a2fa39546..c04dacdaa15820 100644
--- a/clang/include/clang/Serialization/TypeBitCodes.def
+++ b/clang/include/clang/Serialization/TypeBitCodes.def
@@ -64,5 +64,6 @@ TYPE_BIT_CODE(ConstantMatrix, CONSTANT_MATRIX, 52)
 TYPE_BIT_CODE(DependentSizedMatrix, DEPENDENT_SIZE_MATRIX, 53)
 TYPE_BIT_CODE(Using, USING, 54)
 TYPE_BIT_CODE(BTFTagAttributed, BTFTAG_ATTRIBUTED, 55)
+TYPE_BIT_CODE(CountAttributed, COUNT_ATTRIBUTED, 56)
 
 #undef TYPE_BIT_CODE
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index d9cefcaa84d7e5..43d2da0e60f367 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -2341,6 +2341,9 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const {
     return getTypeInfo(
                   cast<AttributedType>(T)->getEquivalentType().getTypePtr());
 
+  case Type::CountAttributed:
+    return getTypeInfo(cast<CountAttributedType>(T)->des...
[truncated]

Copy link

github-actions bot commented Jan 13, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@rapidsna rapidsna changed the title [WIP] Turn 'counted_by' into a type attribute and parse it into 'CountAttributedType' Turn 'counted_by' into a type attribute and parse it into 'CountAttributedType' Jan 30, 2024
@rapidsna rapidsna self-assigned this Jan 30, 2024
@rapidsna rapidsna marked this pull request as ready for review February 1, 2024 18:04
@rapidsna rapidsna force-pushed the bounds-safety/counted_by_type branch from d501646 to 5fd456d Compare February 1, 2024 19:11
@rapidsna rapidsna added the clang:frontend Language frontend issues, e.g. anything involving "Sema" label Feb 1, 2024
@rapidsna rapidsna marked this pull request as draft February 1, 2024 20:10
@rapidsna rapidsna force-pushed the bounds-safety/counted_by_type branch from 2ada26d to c39871e Compare February 1, 2024 20:10
@rapidsna rapidsna marked this pull request as ready for review February 1, 2024 20:10
@llvmbot llvmbot added clang:modules C++20 modules and Clang Header Modules clang:codegen IR generation bugs: mangling, exceptions, etc. debuginfo labels Feb 1, 2024
@rapidsna
Copy link
Contributor Author

rapidsna commented Feb 2, 2024

@AaronBallman @bwendling I'd appreciate your feedback!

@efriedma-quic
Copy link
Collaborator

It's generally not a good idea to use sugar to represent constructs that are semantically significant: anything that uses the canonical type will throw it away. We try to avoid throwing away sugar in most cases, but we aren't always successful.

It's possible there are other considerations here (maybe it matters less if the attribute is only relevant inside the definition?), but I'd like an explanation of the tradeoffs.

@rapidsna
Copy link
Contributor Author

rapidsna commented Feb 6, 2024

It's generally not a good idea to use sugar to represent constructs that are semantically significant: anything that uses the canonical type will throw it away. We try to avoid throwing away sugar in most cases, but we aren't always successful.

It's possible there are other considerations here (maybe it matters less if the attribute is only relevant inside the definition?), but I'd like an explanation of the tradeoffs.

Thanks @efriedma-quic! Yes, these are really good points.

The main reason that we decided it to be a sugar type was that this type of attribute doesn't change the shape of the underlying type so we wanted such as isa<PointerType>(BoundAttributedTy) or isa<ArrayType>(BoundAttributedTy) still holds true depending on the underlying canonical type (in the current implementation the attribute only applies to array type but it will be extended to support pointer type as well). And we figured that adding a sugar type might less disruptive to the rest of Clang code base compared to extending the existing Clang types.

But as you pointed out we encountered cases where the sugar wasn't preserved as it needed to be so we had to specially handle such cases in our own implementation, so we're open for other suggestions.

An alternative design could be to create new canonical types that inherit IncompleteArrayType or PointerType respectively so isa still holds, but unfortunately IncompleteArrayType is final.

We could also add an additional field in the IncompleteArrayType class to contain the optional count expression, then the question is what we want to do for ConstantArrayType later when we like to have it count attribute as well (this could be potentially relevant for flexible array member-like array member with a constant size).

Similarly, we could add more fields to PointerType to contain the optional count expression (or upper bound expression) and necessary data. For this, we should be careful not to grow the size of PointerType by default, possibly by adding additional data as TrailingObject.

We haven't experimented these alternative designs so I'm curious about @AaronBallman's thoughts.

@rapidsna rapidsna requested a review from rjmccall February 6, 2024 00:33
Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No additional comments from me, if the other reviewers are happy, I'm happy with where this is/is going.

@rapidsna rapidsna requested a review from Endilll as a code owner March 7, 2024 01:18
@rapidsna
Copy link
Contributor Author

rapidsna commented Mar 7, 2024

@AaronBallman thanks for the review! I addressed your comments.

@Endilll
Copy link
Contributor

Endilll commented Mar 7, 2024

Can you resolve merge conflicts, so that I can review changes to Sema.h?

rapidsna added 9 commits March 7, 2024 09:46
CountAttributedType is a sugar type to represent a type with
a 'counted_by' attribute and the likes, which provides bounds
information to the underlying type. The type contains an
the argument of attribute as an expression. Additionally, the type
holds metadata about declarations referenced by the expression in
order to make it easier for Sema to access declarations on which
the type depends.

This also adjusts the CountedBy attribute definition and implements
parsing CountAttributedType.

__bdos and array-checks sanitizer use CountAttributedType instead
of hasAttr<CountedByAttr>.

Implements special lookup for counted_by argument in structs.

Adjust test/Sema/attr-counted-by.c to match the default diags
generated by the expression parser.
@rapidsna rapidsna force-pushed the bounds-safety/counted_by_type branch from a92e090 to 8f18416 Compare March 7, 2024 18:05
@rapidsna
Copy link
Contributor Author

rapidsna commented Mar 7, 2024

@Endilll I just rebased it so you can review Sema.h.

Copy link
Contributor

@Endilll Endilll left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sema.h changes look good to me.

Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this, it's exciting to see more bounds safety being added to Clang!

I think everything looks good to me (we can clean up any surprises in follow-ups at this point). I'm presuming you'll add a release note & docs once more of the functionality is place?

@rapidsna
Copy link
Contributor Author

rapidsna commented Mar 14, 2024

@AaronBallman Thank you! The -fbounds-safety documentation is currently shown in https://clang.llvm.org/docs/BoundsSafety.html, but yes, we're planning to add the feature in the release note once we have more functionalities in place.

@rapidsna rapidsna merged commit 3eb9ff3 into llvm:main Mar 20, 2024
@nathanchance
Copy link
Member

nathanchance commented Mar 20, 2024

I see a crash from an unreachable statement while building the Linux kernel with debug info enabled after this change. A trivial reproducer from cvise:

struct {
  int num_counters;
  long value[] __attribute__((__counted_by__(num_counters)));
} agent_send_response_port_num;
$ clang -g -c -o /dev/null agent.i
type should have been unwrapped!
UNREACHABLE executed at /mnt/nvme/tmp/cvise.DVoC71ctTu/src/clang/lib/CodeGen/CGDebugInfo.cpp:3681!
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace, preprocessed source, and associated run script.
Stack dump:
0.	Program arguments: clang -g -c -o /dev/null agent.i
1.	<eof> parser at end of file
 #0 0x0000564a42c9f028 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin/clang-19+0x534c028)
 #1 0x0000564a42c9cc6e llvm::sys::RunSignalHandlers() (/home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin/clang-19+0x5349c6e)
 #2 0x0000564a42c1f526 CrashRecoverySignalHandler(int) CrashRecoveryContext.cpp:0:0
 #3 0x00007f91646c5770 (/usr/lib/libc.so.6+0x3c770)
 #4 0x00007f916471632c (/usr/lib/libc.so.6+0x8d32c)
 #5 0x00007f91646c56c8 raise (/usr/lib/libc.so.6+0x3c6c8)
 #6 0x00007f91646ad4b8 abort (/usr/lib/libc.so.6+0x244b8)
 #7 0x0000564a42c2522f (/home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin/clang-19+0x52d222f)
 #8 0x0000564a42f3ccf1 (/home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin/clang-19+0x55e9cf1)
 #9 0x0000564a42f2aeab clang::CodeGen::CGDebugInfo::getOrCreateType(clang::QualType, llvm::DIFile*) (/home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin/clang-19+0x55d7eab)
#10 0x0000564a42f31090 clang::CodeGen::CGDebugInfo::createFieldType(llvm::StringRef, clang::QualType, clang::SourceLocation, clang::AccessSpecifier, unsigned long, unsigned int, llvm::DIFile*, llvm::DIScope*, clang::RecordDecl const*, llvm::MDTupleTypedArrayWrapper<llvm::DINode>) (/home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin/clang-19+0x55de090)
#11 0x0000564a42f31dd2 clang::CodeGen::CGDebugInfo::CollectRecordNormalField(clang::FieldDecl const*, unsigned long, llvm::DIFile*, llvm::SmallVectorImpl<llvm::Metadata*>&, llvm::DIType*, clang::RecordDecl const*) (/home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin/clang-19+0x55dedd2)
#12 0x0000564a42f322f8 clang::CodeGen::CGDebugInfo::CollectRecordFields(clang::RecordDecl const*, llvm::DIFile*, llvm::SmallVectorImpl<llvm::Metadata*>&, llvm::DICompositeType*) (/home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin/clang-19+0x55df2f8)
#13 0x0000564a42f3765d clang::CodeGen::CGDebugInfo::CreateTypeDefinition(clang::RecordType const*) (/home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin/clang-19+0x55e465d)
#14 0x0000564a42f37b3d clang::CodeGen::CGDebugInfo::CreateType(clang::RecordType const*) (/home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin/clang-19+0x55e4b3d)
#15 0x0000564a42f3ca03 clang::CodeGen::CGDebugInfo::CreateTypeNode(clang::QualType, llvm::DIFile*) (/home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin/clang-19+0x55e9a03)
#16 0x0000564a42f2aeab clang::CodeGen::CGDebugInfo::getOrCreateType(clang::QualType, llvm::DIFile*) (/home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin/clang-19+0x55d7eab)
#17 0x0000564a42f475b4 clang::CodeGen::CGDebugInfo::EmitGlobalVariable(llvm::GlobalVariable*, clang::VarDecl const*) (/home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin/clang-19+0x55f45b4)
#18 0x0000564a42ea15fd clang::CodeGen::CodeGenModule::EmitGlobalVarDefinition(clang::VarDecl const*, bool) (/home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin/clang-19+0x554e5fd)
#19 0x0000564a42ea2f30 clang::CodeGen::CodeGenModule::EmitTentativeDefinition(clang::VarDecl const*) (/home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin/clang-19+0x554ff30)
#20 0x0000564a44895bd8 clang::Sema::ActOnEndOfTranslationUnit() (/home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin/clang-19+0x6f42bd8)
#21 0x0000564a4474bf8e clang::Parser::ParseTopLevelDecl(clang::OpaquePtr<clang::DeclGroupRef>&, clang::Sema::ModuleImportState&) (/home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin/clang-19+0x6df8f8e)
#22 0x0000564a447468be clang::ParseAST(clang::Sema&, bool, bool) (/home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin/clang-19+0x6df38be)
#23 0x0000564a438624af clang::FrontendAction::Execute() (/home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin/clang-19+0x5f0f4af)
#24 0x0000564a437d6efd clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) (/home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin/clang-19+0x5e83efd)
#25 0x0000564a43929a58 clang::ExecuteCompilerInvocation(clang::CompilerInstance*) (/home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin/clang-19+0x5fd6a58)
#26 0x0000564a40e6688f cc1_main(llvm::ArrayRef<char const*>, char const*, void*) (/home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin/clang-19+0x351388f)
#27 0x0000564a40e63019 ExecuteCC1Tool(llvm::SmallVectorImpl<char const*>&, llvm::ToolContext const&) driver.cpp:0:0
#28 0x0000564a4365bd49 void llvm::function_ref<void ()>::callback_fn<clang::driver::CC1Command::Execute(llvm::ArrayRef<std::optional<llvm::StringRef>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>*, bool*) const::$_0>(long) Job.cpp:0:0
#29 0x0000564a42c1f2a6 llvm::CrashRecoveryContext::RunSafely(llvm::function_ref<void ()>) (/home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin/clang-19+0x52cc2a6)
#30 0x0000564a4365b442 clang::driver::CC1Command::Execute(llvm::ArrayRef<std::optional<llvm::StringRef>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>*, bool*) const (/home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin/clang-19+0x5d08442)
#31 0x0000564a43619fb3 clang::driver::Compilation::ExecuteCommand(clang::driver::Command const&, clang::driver::Command const*&, bool) const (/home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin/clang-19+0x5cc6fb3)
#32 0x0000564a4361a267 clang::driver::Compilation::ExecuteJobs(clang::driver::JobList const&, llvm::SmallVectorImpl<std::pair<int, clang::driver::Command const*>>&, bool) const (/home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin/clang-19+0x5cc7267)
#33 0x0000564a43637229 clang::driver::Driver::ExecuteCompilation(clang::driver::Compilation&, llvm::SmallVectorImpl<std::pair<int, clang::driver::Command const*>>&) (/home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin/clang-19+0x5ce4229)
#34 0x0000564a40e626c9 clang_main(int, char**, llvm::ToolContext const&) (/home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin/clang-19+0x350f6c9)
#35 0x0000564a40e722b7 main (/home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin/clang-19+0x351f2b7)
#36 0x00007f91646aecd0 (/usr/lib/libc.so.6+0x25cd0)
#37 0x00007f91646aed8a __libc_start_main (/usr/lib/libc.so.6+0x25d8a)
#38 0x0000564a40e60a25 _start (/home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin/clang-19+0x350da25)
clang: error: clang frontend command failed with exit code 134 (use -v to see invocation)
ClangBuiltLinux clang version 19.0.0git (https://github.com/llvm/llvm-project 3eb9ff30959a670559bcba03d149d4c51bf7c9c9)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /home/nathan/Dev/tmp/cvise.DVoC71ctTu/install/llvm-bad/bin
clang: note: diagnostic msg: Error generating preprocessed source(s) - no preprocessable inputs.
# bad: [75dfa58ea93aa93b97534906778cb3dd24ba841a] [RemoveDIs][NFC] Rename DPMarker->DbgMarker (#85931)
# good: [e2fa90fa0a4b7950dd0d7fae6933e89c075d0af0] [lldb/API] Add missing `eBroadcastBitSymbolsChanged` to SBTarget (NFC) (#85883)
git bisect start '75dfa58ea93aa93b97534906778cb3dd24ba841a' 'e2fa90fa0a4b7950dd0d7fae6933e89c075d0af0'
# bad: [197f3ecf92b91ad1626307a17edf0761f2e4136b] [flang][OpenMP] lower simple array reductions (#84958)
git bisect bad 197f3ecf92b91ad1626307a17edf0761f2e4136b
# bad: [27df1b23e07009b1450ebb2072abac97e2958b07] [SLPVectorizer] Use TargetFolder (#85800)
git bisect bad 27df1b23e07009b1450ebb2072abac97e2958b07
# good: [f375aff594d97fc9f6cf2cffb502882833c15cdd] [RISCV] Add the CSR names from Smrnmi. (#83370)
git bisect good f375aff594d97fc9f6cf2cffb502882833c15cdd
# bad: [d7e28cd82bd3141093f96f7ce2e7b36f1b115fad] MIPS: Support -m(no-)unaligned-access for r6 (#85174)
git bisect bad d7e28cd82bd3141093f96f7ce2e7b36f1b115fad
# bad: [4a026b5092d77426b70ab299447af4dbd5a012d9] [AMDGCN] Use ZExt when handling indices in insertment element  (#85718)
git bisect bad 4a026b5092d77426b70ab299447af4dbd5a012d9
# bad: [3eb9ff30959a670559bcba03d149d4c51bf7c9c9] Turn 'counted_by' into a type attribute and parse it into 'CountAttributedType' (#78000)
git bisect bad 3eb9ff30959a670559bcba03d149d4c51bf7c9c9
# good: [b2082a98175b0e5356f23bf21d3dc5b76edea390] Revert "[clang-format][NFC] Delete 100+ redundant #include lines in .cpp files"
git bisect good b2082a98175b0e5356f23bf21d3dc5b76edea390
# first bad commit: [3eb9ff30959a670559bcba03d149d4c51bf7c9c9] Turn 'counted_by' into a type attribute and parse it into 'CountAttributedType' (#78000)

cc @kees @bwendling

@delcypher
Copy link
Contributor

@nathanchance Thanks for reporting this. I'm going to have a quick go at reproducing this to see if fixing this is straight forward. If it is not I will revert this PR and then we can re-land this change with the problem you reported fixed.

@rapidsna
Copy link
Contributor Author

Thanks @nathanchance. Opened PR to fix the crash. #86017

chencha3 pushed a commit to chencha3/llvm-project that referenced this pull request Mar 23, 2024
…butedType' (llvm#78000)

In `-fbounds-safety`, bounds annotations are considered type attributes
rather than declaration attributes. Constructing them as type attributes
allows us to extend the attribute to apply nested pointers, which is
essential to annotate functions that involve out parameters: `void
foo(int *__counted_by(*out_count) *out_buf, int *out_count)`.

We introduce a new sugar type to support bounds annotated types,
`CountAttributedType`. In order to maintain extra data (the bounds
expression and the dependent declaration information) that is not
trackable in `AttributedType` we create a new type dedicate to this
functionality.

This patch also extends the parsing logic to parse the `counted_by`
argument as an expression, which will allow us to extend the model to
support arguments beyond an identifier, e.g., `__counted_by(n + m)` in
the future as specified by `-fbounds-safety`.

This also adjusts `__bdos` and array-bounds sanitizer code that already
uses `CountedByAttr` to check `CountAttributedType` instead to get the
field referred to by the attribute.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:bounds-safety Issue/PR relating to the experimental -fbounds-safety feature in Clang clang:codegen IR generation bugs: mangling, exceptions, etc. clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:modules C++20 modules and Clang Header Modules clang Clang issues not falling into any other category debuginfo
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants