Skip to content

Commit 6a01ac7

Browse files
authored
[HLSL] Add concepts for Structured buffers (#119643)
This PR adds concept validation to structured buffers, in the same way that it was done for typed buffers (like RWBuffer) in #116413. This PR should also be responsible for introducing rejection of 0 size elements for structured buffers. Fixes #117406
1 parent 0517772 commit 6a01ac7

File tree

5 files changed

+124
-16
lines changed

5 files changed

+124
-16
lines changed

clang/lib/AST/Type.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5118,7 +5118,7 @@ bool Type::isHLSLIntangibleType() const {
51185118

51195119
CXXRecordDecl *RD = RT->getAsCXXRecordDecl();
51205120
assert(RD != nullptr &&
5121-
"all HLSL struct and classes should be CXXRecordDecl");
5121+
"all HLSL structs and classes should be CXXRecordDecl");
51225122
assert(RD->isCompleteDefinition() && "expecting complete type");
51235123
return RD->isHLSLIntangible();
51245124
}

clang/lib/Sema/HLSLExternalSemaSource.cpp

Lines changed: 78 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -864,6 +864,10 @@ static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S,
864864
.addDefaultHandleConstructor();
865865
}
866866

867+
// This function is responsible for constructing the constraint expression for
868+
// this concept:
869+
// template<typename T> concept is_typed_resource_element_compatible =
870+
// __is_typed_resource_element_compatible<T>;
867871
static Expr *constructTypedBufferConstraintExpr(Sema &S, SourceLocation NameLoc,
868872
TemplateTypeParmDecl *T) {
869873
ASTContext &Context = S.getASTContext();
@@ -885,8 +889,58 @@ static Expr *constructTypedBufferConstraintExpr(Sema &S, SourceLocation NameLoc,
885889
return TypedResExpr;
886890
}
887891

888-
static ConceptDecl *constructTypedBufferConceptDecl(Sema &S,
889-
NamespaceDecl *NSD) {
892+
// This function is responsible for constructing the constraint expression for
893+
// this concept:
894+
// template<typename T> concept is_structured_resource_element_compatible =
895+
// !__is_intangible<T> && sizeof(T) >= 1;
896+
static Expr *constructStructuredBufferConstraintExpr(Sema &S,
897+
SourceLocation NameLoc,
898+
TemplateTypeParmDecl *T) {
899+
ASTContext &Context = S.getASTContext();
900+
901+
// Obtain the QualType for 'bool'
902+
QualType BoolTy = Context.BoolTy;
903+
904+
// Create a QualType that points to this TemplateTypeParmDecl
905+
QualType TType = Context.getTypeDeclType(T);
906+
907+
// Create a TypeSourceInfo for the template type parameter 'T'
908+
TypeSourceInfo *TTypeSourceInfo =
909+
Context.getTrivialTypeSourceInfo(TType, NameLoc);
910+
911+
TypeTraitExpr *IsIntangibleExpr =
912+
TypeTraitExpr::Create(Context, BoolTy, NameLoc, UTT_IsIntangibleType,
913+
{TTypeSourceInfo}, NameLoc, true);
914+
915+
// negate IsIntangibleExpr
916+
UnaryOperator *NotIntangibleExpr = UnaryOperator::Create(
917+
Context, IsIntangibleExpr, UO_LNot, BoolTy, VK_LValue, OK_Ordinary,
918+
NameLoc, false, FPOptionsOverride());
919+
920+
// element types also may not be of 0 size
921+
UnaryExprOrTypeTraitExpr *SizeOfExpr = new (Context) UnaryExprOrTypeTraitExpr(
922+
UETT_SizeOf, TTypeSourceInfo, BoolTy, NameLoc, NameLoc);
923+
924+
// Create a BinaryOperator that checks if the size of the type is not equal to
925+
// 1 Empty structs have a size of 1 in HLSL, so we need to check for that
926+
IntegerLiteral *rhs = IntegerLiteral::Create(
927+
Context, llvm::APInt(Context.getTypeSize(Context.getSizeType()), 1, true),
928+
Context.getSizeType(), NameLoc);
929+
930+
BinaryOperator *SizeGEQOneExpr =
931+
BinaryOperator::Create(Context, SizeOfExpr, rhs, BO_GE, BoolTy, VK_LValue,
932+
OK_Ordinary, NameLoc, FPOptionsOverride());
933+
934+
// Combine the two constraints
935+
BinaryOperator *CombinedExpr = BinaryOperator::Create(
936+
Context, NotIntangibleExpr, SizeGEQOneExpr, BO_LAnd, BoolTy, VK_LValue,
937+
OK_Ordinary, NameLoc, FPOptionsOverride());
938+
939+
return CombinedExpr;
940+
}
941+
942+
static ConceptDecl *constructBufferConceptDecl(Sema &S, NamespaceDecl *NSD,
943+
bool isTypedBuffer) {
890944
ASTContext &Context = S.getASTContext();
891945
DeclContext *DC = NSD->getDeclContext();
892946
SourceLocation DeclLoc = SourceLocation();
@@ -907,9 +961,18 @@ static ConceptDecl *constructTypedBufferConceptDecl(Sema &S,
907961
TemplateParameterList *ConceptParams = TemplateParameterList::Create(
908962
Context, DeclLoc, DeclLoc, {T}, DeclLoc, nullptr);
909963

910-
DeclarationName DeclName = DeclarationName(
911-
&Context.Idents.get("__is_typed_resource_element_compatible"));
912-
Expr *ConstraintExpr = constructTypedBufferConstraintExpr(S, DeclLoc, T);
964+
DeclarationName DeclName;
965+
Expr *ConstraintExpr = nullptr;
966+
967+
if (isTypedBuffer) {
968+
DeclName = DeclarationName(
969+
&Context.Idents.get("__is_typed_resource_element_compatible"));
970+
ConstraintExpr = constructTypedBufferConstraintExpr(S, DeclLoc, T);
971+
} else {
972+
DeclName = DeclarationName(
973+
&Context.Idents.get("__is_structured_resource_element_compatible"));
974+
ConstraintExpr = constructStructuredBufferConstraintExpr(S, DeclLoc, T);
975+
}
913976

914977
// Create a ConceptDecl
915978
ConceptDecl *CD =
@@ -927,8 +990,10 @@ static ConceptDecl *constructTypedBufferConceptDecl(Sema &S,
927990

928991
void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
929992
CXXRecordDecl *Decl;
930-
ConceptDecl *TypedBufferConcept =
931-
constructTypedBufferConceptDecl(*SemaPtr, HLSLNamespace);
993+
ConceptDecl *TypedBufferConcept = constructBufferConceptDecl(
994+
*SemaPtr, HLSLNamespace, /*isTypedBuffer*/ true);
995+
ConceptDecl *StructuredBufferConcept = constructBufferConceptDecl(
996+
*SemaPtr, HLSLNamespace, /*isTypedBuffer*/ false);
932997
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWBuffer")
933998
.addSimpleTemplateParams({"element_type"}, TypedBufferConcept)
934999
.finalizeForwardDeclaration();
@@ -944,7 +1009,7 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
9441009

9451010
Decl =
9461011
BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RasterizerOrderedBuffer")
947-
.addSimpleTemplateParams({"element_type"})
1012+
.addSimpleTemplateParams({"element_type"}, StructuredBufferConcept)
9481013
.finalizeForwardDeclaration();
9491014
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
9501015
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV,
@@ -956,7 +1021,7 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
9561021
});
9571022

9581023
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "StructuredBuffer")
959-
.addSimpleTemplateParams({"element_type"})
1024+
.addSimpleTemplateParams({"element_type"}, StructuredBufferConcept)
9601025
.finalizeForwardDeclaration();
9611026
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
9621027
setupBufferType(Decl, *SemaPtr, ResourceClass::SRV, ResourceKind::RawBuffer,
@@ -966,7 +1031,7 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
9661031
});
9671032

9681033
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWStructuredBuffer")
969-
.addSimpleTemplateParams({"element_type"})
1034+
.addSimpleTemplateParams({"element_type"}, StructuredBufferConcept)
9701035
.finalizeForwardDeclaration();
9711036
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
9721037
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::RawBuffer,
@@ -979,7 +1044,7 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
9791044

9801045
Decl =
9811046
BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "AppendStructuredBuffer")
982-
.addSimpleTemplateParams({"element_type"})
1047+
.addSimpleTemplateParams({"element_type"}, StructuredBufferConcept)
9831048
.finalizeForwardDeclaration();
9841049
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
9851050
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::RawBuffer,
@@ -990,7 +1055,7 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
9901055

9911056
Decl =
9921057
BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "ConsumeStructuredBuffer")
993-
.addSimpleTemplateParams({"element_type"})
1058+
.addSimpleTemplateParams({"element_type"}, StructuredBufferConcept)
9941059
.finalizeForwardDeclaration();
9951060
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
9961061
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::RawBuffer,
@@ -1001,7 +1066,7 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
10011066

10021067
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace,
10031068
"RasterizerOrderedStructuredBuffer")
1004-
.addSimpleTemplateParams({"element_type"})
1069+
.addSimpleTemplateParams({"element_type"}, StructuredBufferConcept)
10051070
.finalizeForwardDeclaration();
10061071
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
10071072
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::RawBuffer,

clang/test/AST/HLSL/StructuredBuffers-AST.hlsl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@
5050

5151
// EMPTY: ClassTemplateDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit [[RESOURCE]]
5252
// EMPTY-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> typename depth 0 index 0 element_type
53+
// EMPTY-NEXT: ConceptSpecializationExpr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> 'bool' Concept 0x{{[0-9A-Fa-f]+}} '__is_structured_resource_element_compatible'
54+
// EMPTY-NEXT: ImplicitConceptSpecializationDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc>
55+
// EMPTY-NEXT: TemplateArgument type 'type-parameter-0-0'
56+
// EMPTY-NEXT: TemplateTypeParmType 0x{{[0-9A-Fa-f]+}} 'type-parameter-0-0' dependent depth 0 index 0
57+
// EMPTY-NEXT: TemplateTypeParm 0x{{[0-9A-Fa-f]+}} ''
58+
// EMPTY-NEXT: TemplateArgument type 'element_type':'type-parameter-0-0'
59+
// EMPTY-NEXT: TemplateTypeParmType 0x{{[0-9A-Fa-f]+}} 'element_type' dependent depth 0 index 0
60+
// EMPTY-NEXT: TemplateTypeParm 0x{{[0-9A-Fa-f]+}} 'element_type'
5361
// EMPTY-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit <undeserialized declarations> class [[RESOURCE]]
5462
// EMPTY-NEXT: FinalAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit final
5563

@@ -64,6 +72,14 @@ RESOURCE<float> Buffer;
6472

6573
// CHECK: ClassTemplateDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit [[RESOURCE]]
6674
// CHECK-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> typename depth 0 index 0 element_type
75+
// CHECK-NEXT: ConceptSpecializationExpr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> 'bool' Concept 0x{{[0-9A-Fa-f]+}} '__is_structured_resource_element_compatible'
76+
// CHECK-NEXT: ImplicitConceptSpecializationDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc>
77+
// CHECK-NEXT: TemplateArgument type 'type-parameter-0-0'
78+
// CHECK-NEXT: TemplateTypeParmType 0x{{[0-9A-Fa-f]+}} 'type-parameter-0-0' dependent depth 0 index 0
79+
// CHECK-NEXT: TemplateTypeParm 0x{{[0-9A-Fa-f]+}} ''
80+
// CHECK-NEXT: TemplateArgument type 'element_type':'type-parameter-0-0'
81+
// CHECK-NEXT: TemplateTypeParmType 0x{{[0-9A-Fa-f]+}} 'element_type' dependent depth 0 index 0
82+
// CHECK-NEXT: TemplateTypeParm 0x{{[0-9A-Fa-f]+}} 'element_type'
6783
// CHECK-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit class [[RESOURCE]] definition
6884

6985
// CHECK: FinalAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit final
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -ast-dump -ast-dump-filter=__is_structured_resource_element_compatible %s | FileCheck %s
2+
3+
// CHECK: ConceptDecl 0x{{[0-9a-f]+}} <<invalid sloc>> <invalid sloc> __is_structured_resource_element_compatible
4+
// CHECK: |-TemplateTypeParmDecl 0x{{[0-9a-f]+}} <<invalid sloc>> <invalid sloc> referenced typename depth 0 index 0 element_type
5+
// CHECK: `-BinaryOperator 0x{{[0-9a-f]+}} <<invalid sloc>> 'bool' lvalue '&&'
6+
// CHECK: |-UnaryOperator 0x{{[0-9a-f]+}} <<invalid sloc>> 'bool' lvalue prefix '!' cannot overflow
7+
// CHECK: | `-TypeTraitExpr 0x{{[0-9a-f]+}} <<invalid sloc>> 'bool' __builtin_hlsl_is_intangible
8+
// CHECK: | `-TemplateTypeParmType 0x{{[0-9a-f]+}} 'element_type' dependent depth 0 index 0
9+
// CHECK: | `-TemplateTypeParm 0x{{[0-9a-f]+}} 'element_type'
10+
// CHECK: `-BinaryOperator 0x{{[0-9a-f]+}} <<invalid sloc>> 'bool' lvalue '>='
11+
// CHECK: |-UnaryExprOrTypeTraitExpr 0x{{[0-9a-f]+}} <<invalid sloc>> 'bool' sizeof 'element_type'
12+
// CHECK: `-IntegerLiteral 0x{{[0-9a-f]+}} <<invalid sloc>> 'unsigned long' 1
13+
14+
15+
StructuredBuffer<float> Buffer;

clang/test/SemaHLSL/BuiltIns/StructuredBuffers.hlsl

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,25 @@ typedef vector<float, 3> float3;
55
StructuredBuffer<float3> Buffer;
66

77
// expected-error@+2 {{class template 'StructuredBuffer' requires template arguments}}
8-
// expected-note@*:* {{template declaration from hidden source: template <typename element_type> class StructuredBuffer {}}}
8+
// expected-note@*:* {{template declaration from hidden source: template <typename element_type> requires __is_structured_resource_element_compatible<element_type> class StructuredBuffer {}}}
99
StructuredBuffer BufferErr1;
1010

1111
// expected-error@+2 {{too few template arguments for class template 'StructuredBuffer'}}
12-
// expected-note@*:* {{template declaration from hidden source: template <typename element_type> class StructuredBuffer {}}}
12+
// expected-note@*:* {{template declaration from hidden source: template <typename element_type> requires __is_structured_resource_element_compatible<element_type> class StructuredBuffer {}}}
1313
StructuredBuffer<> BufferErr2;
1414

15+
// test elements of 0 size
16+
// expected-error@+3{{constraints not satisfied for class template 'StructuredBuffer' [with element_type = int[0]]}}
17+
// expected-note@*:*{{because 'int[0]' does not satisfy '__is_structured_resource_element_compatible'}}
18+
// expected-note@*:*{{because 'sizeof(int[0]) >= 1UL' (0 >= 1) evaluated to false}}
19+
StructuredBuffer<int[0]> BufferErr3;
20+
21+
// In C++, empty structs do have a size of 1. So should HLSL.
22+
// The concept will accept empty structs as element types, despite it being unintuitive.
23+
struct Empty {};
24+
StructuredBuffer<Empty> BufferErr4;
25+
26+
1527
[numthreads(1,1,1)]
1628
void main() {
1729
(void)Buffer.__handle; // expected-error {{'__handle' is a private member of 'hlsl::StructuredBuffer<vector<float, 3>>'}}

0 commit comments

Comments
 (0)