[clang] [Clang] add user-level sizeless attribute (PR #71894)
William Moses via cfe-commits
cfe-commits at lists.llvm.org
Thu Nov 9 22:21:43 PST 2023
https://github.com/wsmoses updated https://github.com/llvm/llvm-project/pull/71894
>From 4de8e238b6680540f4bc884c66430eb2974512c2 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 1/2] [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 | 24 +++++-----
clang/lib/Sema/SemaType.cpp | 28 ++++++++++--
clang/test/Sema/sizeless.c | 22 ++++++++++
9 files changed, 184 insertions(+), 18 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..31a8374b3a3f83d 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,16 +4772,6 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(QualType ExprType,
ExprKind))
return false;
- if (RequireCompleteSizedType(
- OpLoc, ExprType, diag::err_sizeof_alignof_incomplete_or_sizeless_type,
- KWName, ExprRange))
- return true;
-
- if (ExprType->isFunctionType()) {
- Diag(OpLoc, diag::err_sizeof_alignof_function_type) << KWName << ExprRange;
- return true;
- }
-
// WebAssembly tables are always illegal operands to unary expressions and
// type traits.
if (Context.getTargetInfo().getTriple().isWasm() &&
@@ -4791,6 +4781,16 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(QualType ExprType,
return true;
}
+ if (RequireCompleteSizeofType(
+ OpLoc, ExprType, diag::err_sizeof_alignof_incomplete_or_sizeless_type,
+ KWName, ExprRange))
+ return true;
+
+ if (ExprType->isFunctionType()) {
+ Diag(OpLoc, diag::err_sizeof_alignof_function_type) << KWName << ExprRange;
+ return true;
+ }
+
if (CheckObjCTraitOperandConstraints(*this, ExprType, OpLoc, ExprRange,
ExprKind))
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..acae558612b8945
--- /dev/null
+++ b/clang/test/Sema/sizeless.c
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 %s -fsyntax-only -verify
+
+struct T { // expected-note {{forward declaration of 'struct T'}} expected-note {{forward declaration of 'struct T'}} expected-note {{forward declaration of 'struct T'}} expected-note {{forward declaration of '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'}}
+}
>From 9e88e7c028de8c8c2a6b187d8b216cf4b1bf8c49 Mon Sep 17 00:00:00 2001
From: William Moses <gh at wsmoses.com>
Date: Fri, 10 Nov 2023 00:21:36 -0600
Subject: [PATCH 2/2] Apply suggestions from code review
Co-authored-by: Timm Baeder <tbaeder at redhat.com>
---
clang/lib/AST/Type.cpp | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index a142c29c5c579e6..f0e8bde91332a08 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -2406,8 +2406,8 @@ 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;
+ SmallVector<const Type *, 1> Todo = {this};
+ llvm::SmallPtrSet<const Type *, 1> Done;
while (todo.size()) {
auto current = todo.pop_back_val();
@@ -2428,7 +2428,7 @@ bool Type::isSizelessType() const {
break;
case Record: {
// A struct with sizeless types is itself sizeless.
- RecordDecl *Rec = cast<RecordType>(current->CanonicalType)->getDecl();
+ const auto *Rec = cast<RecordType>(current->CanonicalType)->getDecl();
// skip incomplete structs
if (!Rec->isCompleteDefinition())
@@ -2443,7 +2443,7 @@ bool Type::isSizelessType() const {
todo.push_back(field->getType().getTypePtr());
// A class is sizeless if it contains a sizeless base
- if (auto CXXRec = dyn_cast<CXXRecordDecl>(Rec))
+ if (const auto *CXXRec = dyn_cast<CXXRecordDecl>(Rec))
for (auto base : CXXRec->bases())
todo.push_back(base.getType().getTypePtr());
break;
More information about the cfe-commits
mailing list