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 <g...@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 <g...@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 <tbae...@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; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits