diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 60b549999c155..591762c8be5a7 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 05703df2129f6..25500689eb4f1 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`` 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 a8c41492b61ac..3e6988759e19a 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 Diagnoser(DiagID, Args...); + return RequireCompleteType(Loc, T, CompleteTypeKind::AcceptUserSizeless, + Diagnoser); + } + + template + bool RequireCompleteSizeofType(SourceLocation Loc, QualType T, + unsigned DiagID, const Ts &...Args) { + SizelessTypeDiagnoser 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 Diagnoser(DiagID, Args...); + return RequireCompleteExprType(E, CompleteTypeKind::AcceptUserSizeless, + Diagnoser); + } + + template + bool RequireCompleteSizeofExprType(Expr *E, unsigned DiagID, + const Ts &...Args) { + SizelessTypeDiagnoser Diagnoser(DiagID, Args...); return RequireCompleteExprType(E, CompleteTypeKind::Normal, Diagnoser); } diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index c8e452e2feab0..03354a36bc4ec 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 Todo = {this}; + llvm::SmallPtrSet 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. + const auto *Rec = cast(current->CanonicalType)->getDecl(); + + // skip incomplete structs + if (!Rec->isCompleteDefinition()) + break; + + // a struct marked sizeless explicitly is sizeless + if (Rec->hasAttr()) + 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 (const auto *CXXRec = dyn_cast(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(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 e4f5f40cd6259..429deac97a82a 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 7a424eaa5fe7d..30a1aaefb4fbf 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 432e4285e8a01..31a8374b3a3f8 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 560feafa1857c..6dbe7a5d0c39c 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 0000000000000..acae558612b89 --- /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'}} +}