Index: include/clang/AST/CanonicalType.h =================================================================== --- include/clang/AST/CanonicalType.h +++ include/clang/AST/CanonicalType.h @@ -264,6 +264,10 @@ // Type predicates LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isObjectType) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isIncompleteType) + LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isDefiniteType) + LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isIndefiniteType) + LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isSizelessType) + LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isSizelessBuiltinType) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isIncompleteOrObjectType) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isVariablyModifiedType) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isIntegerType) Index: include/clang/AST/Type.h =================================================================== --- include/clang/AST/Type.h +++ include/clang/AST/Type.h @@ -1852,6 +1852,29 @@ /// or QualType::getSingleStepDesugaredType(const ASTContext&). QualType getLocallyUnqualifiedSingleStepDesugaredType() const; + /// As an extension, we classify types as one of "sized" or "sizeless"; + /// every type is one or the other. Standard types are all sized; + /// sizeless types are purely an extension. + /// + /// Sizeless types contain data with no specified size, alignment, + /// or layout. They are always incomplete. + bool isSizelessType() const; + bool isSizelessBuiltinType() const; + + /// As an extension, we classify types as one of "definite" or "indefinite"; + /// every type is one or the other. Definite types provide enough + /// information to construct new objects of that type while indefinite + /// types do not. A type is indefinite iff the type is both "sized" + /// and incomplete according to the standard definition. + /// + /// If Def is non-null, and the type refers to some kind of declaration + /// that can be completed (such as a C struct, C++ class, or Objective-C + /// class), Def will be set to the declaration. + bool isIndefiniteType(NamedDecl **Def = nullptr) const; + bool isDefiniteType(NamedDecl **Def = nullptr) const { + return !isIndefiniteType(Def); + } + /// Types are partitioned into 3 broad categories (C99 6.2.5p1): /// object types, function types, and incomplete types. @@ -1860,10 +1883,23 @@ /// determine its size (e.g. void, or a fwd declared struct). Clients of this /// routine will need to determine if the size is actually required. /// + /// A type is incomplete according to our definition iff: + /// - it is incomplete according to the standard definition; or + /// - it is "sizeless" + /// + /// The intention is that the usual rules for incomplete types will + /// by default apply to sizeless types as well. Specifically-chosen + /// rules can then be redefined in terms of "definite" and "indefinite" + /// if sizeless definite types are acceptable. + /// /// Def If non-null, and the type refers to some kind of declaration /// that can be completed (such as a C struct, C++ class, or Objective-C /// class), will be set to the declaration. - bool isIncompleteType(NamedDecl **Def = nullptr) const; + bool isIncompleteType(NamedDecl **Def = nullptr) const { + // isIndefiniteType also sets Def to nullptr on failure, so the call + // to isSizelessType is safe. + return isIndefiniteType(Def) || isSizelessType(); + } /// Return true if this is an incomplete or object /// type, in other words, not a function type. Index: lib/AST/Type.cpp =================================================================== --- lib/AST/Type.cpp +++ lib/AST/Type.cpp @@ -2056,10 +2056,10 @@ return !isa(CanonicalType); } -/// isIncompleteType - Return true if this is an incomplete type (C99 6.2.5p1) -/// - a type that can describe objects, but which lacks information needed to -/// determine its size. -bool Type::isIncompleteType(NamedDecl **Def) const { +/// isIndefiniteType - Return true if this is an indefinite type - a type that +/// can describe objects, but lacks information needed to construct them. +/// For sized types, "indefinite" is equivalent to "incomplete" (C99 6.2.5p1). +bool Type::isIndefiniteType(NamedDecl **Def) const { if (Def) *Def = nullptr; @@ -2129,6 +2129,27 @@ } } +bool Type::isSizelessBuiltinType() const { + if (const BuiltinType *BT = getAs()) { + switch (BT->getKind()) { + // SVE Types +#define SVE_VECTOR_TYPE(Name, Id, SingletonId, ElKind, ElBits, IsSigned, IsFP)\ + case BuiltinType::Id: +#define SVE_PREDICATE_TYPE(Name, Id, SingletonId, ElKind) \ + case BuiltinType::Id: +#include "clang/Basic/AArch64SVEACLETypes.def" + return true; + default: + return false; + } + } + return false; +} + +bool Type::isSizelessType() const { + return isSizelessBuiltinType(); +} + bool QualType::isPODType(const ASTContext &Context) const { // C++11 has a more relaxed definition of POD. if (Context.getLangOpts().CPlusPlus11) Index: unittests/AST/CMakeLists.txt =================================================================== --- unittests/AST/CMakeLists.txt +++ unittests/AST/CMakeLists.txt @@ -26,6 +26,7 @@ Language.cpp NamedDeclPrinterTest.cpp OMPStructuredBlockTest.cpp + SizelessTypesTest.cpp SourceLocationTest.cpp StmtPrinterTest.cpp StructuralEquivalenceTest.cpp Index: unittests/AST/SizelessTypesTest.cpp =================================================================== --- /dev/null +++ unittests/AST/SizelessTypesTest.cpp @@ -0,0 +1,105 @@ +//===- unittests/AST/SizelessTypesTest.cpp --- Sizeless type tests --------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains tests for clang::Type queries related to sizeless types. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTContext.h" +#include "clang/AST/Type.h" +#include "clang/Tooling/Tooling.h" +#include "gtest/gtest.h" + +using namespace clang; + +struct SizelessTypeTester : public ::testing::Test { + // Declare an incomplete structure type. + std::unique_ptr AST = tooling::buildASTFromCode("struct foo;"); + ASTContext &Ctx = AST->getASTContext(); + TranslationUnitDecl &TU = *Ctx.getTranslationUnitDecl(); + TypeDecl *Foo = cast(TU.lookup(&Ctx.Idents.get("foo")).front()); + const Type *FooTy = Foo->getTypeForDecl(); +}; + +TEST_F(SizelessTypeTester, TestSizeless) { + ASSERT_TRUE(Ctx.SveInt8Ty->isSizelessType()); + ASSERT_TRUE(Ctx.SveInt16Ty->isSizelessType()); + ASSERT_TRUE(Ctx.SveInt32Ty->isSizelessType()); + ASSERT_TRUE(Ctx.SveInt64Ty->isSizelessType()); + + ASSERT_TRUE(Ctx.SveUint8Ty->isSizelessType()); + ASSERT_TRUE(Ctx.SveUint16Ty->isSizelessType()); + ASSERT_TRUE(Ctx.SveUint32Ty->isSizelessType()); + ASSERT_TRUE(Ctx.SveUint64Ty->isSizelessType()); + + ASSERT_TRUE(Ctx.SveFloat16Ty->isSizelessType()); + ASSERT_TRUE(Ctx.SveFloat32Ty->isSizelessType()); + ASSERT_TRUE(Ctx.SveFloat64Ty->isSizelessType()); + + ASSERT_TRUE(Ctx.SveBoolTy->isSizelessType()); + + ASSERT_FALSE(Ctx.VoidTy->isSizelessType()); + ASSERT_FALSE(Ctx.PseudoObjectTy->isSizelessType()); + ASSERT_FALSE(FooTy->isSizelessType()); + + ASSERT_FALSE(Ctx.getPointerType(Ctx.SveBoolTy)->isSizelessType()); + ASSERT_FALSE(Ctx.getLValueReferenceType(Ctx.SveBoolTy)->isSizelessType()); + ASSERT_FALSE(Ctx.getRValueReferenceType(Ctx.SveBoolTy)->isSizelessType()); +} + +TEST_F(SizelessTypeTester, TestIndefinite) { + ASSERT_FALSE(Ctx.SveInt8Ty->isIndefiniteType()); + ASSERT_FALSE(Ctx.SveInt16Ty->isIndefiniteType()); + ASSERT_FALSE(Ctx.SveInt32Ty->isIndefiniteType()); + ASSERT_FALSE(Ctx.SveInt64Ty->isIndefiniteType()); + + ASSERT_FALSE(Ctx.SveUint8Ty->isIndefiniteType()); + ASSERT_FALSE(Ctx.SveUint16Ty->isIndefiniteType()); + ASSERT_FALSE(Ctx.SveUint32Ty->isIndefiniteType()); + ASSERT_FALSE(Ctx.SveUint64Ty->isIndefiniteType()); + + ASSERT_FALSE(Ctx.SveFloat16Ty->isIndefiniteType()); + ASSERT_FALSE(Ctx.SveFloat32Ty->isIndefiniteType()); + ASSERT_FALSE(Ctx.SveFloat64Ty->isIndefiniteType()); + + ASSERT_FALSE(Ctx.SveBoolTy->isIndefiniteType()); + + ASSERT_TRUE(Ctx.VoidTy->isIndefiniteType()); + ASSERT_FALSE(Ctx.PseudoObjectTy->isIndefiniteType()); + ASSERT_TRUE(FooTy->isIndefiniteType()); + + ASSERT_FALSE(Ctx.getPointerType(Ctx.SveBoolTy)->isIndefiniteType()); + ASSERT_FALSE(Ctx.getLValueReferenceType(Ctx.SveBoolTy)->isIndefiniteType()); + ASSERT_FALSE(Ctx.getRValueReferenceType(Ctx.SveBoolTy)->isIndefiniteType()); +} + +TEST_F(SizelessTypeTester, TestIncomplete) { + ASSERT_TRUE(Ctx.SveInt8Ty->isIncompleteType()); + ASSERT_TRUE(Ctx.SveInt16Ty->isIncompleteType()); + ASSERT_TRUE(Ctx.SveInt32Ty->isIncompleteType()); + ASSERT_TRUE(Ctx.SveInt64Ty->isIncompleteType()); + + ASSERT_TRUE(Ctx.SveUint8Ty->isIncompleteType()); + ASSERT_TRUE(Ctx.SveUint16Ty->isIncompleteType()); + ASSERT_TRUE(Ctx.SveUint32Ty->isIncompleteType()); + ASSERT_TRUE(Ctx.SveUint64Ty->isIncompleteType()); + + ASSERT_TRUE(Ctx.SveFloat16Ty->isIncompleteType()); + ASSERT_TRUE(Ctx.SveFloat32Ty->isIncompleteType()); + ASSERT_TRUE(Ctx.SveFloat64Ty->isIncompleteType()); + + ASSERT_TRUE(Ctx.SveBoolTy->isIncompleteType()); + + ASSERT_TRUE(Ctx.VoidTy->isIncompleteType()); + ASSERT_FALSE(Ctx.PseudoObjectTy->isIncompleteType()); + ASSERT_TRUE(FooTy->isIncompleteType()); + + ASSERT_FALSE(Ctx.getPointerType(Ctx.SveBoolTy)->isIncompleteType()); + ASSERT_FALSE(Ctx.getLValueReferenceType(Ctx.SveBoolTy)->isIncompleteType()); + ASSERT_FALSE(Ctx.getRValueReferenceType(Ctx.SveBoolTy)->isIncompleteType()); +}