[clang] [Clang] add user-level sizeless attribute (PR #71894)

William Moses via cfe-commits cfe-commits at lists.llvm.org
Thu Nov 9 21:03:41 PST 2023


https://github.com/wsmoses updated https://github.com/llvm/llvm-project/pull/71894

>From 3e23febb464791a4b24ec73476ef988d2e98ca18 Mon Sep 17 00:00:00 2001
From: Billy Moses <gh at wsmoses.com>
Date: Thu, 9 Nov 2023 02:17:05 +0000
Subject: [PATCH] [Clang] add user-level sizeless attribute

---
 clang/include/clang/Basic/Attr.td     |  7 +++
 clang/include/clang/Basic/AttrDocs.td | 31 +++++++++++++
 clang/include/clang/Sema/Sema.h       | 22 +++++++++-
 clang/lib/AST/Type.cpp                | 63 ++++++++++++++++++++++++++-
 clang/lib/AST/TypePrinter.cpp         |  3 ++
 clang/lib/Sema/SemaDecl.cpp           |  2 +-
 clang/lib/Sema/SemaExpr.cpp           |  6 +--
 clang/lib/Sema/SemaType.cpp           | 28 ++++++++++--
 clang/test/Sema/sizeless.c            | 22 ++++++++++
 9 files changed, 175 insertions(+), 9 deletions(-)
 create mode 100644 clang/test/Sema/sizeless.c

diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 60b549999c155e5..591762c8be5a705 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4285,3 +4285,10 @@ def PreferredType: InheritableAttr {
   let Args = [TypeArgument<"Type", 1>];
   let Documentation = [PreferredTypeDocumentation];
 }
+
+def SizelessType : DeclOrTypeAttr {
+  let Spellings = [Clang<"sizeless">];
+  let Documentation = [SizelessTypeDocs];
+  let HasCustomParsing = 1;
+}
+
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 05703df2129f612..2675aaad5a96a45 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -7095,6 +7095,37 @@ neither type checking rules, nor runtime semantics. In particular:
   }];
 }
 
+def SizelessTypeDocs : Documentation {
+  let Category = DocCatType;
+  let Heading = "sizeless";
+  let Content = [{
+This attribute is used to on types to forbid the use of the sizeof operation.
+This may be useful for code with scalable vectors, which may forbid the use
+of sizeof on that platform already. This attribute enables more consistent
+behavior by forbidding sizeof on all platforms. 
+
+The attribute takes no arguments.
+
+For example:
+
+.. code-block:: c++
+
+  int* [[clang::sizeless]] f();
+
+The attribute does not have any effect on the semantics of the type system,
+neither type checking rules, nor runtime semantics. In particular:
+
+- ``std::is_same<T, T [[clang::sizeless]]>`` is true for all types
+  ``T``.
+
+- It is not permissible for overloaded functions or template specializations
+  to differ merely by an ``sizeless`` attribute.
+
+- The presence of an ``sizeless`` attribute will not affect name
+  mangling.
+  }];
+}
+
 def WeakDocs : Documentation {
   let Category = DocCatDecl;
   let Content = [{
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index a8c41492b61ac4c..3e6988759e19a00 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2275,9 +2275,13 @@ class Sema final {
     Normal,
 
     /// Relax the normal rules for complete types so that they include
-    /// sizeless built-in types.
+    /// sizeless built-in or sizeless user types.
     AcceptSizeless,
 
+    /// Relax the normal rules for complete types so that they include
+    /// sizeless user types, but not sizeless builtin types.
+    AcceptUserSizeless,
+
     // FIXME: Eventually we should flip the default to Normal and opt in
     // to AcceptSizeless rather than opt out of it.
     Default = AcceptSizeless
@@ -2539,6 +2543,14 @@ class Sema final {
   bool RequireCompleteSizedType(SourceLocation Loc, QualType T, unsigned DiagID,
                                 const Ts &... Args) {
     SizelessTypeDiagnoser<Ts...> Diagnoser(DiagID, Args...);
+    return RequireCompleteType(Loc, T, CompleteTypeKind::AcceptUserSizeless,
+                               Diagnoser);
+  }
+
+  template <typename... Ts>
+  bool RequireCompleteSizeofType(SourceLocation Loc, QualType T,
+                                 unsigned DiagID, const Ts &...Args) {
+    SizelessTypeDiagnoser<Ts...> Diagnoser(DiagID, Args...);
     return RequireCompleteType(Loc, T, CompleteTypeKind::Normal, Diagnoser);
   }
 
@@ -2567,6 +2579,14 @@ class Sema final {
   bool RequireCompleteSizedExprType(Expr *E, unsigned DiagID,
                                     const Ts &... Args) {
     SizelessTypeDiagnoser<Ts...> Diagnoser(DiagID, Args...);
+    return RequireCompleteExprType(E, CompleteTypeKind::AcceptUserSizeless,
+                                   Diagnoser);
+  }
+
+  template <typename... Ts>
+  bool RequireCompleteSizeofExprType(Expr *E, unsigned DiagID,
+                                     const Ts &...Args) {
+    SizelessTypeDiagnoser<Ts...> Diagnoser(DiagID, Args...);
     return RequireCompleteExprType(E, CompleteTypeKind::Normal, Diagnoser);
   }
 
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index c8e452e2feab0bf..a142c29c5c579e6 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -43,6 +43,7 @@
 #include "llvm/ADT/APSInt.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/ErrorHandling.h"
@@ -2381,6 +2382,7 @@ bool Type::isSizelessBuiltinType() const {
       return false;
     }
   }
+
   return false;
 }
 
@@ -2400,7 +2402,66 @@ bool Type::isWebAssemblyTableType() const {
   return false;
 }
 
-bool Type::isSizelessType() const { return isSizelessBuiltinType(); }
+bool Type::isSizelessType() const {
+  // Check if this type or any of its constituents are sizeless, due to
+  // being a builtin type or individually having the user attribute.
+  // As structs can be recursive, we iterate through without repeats.
+  SmallVector<const Type *, 1> todo = {this};
+  llvm::SmallPtrSet<const Type *, 1> done;
+
+  while (todo.size()) {
+    auto current = todo.pop_back_val();
+    if (done.count(current))
+      continue;
+    done.insert(current);
+
+    // If either this is a known sizeless type from being a builtin
+    // or as marked by the user, this is a sizeless type.
+    if (current->isSizelessBuiltinType())
+      return true;
+    if (current->hasAttr(attr::SizelessType))
+      return true;
+
+    // Otherwise return true if any inner types are sizeless.
+    switch (current->CanonicalType->getTypeClass()) {
+    default:
+      break;
+    case Record: {
+      // A struct with sizeless types is itself sizeless.
+      RecordDecl *Rec = cast<RecordType>(current->CanonicalType)->getDecl();
+
+      // skip incomplete structs
+      if (!Rec->isCompleteDefinition())
+        break;
+
+      // a struct marked sizeless explicitly is sizeless
+      if (Rec->hasAttr<clang::SizelessTypeAttr>())
+        return true;
+
+      // A struct is sizeless if it contains a sizeless field
+      for (auto field : Rec->fields())
+        todo.push_back(field->getType().getTypePtr());
+
+      // A class is sizeless if it contains a sizeless base
+      if (auto CXXRec = dyn_cast<CXXRecordDecl>(Rec))
+        for (auto base : CXXRec->bases())
+          todo.push_back(base.getType().getTypePtr());
+      break;
+    }
+    case ConstantArray:
+    case VariableArray:
+      // An array is sizeless if its element type is sizeless
+      todo.push_back(cast<ArrayType>(current->CanonicalType)
+                         ->getElementType()
+                         .getTypePtr());
+      break;
+    case IncompleteArray:
+      // skip incomplete arrays
+      break;
+    }
+  }
+  return false;
+}
 
 bool Type::isSizelessVectorType() const {
   return isSVESizelessBuiltinType() || isRVVSizelessBuiltinType();
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index e4f5f40cd625996..429deac97a82ab9 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -1861,6 +1861,9 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
   case attr::MSABI: OS << "ms_abi"; break;
   case attr::SysVABI: OS << "sysv_abi"; break;
   case attr::RegCall: OS << "regcall"; break;
+  case attr::SizelessType:
+    OS << "sizeless";
+    break;
   case attr::Pcs: {
     OS << "pcs(";
    QualType t = T->getEquivalentType();
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 7a424eaa5fe7d8e..30a1aaefb4fbfaf 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -8837,7 +8837,7 @@ void Sema::CheckVariableDeclarationType(VarDecl *NewVD) {
     return;
   }
 
-  if (!NewVD->hasLocalStorage() && T->isSizelessType() &&
+  if (!NewVD->hasLocalStorage() && T->isSizelessBuiltinType() &&
       !T.isWebAssemblyReferenceType()) {
     Diag(NewVD->getLocation(), diag::err_sizeless_nonlocal) << T;
     NewVD->setInvalidDecl();
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 432e4285e8a01d6..ade81573dfe4271 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -4491,13 +4491,13 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(Expr *E,
   // be complete (and will attempt to complete it if it's an array of unknown
   // bound).
   if (ExprKind == UETT_AlignOf || ExprKind == UETT_PreferredAlignOf) {
-    if (RequireCompleteSizedType(
+    if (RequireCompleteSizeofType(
             E->getExprLoc(), Context.getBaseElementType(E->getType()),
             diag::err_sizeof_alignof_incomplete_or_sizeless_type,
             getTraitSpelling(ExprKind), E->getSourceRange()))
       return true;
   } else {
-    if (RequireCompleteSizedExprType(
+    if (RequireCompleteSizeofExprType(
             E, diag::err_sizeof_alignof_incomplete_or_sizeless_type,
             getTraitSpelling(ExprKind), E->getSourceRange()))
       return true;
@@ -4772,7 +4772,7 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(QualType ExprType,
                                       ExprKind))
     return false;
 
-  if (RequireCompleteSizedType(
+  if (RequireCompleteSizeofType(
           OpLoc, ExprType, diag::err_sizeof_alignof_incomplete_or_sizeless_type,
           KWName, ExprRange))
     return true;
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 560feafa1857cb3..6dbe7a5d0c39cc4 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -8651,6 +8651,14 @@ static void HandleAnnotateTypeAttr(TypeProcessingState &State,
   CurType = State.getAttributedType(AnnotateTypeAttr, CurType, CurType);
 }
 
+static void HandleSizelessTypeAttr(TypeProcessingState &State,
+                                   QualType &CurType, const ParsedAttr &PA) {
+  Sema &S = State.getSema();
+
+  auto *SizelessTypeAttr = SizelessTypeAttr::Create(S.Context, PA);
+  CurType = State.getAttributedType(SizelessTypeAttr, CurType, CurType);
+}
+
 static void HandleLifetimeBoundAttr(TypeProcessingState &State,
                                     QualType &CurType,
                                     ParsedAttr &Attr) {
@@ -8947,6 +8955,11 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type,
       attr.setUsedAsTypeAttr();
       break;
     }
+    case ParsedAttr::AT_SizelessType: {
+      HandleSizelessTypeAttr(state, type, attr);
+      attr.setUsedAsTypeAttr();
+      break;
+    }
     }
 
     // Handle attributes that are defined in a macro. We do not want this to be
@@ -9274,9 +9287,18 @@ bool Sema::RequireCompleteTypeImpl(SourceLocation Loc, QualType T,
   }
 
   NamedDecl *Def = nullptr;
-  bool AcceptSizeless = (Kind == CompleteTypeKind::AcceptSizeless);
-  bool Incomplete = (T->isIncompleteType(&Def) ||
-                     (!AcceptSizeless && T->isSizelessBuiltinType()));
+  bool Incomplete = T->isIncompleteType(&Def);
+  if (!Incomplete)
+    switch (Kind) {
+    case CompleteTypeKind::Normal:
+      Incomplete |= T->isSizelessType();
+      break;
+    case CompleteTypeKind::AcceptUserSizeless:
+      Incomplete |= T->isSizelessBuiltinType();
+      break;
+    case CompleteTypeKind::AcceptSizeless:
+      break;
+    }
 
   // Check that any necessary explicit specializations are visible. For an
   // enum, we just need the declaration, so don't check this.
diff --git a/clang/test/Sema/sizeless.c b/clang/test/Sema/sizeless.c
new file mode 100644
index 000000000000000..ea64c89d269bc78
--- /dev/null
+++ b/clang/test/Sema/sizeless.c
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 %s -fsyntax-only -verify
+
+struct T {
+	int __attribute__((sizeless)) x;
+	float y;
+};
+
+void f(void) {
+  int size_intty[sizeof(int __attribute__((sizeless))) == 0 ? 1 : -1];        // expected-error {{invalid application of 'sizeof' to sizeless type 'int __attribute__((sizeless))'}}
+  int align_intty[__alignof__(int __attribute__((sizeless))) == 16 ? 1 : -1]; // expected-error {{invalid application of '__alignof' to sizeless type 'int __attribute__((sizeless))'}}
+  
+  int __attribute__((sizeless)) var1;
+  int size_int[sizeof(var1) == 0 ? 1 : -1];        // expected-error {{invalid application of 'sizeof' to sizeless type 'int __attribute__((sizeless))'}}
+  int align_int[__alignof__(var1) == 16 ? 1 : -1]; // expected-error {{invalid application of '__alignof' to sizeless type 'int __attribute__((sizeless))'}}
+
+  int size_struct[sizeof(struct T) == 0 ? 1 : -1];        // expected-error {{invalid application of 'sizeof' to sizeless type 'struct T'}}
+  int align_struct[__alignof__(struct T) == 16 ? 1 : -1]; // expected-error {{invalid application of '__alignof' to sizeless type 'struct T'}}
+  
+  struct T var2;
+  int size_structty[sizeof(var2) == 0 ? 1 : -1];        // expected-error {{invalid application of 'sizeof' to sizeless type 'struct T'}}
+  int align_structty[__alignof__(var2) == 16 ? 1 : -1]; // expected-error {{invalid application of '__alignof' to sizeless type 'struct T'}}
+}



More information about the cfe-commits mailing list