================
@@ -49,6 +77,169 @@ enum class CountedByInvalidPointeeTypeKind {
   VALID,
 };
 
+/// Check type conflicts under the -fbounds-safety attribute model.
+/// Return std::nullopt if validation should continue with the shared checks.
+static std::optional<bool> checkBoundsAttrTypeConflictsAndMisc(
+    Sema &S, QualType Ty, SourceLocation AttrLoc,
+    const Sema::BoundsAttrFlags &Flags, StringRef AttrSpelling,
+    bool AllowRedecl, Expr *AttrArg) {
+  // The -fbounds-safety model's checks for conflicting attributes, atomic
+  // pointers and array shapes are not upstream yet.
+  return std::nullopt;
+}
+
+bool Sema::ValidateBoundsAttrTypeShape(QualType Ty, SourceLocation AttrLoc,
+                                       SourceRange AttrRange,
+                                       BoundsAttrFlags &Flags,
+                                       StringRef AttrSpelling, bool 
AllowRedecl,
+                                       Expr *AttrArg, bool UpdateFlags) {
+  if (getLangOpts().hasBoundsSafetyAttributes())
+    if (std::optional<bool> Result = checkBoundsAttrTypeConflictsAndMisc(
+            *this, Ty, AttrLoc, Flags, AttrSpelling, AllowRedecl, AttrArg))
+      return *Result;
+
+  BoundsAttributedType::BoundsAttrKind Kind = getBoundsAttrKind(Flags);
+
+  // counted_by and sized_by require a pointer or array.
+  if (!Ty->isPointerType() && !Ty->isArrayType()) {
+    Diag(AttrLoc, diag::err_count_attr_not_on_ptr_or_flexible_array_member)
+        << Kind << 0;
+    return false;
+  }
+
+  // Arrays only support counted_by outside the -fbounds-safety attribute 
model.
+  if (!getLangOpts().hasBoundsSafetyAttributes() && Ty->isArrayType() &&
+      (Flags.CountInBytes || Flags.OrNull)) {
+    Diag(AttrLoc, diag::err_count_attr_not_on_ptr_or_flexible_array_member)
+        << Kind << /*suggest counted_by*/ 1;
+    return false;
+  }
+
+  // Check the pointee or element type.
+  QualType PointeeTy;
+  int SelectPtrOrArr;
+  if (Ty->isPointerType()) {
+    PointeeTy = Ty->getPointeeType();
+    SelectPtrOrArr = 0;
+  } else {
+    const ArrayType *AT = getASTContext().getAsArrayType(Ty);
+    PointeeTy = AT->getElementType();
+    SelectPtrOrArr = 1;
+  }
+
+  auto InvalidTypeKind = CountedByInvalidPointeeTypeKind::VALID;
+  bool ShouldWarn = false;
+  if (!Flags.CountInBytes && PointeeTy->isAlwaysIncompleteType()) {
+    // In general using `counted_by` or `counted_by_or_null` on
+    // pointers where the pointee is an incomplete type are problematic. This
+    // is because it isn't possible to compute the pointer's bounds without
+    // knowing the pointee type size. At the same time it is common to forward
+    // declare types in header files.
+    //
+    // E.g.:
+    //
+    // struct Handle;
+    // struct Wrapper {
+    //   size_t count;
+    //   struct Handle* __counted_by(count) handles;
+    // }
+    //
+    // To allow the above code pattern but still prevent the pointee type from
+    // being incomplete in places where bounds checks are needed the following
+    // scheme is used:
+    //
+    // * When the pointee type might not always be an incomplete type (i.e.
+    // a type that is currently incomplete but might be completed later
+    // on in the translation unit) the attribute is allowed by this method
+    // but later uses of the FieldDecl are checked that the pointee type
+    // is complete see `BoundsSafetyCheckAssignmentToCountAttrPtr`,
+    // `BoundsSafetyCheckInitialization`, and
+    // `BoundsSafetyCheckUseOfCountAttrPtr`
+    //
+    // * When the pointee type is always an incomplete type (e.g.
+    // `void` in strict C mode) the attribute is disallowed by this method
+    // because we know the type can never be completed so there's no reason
+    // to allow it.
+    //
+    // Exception: void has an implicit size of 1 byte for pointer arithmetic
+    // (following GNU convention). Therefore, counted_by on void* is allowed
+    // and behaves equivalently to sized_by (treating the count as bytes).
+    if (PointeeTy->isVoidType() && !getLangOpts().hasBoundsSafetyAttributes()) 
{
+      // Emit a warning that this is a GNU extension.
+      Diag(AttrLoc, diag::ext_gnu_counted_by_void_ptr) << Kind;
+      Diag(AttrLoc, diag::note_gnu_counted_by_void_ptr_use_sized_by) << Kind;
+      assert(!Ty->isArrayType() && "sized_by is not allowed on arrays");
+      if (UpdateFlags)
+        Flags.CountInBytes = true;
+      return true;
+    }
+    InvalidTypeKind = CountedByInvalidPointeeTypeKind::INCOMPLETE;
+  } else if (PointeeTy->isSizelessType()) {
+    InvalidTypeKind = CountedByInvalidPointeeTypeKind::SIZELESS;
+  } else if (PointeeTy->isFunctionType()) {
+    InvalidTypeKind = CountedByInvalidPointeeTypeKind::FUNCTION;
+  } else if (!Flags.CountInBytes &&
+             PointeeTy->isStructureTypeWithFlexibleArrayMember()) {
+    if (Ty->isArrayType()) {
+      // This is a workaround for the Linux kernel that has already adopted
+      // `counted_by` on a FAM where the pointee is a struct with a FAM. This
+      // should be an error because computing the bounds of the array cannot
+      // be done correctly without manually traversing every struct object in
+      // the array at runtime. To allow the code to be built this error is
+      // downgraded to a warning.
+      ShouldWarn = true;
+    }
+    InvalidTypeKind = CountedByInvalidPointeeTypeKind::FLEXIBLE_ARRAY_MEMBER;
+  }
+
+  if (InvalidTypeKind != CountedByInvalidPointeeTypeKind::VALID) {
+    unsigned DiagID = ShouldWarn
+                          ? diag::warn_counted_by_attr_elt_type_unknown_size
+                          : diag::err_counted_by_attr_pointee_unknown_size;
+    Diag(AttrLoc, DiagID) << SelectPtrOrArr << PointeeTy << 
(int)InvalidTypeKind
+                          << (ShouldWarn ? 1 : 0) << Kind << AttrRange;
+    if (ShouldWarn)
+      return true;
+    if (getLangOpts().hasBoundsSafetyAttributes() && !Ty->isArrayType()) {
+      // Under BoundsSafety, recover by switching to byte count so that
+      // type construction can proceed and emit follow-up diagnostics.
+      // We don't do this for arrays because `__sized_by` is not allowed.
+      Flags.CountInBytes = true;
+      return true;
+    }
+    return false;
+  }
+
+  return true;
+}
+
+bool Sema::ValidateBoundsAttrTypeForTypePosition(
+    QualType Ty, AttributeCommonInfo::Kind AttrKind, SourceLocation AttrLoc,
+    SourceRange AttrRange, unsigned PointerNestLevel, BoundsAttrFlags &Flags) {
+  Flags = BoundsAttrFlags::get(AttrKind);
+
+  // Preserve the counted_by kind for the GNU void pointer extension to match
----------------
delcypher wrote:

This comment is confusing. I can't see any obvious relationship between what is 
says and the call.

https://github.com/llvm/llvm-project/pull/227774
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to