https://github.com/wsmoses created https://github.com/llvm/llvm-project/pull/71894
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. >From fb96061f85585e15b0a15d7b89ecf93b5dd4e3f2 Mon Sep 17 00:00:00 2001 From: Billy Moses <g...@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 | 20 +++++++++- clang/lib/AST/Type.cpp | 55 ++++++++++++++++++++++++++- clang/lib/AST/TypePrinter.cpp | 1 + clang/lib/Sema/SemaDecl.cpp | 2 +- clang/lib/Sema/SemaExpr.cpp | 6 +-- clang/lib/Sema/SemaType.cpp | 29 ++++++++++++-- clang/test/Sema/sizeless.c | 22 +++++++++++ 9 files changed, 164 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..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'}} +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits