[clang] [Clang] add user-level sizeless attribute (PR #71894)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Nov 9 21:03:18 PST 2023
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: William Moses (wsmoses)
<details>
<summary>Changes</summary>
As discussed in [this RFC](https://discourse.llvm.org/t/rfc-attribute-no-sizeof/74695) this PR implements a. new user-level sizeless attribute.
This prevents types or variables marked with this attribute from having sizeof or alignof taken, including of struct or other types which contain sizeless types.
---
Full diff: https://github.com/llvm/llvm-project/pull/71894.diff
9 Files Affected:
- (modified) clang/include/clang/Basic/Attr.td (+7)
- (modified) clang/include/clang/Basic/AttrDocs.td (+31)
- (modified) clang/include/clang/Sema/Sema.h (+19-1)
- (modified) clang/lib/AST/Type.cpp (+54-1)
- (modified) clang/lib/AST/TypePrinter.cpp (+1)
- (modified) clang/lib/Sema/SemaDecl.cpp (+1-1)
- (modified) clang/lib/Sema/SemaExpr.cpp (+3-3)
- (modified) clang/lib/Sema/SemaType.cpp (+26-3)
- (added) clang/test/Sema/sizeless.c (+22)
``````````diff
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..db1e00a2747f875 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,13 @@ 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 +2578,13 @@ 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..f25e90555ba608d 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -44,6 +44,7 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MathExtras.h"
@@ -2381,6 +2382,7 @@ bool Type::isSizelessBuiltinType() const {
return false;
}
}
+
return false;
}
@@ -2400,7 +2402,58 @@ 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..9c04e8ee9bfa382 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -1861,6 +1861,7 @@ 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..42f6871caa252e3 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -8651,6 +8651,15 @@ 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 +8956,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 +9288,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'}}
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/71894
More information about the cfe-commits
mailing list