Skip to content

[HLSL] Implement '__builtin_hlsl_is_intangible' type trait #104544

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
Original file line number Diff line number Diff line change
Expand Up @@ -249,4 +249,8 @@ FIELD(HasDeclaredCopyAssignmentWithConstParam, 1, MERGE_OR)
/// base classes or fields have a no-return destructor
FIELD(IsAnyDestructorNoReturn, 1, NO_MERGE)

/// Whether the record type is intangible (if any base classes or fields have
/// type that is intangible). HLSL only.
FIELD(IsHLSLIntangible, 1, NO_MERGE)

#undef FIELD
4 changes: 4 additions & 0 deletions clang/include/clang/AST/DeclCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -1547,6 +1547,10 @@ class CXXRecordDecl : public RecordDecl {
/// destructors are marked noreturn.
bool isAnyDestructorNoReturn() const { return data().IsAnyDestructorNoReturn; }

/// Returns true if the class contains HLSL intangible type, either as
/// a field or in base class.
bool isHLSLIntangible() const { return data().IsHLSLIntangible; }

/// If the class is a local class [class.local], returns
/// the enclosing function declaration.
const FunctionDecl *isLocalClass() const {
Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -2658,6 +2658,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
#define HLSL_INTANGIBLE_TYPE(Name, Id, SingletonId) bool is##Id##Type() const;
#include "clang/Basic/HLSLIntangibleTypes.def"
bool isHLSLSpecificType() const; // Any HLSL specific type
bool isHLSLIntangibleType() const; // Any HLSL intangible type

/// Determines if this type, which must satisfy
/// isObjCLifetimeType(), is implicitly __unsafe_unretained rather
Expand Down Expand Up @@ -8336,6 +8337,12 @@ inline bool Type::isHLSLSpecificType() const {
false; // end boolean or operation
}

inline bool Type::isHLSLIntangibleType() const {
// All HLSL specific types are currently intangible type as well, but that
// might change in the future.
return isHLSLSpecificType();
}

inline bool Type::isTemplateTypeParmType() const {
return isa<TemplateTypeParmType>(CanonicalType);
}
Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -660,8 +660,9 @@ KEYWORD(out , KEYHLSL)
#define HLSL_INTANGIBLE_TYPE(Name, Id, SingletonId) KEYWORD(Name, KEYHLSL)
#include "clang/Basic/HLSLIntangibleTypes.def"

// HLSL Type traits.
// HLSL Type traits
TYPE_TRAIT_2(__builtin_hlsl_is_scalarized_layout_compatible, IsScalarizedLayoutCompatible, KEYHLSL)
TYPE_TRAIT_1(__builtin_hlsl_is_intangible, IsIntangibleType, KEYHLSL)

// OpenMP Type Traits
UNARY_EXPR_OR_TYPE_TRAIT(__builtin_omp_required_simd_align, OpenMPRequiredSimdAlign, KEYALL)
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Sema/SemaHLSL.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class SemaHLSL : public SemaBase {

// HLSL Type trait implementations
bool IsScalarizedLayoutCompatible(QualType T1, QualType T2) const;
bool IsIntangibleType(QualType T1);

bool CheckCompatibleParameterABI(FunctionDecl *New, FunctionDecl *Old);

Expand Down
17 changes: 16 additions & 1 deletion clang/lib/AST/DeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
ImplicitCopyAssignmentHasConstParam(true),
HasDeclaredCopyConstructorWithConstParam(false),
HasDeclaredCopyAssignmentWithConstParam(false),
IsAnyDestructorNoReturn(false), IsLambda(false),
IsAnyDestructorNoReturn(false), IsHLSLIntangible(false), IsLambda(false),
IsParsingBaseSpecifiers(false), ComputedVisibleConversions(false),
HasODRHash(false), Definition(D) {}

Expand Down Expand Up @@ -431,6 +431,9 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
if (BaseClassDecl->isAnyDestructorNoReturn())
data().IsAnyDestructorNoReturn = true;

if (BaseClassDecl->isHLSLIntangible())
data().IsHLSLIntangible = true;

// C++11 [class.copy]p18:
// The implicitly-declared copy assignment operator for a class X will
// have the form 'X& X::operator=(const X&)' if each direct base class B
Expand Down Expand Up @@ -1401,6 +1404,18 @@ void CXXRecordDecl::addedMember(Decl *D) {
// than subobjects of zero size
if (data().Empty && !IsZeroSize)
data().Empty = false;

if (getLangOpts().HLSL) {
const Type *Ty = Field->getType()->getUnqualifiedDesugaredType();
while (isa<ConstantArrayType>(Ty))
Ty = Ty->getArrayElementTypeNoTypeQual();

Ty = Ty->getUnqualifiedDesugaredType();
if (Ty->isBuiltinType())
data().IsHLSLIntangible |= Ty->isHLSLIntangibleType();
else if (const RecordType *RT = dyn_cast<RecordType>(Ty))
data().IsHLSLIntangible |= RT->getAsCXXRecordDecl()->isHLSLIntangible();
}
}

// Handle using declarations of conversion functions.
Expand Down
11 changes: 11 additions & 0 deletions clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5111,6 +5111,7 @@ static bool CheckUnaryTypeTraitTypeCompleteness(Sema &S, TypeTrait UTT,
case UTT_IsDestructible:
case UTT_IsNothrowDestructible:
case UTT_IsTriviallyDestructible:
case UTT_IsIntangibleType:
if (ArgTy->isIncompleteArrayType() || ArgTy->isVoidType())
return true;

Expand Down Expand Up @@ -5696,6 +5697,16 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
return true;
return false;
}
case UTT_IsIntangibleType:
assert(Self.getLangOpts().HLSL && "intangible types are HLSL-only feature");
if (!T->isVoidType() && !T->isIncompleteArrayType())
if (Self.RequireCompleteType(TInfo->getTypeLoc().getBeginLoc(), T,
diag::err_incomplete_type))
return false;
if (DiagnoseVLAInCXXTypeTrait(Self, TInfo,
tok::kw___builtin_hlsl_is_intangible))
return false;
return Self.HLSL().IsIntangibleType(T);
}
}

Expand Down
28 changes: 28 additions & 0 deletions clang/lib/Sema/SemaHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@

#include "clang/Sema/SemaHLSL.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/Expr.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/Type.h"
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/SourceLocation.h"
Expand Down Expand Up @@ -1609,6 +1612,31 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
return false;
}

bool SemaHLSL::IsIntangibleType(clang::QualType QT) {
if (QT.isNull())
return false;

const Type *Ty = QT->getUnqualifiedDesugaredType();

// check if it's a builtin type first (simple check, no need to cache it)
if (Ty->isBuiltinType())
return Ty->isHLSLIntangibleType();

// unwrap arrays
while (isa<ConstantArrayType>(Ty))
Ty = Ty->getArrayElementTypeNoTypeQual();

const RecordType *RT =
dyn_cast<RecordType>(Ty->getUnqualifiedDesugaredType());
if (!RT)
return false;

CXXRecordDecl *RD = RT->getAsCXXRecordDecl();
assert(RD != nullptr &&
"all HLSL struct and classes should be CXXRecordDecl");
return RD->isHLSLIntangible();
}

static void BuildFlattenedTypeList(QualType BaseTy,
llvm::SmallVectorImpl<QualType> &List) {
llvm::SmallVector<QualType, 16> WorkList;
Expand Down
78 changes: 78 additions & 0 deletions clang/test/SemaHLSL/Types/Traits/IsIntangibleType.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -verify %s
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -fnative-half-type -verify %s
// expected-no-diagnostics

_Static_assert(__builtin_hlsl_is_intangible(__hlsl_resource_t), "");
// no need to check array of __hlsl_resource_t, arrays of sizeless types are not supported

_Static_assert(!__builtin_hlsl_is_intangible(int), "");
_Static_assert(!__builtin_hlsl_is_intangible(float3), "");
_Static_assert(!__builtin_hlsl_is_intangible(half[4]), "");

typedef __hlsl_resource_t Res;
_Static_assert(__builtin_hlsl_is_intangible(const Res), "");
// no need to check array of Res, arrays of sizeless types are not supported

struct ABuffer {
const int i[10];
__hlsl_resource_t h;
};
_Static_assert(__builtin_hlsl_is_intangible(ABuffer), "");
_Static_assert(__builtin_hlsl_is_intangible(ABuffer[10]), "");

struct MyStruct {
half2 h2;
int3 i3;
};
_Static_assert(!__builtin_hlsl_is_intangible(MyStruct), "");
_Static_assert(!__builtin_hlsl_is_intangible(MyStruct[10]), "");

class MyClass {
int3 ivec;
float farray[12];
MyStruct ms;
ABuffer buf;
};
_Static_assert(__builtin_hlsl_is_intangible(MyClass), "");
_Static_assert(__builtin_hlsl_is_intangible(MyClass[2]), "");

union U {
double d[4];
Res buf;
};
_Static_assert(__builtin_hlsl_is_intangible(U), "");
_Static_assert(__builtin_hlsl_is_intangible(U[100]), "");

class MyClass2 {
int3 ivec;
float farray[12];
U u;
};
_Static_assert(__builtin_hlsl_is_intangible(MyClass2), "");
_Static_assert(__builtin_hlsl_is_intangible(MyClass2[5]), "");

class Simple {
int a;
};

template<typename T> struct TemplatedBuffer {
T a;
__hlsl_resource_t h;
};
_Static_assert(__builtin_hlsl_is_intangible(TemplatedBuffer<int>), "");

struct MyStruct2 : TemplatedBuffer<float> {
float x;
};
_Static_assert(__builtin_hlsl_is_intangible(MyStruct2), "");

struct MyStruct3 {
const TemplatedBuffer<float> TB[10];
};
_Static_assert(__builtin_hlsl_is_intangible(MyStruct3), "");

template<typename T> struct SimpleTemplate {
T a;
};
_Static_assert(__builtin_hlsl_is_intangible(SimpleTemplate<__hlsl_resource_t>), "");
_Static_assert(!__builtin_hlsl_is_intangible(SimpleTemplate<float>), "");
11 changes: 11 additions & 0 deletions clang/test/SemaHLSL/Types/Traits/IsIntangibleTypeErrors.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -verify %s

struct Undefined; // expected-note {{forward declaration of 'Undefined'}}
_Static_assert(!__builtin_hlsl_is_intangible(Undefined), ""); // expected-error{{incomplete type 'Undefined' used in type trait expression}}

void fn(int X) {
// expected-error@#vla {{variable length arrays are not supported for the current target}}
// expected-error@#vla {{variable length arrays are not supported in '__builtin_hlsl_is_intangible'}}
// expected-warning@#vla {{variable length arrays in C++ are a Clang extension}}
_Static_assert(!__builtin_hlsl_is_intangible(int[X]), ""); // #vla
}