diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 005d81b5b9282..f07d8ebb75035 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -691,6 +691,7 @@ KEYWORD(column_major , KEYHLSL) TYPE_TRAIT_2(__builtin_hlsl_is_scalarized_layout_compatible, IsScalarizedLayoutCompatible, KEYHLSL) TYPE_TRAIT_1(__builtin_hlsl_is_intangible, IsIntangibleType, KEYHLSL) TYPE_TRAIT_1(__builtin_hlsl_is_typed_resource_element_compatible, IsTypedResourceElementCompatible, KEYHLSL) +TYPE_TRAIT_1(__builtin_hlsl_is_constant_buffer_element_compatible, IsConstantBufferElementCompatible, KEYHLSL) // OpenMP Type Traits UNARY_EXPR_OR_TYPE_TRAIT(__builtin_omp_required_simd_align, OpenMPRequiredSimdAlign, KEYALL) diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h index 68c2f209976c4..e65de5d4aa4c3 100644 --- a/clang/include/clang/Sema/SemaHLSL.h +++ b/clang/include/clang/Sema/SemaHLSL.h @@ -216,6 +216,7 @@ class SemaHLSL : public SemaBase { // HLSL Type trait implementations bool IsScalarizedLayoutCompatible(QualType T1, QualType T2) const; bool IsTypedResourceElementCompatible(QualType T1); + bool IsConstantBufferElementCompatible(QualType T1); bool CheckCompatibleParameterABI(FunctionDecl *New, FunctionDecl *Old); diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index 9769eee10ae2f..449b32a215631 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -365,6 +365,32 @@ static Expr *constructTypedBufferConstraintExpr(Sema &S, SourceLocation NameLoc, return TypedResExpr; } +// This function is responsible for constructing the constraint expression for +// this concept: +// template concept is_constant_buffer_element_compatible = +// std::is_class_v && !__is_intangible(T); +static Expr *constructConstantBufferConstraintExpr(Sema &S, + SourceLocation NameLoc, + TemplateTypeParmDecl *T) { + ASTContext &Context = S.getASTContext(); + + // Obtain the QualType for 'bool' + QualType BoolTy = Context.BoolTy; + + // Create a QualType that points to this TemplateTypeParmDecl + QualType TType = Context.getTypeDeclType(T); + + // Create a TypeSourceInfo for the template type parameter 'T' + TypeSourceInfo *TTypeSourceInfo = + Context.getTrivialTypeSourceInfo(TType, NameLoc); + + TypeTraitExpr *ResExpr = TypeTraitExpr::Create( + Context, BoolTy, NameLoc, UTT_IsConstantBufferElementCompatible, + {TTypeSourceInfo}, NameLoc, true); + + return ResExpr; +} + // This function is responsible for constructing the constraint expression for // this concept: // template concept is_structured_resource_element_compatible = @@ -415,8 +441,10 @@ static Expr *constructStructuredBufferConstraintExpr(Sema &S, return CombinedExpr; } +enum class HLSLBufferType { Typed, Structured, Constant }; + static ConceptDecl *constructBufferConceptDecl(Sema &S, NamespaceDecl *NSD, - bool isTypedBuffer) { + HLSLBufferType BT) { ASTContext &Context = S.getASTContext(); DeclContext *DC = NSD->getDeclContext(); SourceLocation DeclLoc = SourceLocation(); @@ -440,14 +468,22 @@ static ConceptDecl *constructBufferConceptDecl(Sema &S, NamespaceDecl *NSD, DeclarationName DeclName; Expr *ConstraintExpr = nullptr; - if (isTypedBuffer) { + switch (BT) { + case HLSLBufferType::Typed: DeclName = DeclarationName( &Context.Idents.get("__is_typed_resource_element_compatible")); ConstraintExpr = constructTypedBufferConstraintExpr(S, DeclLoc, T); - } else { + break; + case HLSLBufferType::Structured: DeclName = DeclarationName( &Context.Idents.get("__is_structured_resource_element_compatible")); ConstraintExpr = constructStructuredBufferConstraintExpr(S, DeclLoc, T); + break; + case HLSLBufferType::Constant: + DeclName = DeclarationName( + &Context.Idents.get("__is_constant_buffer_element_compatible")); + ConstraintExpr = constructConstantBufferConstraintExpr(S, DeclLoc, T); + break; } // Create a ConceptDecl @@ -468,12 +504,14 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { ASTContext &AST = SemaPtr->getASTContext(); CXXRecordDecl *Decl; ConceptDecl *TypedBufferConcept = constructBufferConceptDecl( - *SemaPtr, HLSLNamespace, /*isTypedBuffer*/ true); + *SemaPtr, HLSLNamespace, HLSLBufferType::Typed); ConceptDecl *StructuredBufferConcept = constructBufferConceptDecl( - *SemaPtr, HLSLNamespace, /*isTypedBuffer*/ false); + *SemaPtr, HLSLNamespace, HLSLBufferType::Structured); + ConceptDecl *ConstantBufferConcept = constructBufferConceptDecl( + *SemaPtr, HLSLNamespace, HLSLBufferType::Constant); Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "ConstantBuffer") - .addSimpleTemplateParams({"element_type"}) + .addSimpleTemplateParams({"element_type"}, ConstantBufferConcept) .finalizeForwardDeclaration(); onCompletion(Decl, [this](CXXRecordDecl *Decl) { diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 594a18f0b8c78..4a7df5b4266f6 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -4700,6 +4700,19 @@ static void BuildFlattenedTypeList(QualType BaseTy, } } +bool SemaHLSL::IsConstantBufferElementCompatible(clang::QualType QT) { + if (QT.isNull()) + return false; + + // Must be a class/struct. + const auto *RD = QT->getAsCXXRecordDecl(); + if (!RD || RD->isUnion()) + return false; + + // Cannot be a resource type or contain one. + return !QT->isHLSLIntangibleType(); +} + bool SemaHLSL::IsTypedResourceElementCompatible(clang::QualType QT) { // null and array types are not allowed. if (QT.isNull() || QT->isArrayType()) diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp index a94a59e8add7b..c79b3f7045ca6 100644 --- a/clang/lib/Sema/SemaTypeTraits.cpp +++ b/clang/lib/Sema/SemaTypeTraits.cpp @@ -367,6 +367,7 @@ static bool CheckUnaryTypeTraitTypeCompleteness(Sema &S, TypeTrait UTT, case UTT_IsCompound: case UTT_IsMemberPointer: case UTT_IsTypedResourceElementCompatible: + case UTT_IsConstantBufferElementCompatible: // Fall-through // These traits are modeled on type predicates in C++0x [meta.unary.prop] @@ -1131,6 +1132,14 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT, return false; return Self.HLSL().IsTypedResourceElementCompatible(T); + + case UTT_IsConstantBufferElementCompatible: + assert(Self.getLangOpts().HLSL && + "constant buffer element compatible types are an HLSL-only feature"); + if (T->isIncompleteType()) + return false; + + return Self.HLSL().IsConstantBufferElementCompatible(T); } } diff --git a/clang/test/SemaHLSL/BuiltIns/ConstantBuffers.hlsl b/clang/test/SemaHLSL/BuiltIns/ConstantBuffers.hlsl deleted file mode 100644 index 10c65031b79f2..0000000000000 --- a/clang/test/SemaHLSL/BuiltIns/ConstantBuffers.hlsl +++ /dev/null @@ -1,35 +0,0 @@ -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -x hlsl -finclude-default-header -fsyntax-only -verify %s - -struct S { // expected-note 3 {{candidate constructor}} - float a; - int b; -}; - -struct Empty {}; - -struct ContainsResource { - Texture2D tex; -}; - -union U { - float a; - int b; -}; - -// Valid -ConstantBuffer cb; -ConstantBuffer cb_empty; - -void takes_inout_s(inout S s) {} - -void foo() { - // This case should fail because we cannot writeback to `cb` after the call. - // expected-error@+1 {{no viable constructor copying parameter of type 'const hlsl_constant S'}} - takes_inout_s(cb); -} - -void test_direct_assignment() { - // expected-error@+2 {{cannot assign to return value because function 'operator const hlsl_constant S &' returns a const value}} - // expected-note@* {{function 'operator const hlsl_constant S &' which returns const-qualified type 'const hlsl_constant S &' declared here}} - cb.a = 5.0; -} diff --git a/clang/test/SemaHLSL/Resources/ConstantBuffers.hlsl b/clang/test/SemaHLSL/Resources/ConstantBuffers.hlsl new file mode 100644 index 0000000000000..0ef3ada50c988 --- /dev/null +++ b/clang/test/SemaHLSL/Resources/ConstantBuffers.hlsl @@ -0,0 +1,74 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -x hlsl -finclude-default-header -fsyntax-only -verify %s + +struct S { // expected-note 3 {{candidate constructor}} + float a; + int b; +}; + +struct Empty {}; + +struct ContainsResource { + Texture2D tex; +}; + +union U { + float a; + int b; +}; + +// Valid +ConstantBuffer cb; +ConstantBuffer cb_empty; + +// Invalid: non-struct/class +// expected-error@+1 {{constraints not satisfied for class template 'ConstantBuffer'}} +ConstantBuffer cb_float; +// expected-note@* {{because 'float' does not satisfy '__is_constant_buffer_element_compatible'}} +// expected-note@* {{because '__builtin_hlsl_is_constant_buffer_element_compatible(float)' evaluated to false}} + +// expected-error@+1 {{constraints not satisfied for class template 'ConstantBuffer'}} +ConstantBuffer cb_float4; +// expected-note@* {{because 'float4' (aka 'vector') does not satisfy '__is_constant_buffer_element_compatible'}} +// expected-note@* {{because '__builtin_hlsl_is_constant_buffer_element_compatible(vector)' evaluated to false}} + +// expected-error@+1 {{constraints not satisfied for class template 'ConstantBuffer'}} +ConstantBuffer cb_array; +// expected-note@* {{because 'float[4]' does not satisfy '__is_constant_buffer_element_compatible'}} +// expected-note@* {{because '__builtin_hlsl_is_constant_buffer_element_compatible(float[4])' evaluated to false}} + +// Invalid: contains resource +// expected-error@+1 {{constraints not satisfied for class template 'ConstantBuffer'}} +ConstantBuffer cb_res; +// expected-note@* {{because 'ContainsResource' does not satisfy '__is_constant_buffer_element_compatible'}} +// expected-note@* {{because '__builtin_hlsl_is_constant_buffer_element_compatible(ContainsResource)' evaluated to false}} + +// Invalid: intangible type +// expected-error@+1 {{use of class template 'Texture2D' requires template arguments}} +ConstantBuffer cb_tex; +// expected-note@* {{template declaration from hidden source}} + +// Invalid: intangible type +// expected-error@+1 {{constraints not satisfied for class template 'ConstantBuffer'}} +ConstantBuffer> cb_tex; +// expected-note@* {{because 'Texture2D' does not satisfy '__is_constant_buffer_element_compatible'}} +// expected-note@*:* {{because '__builtin_hlsl_is_constant_buffer_element_compatible(hlsl::Texture2D)' evaluated to false}} + +// Invalid: union +// expected-error@+1 {{constraints not satisfied for class template 'ConstantBuffer'}} +ConstantBuffer cb_union; +// expected-note@* {{because 'U' does not satisfy '__is_constant_buffer_element_compatible'}} +// expected-note@* {{because '__builtin_hlsl_is_constant_buffer_element_compatible(U)' evaluated to false}} + +void takes_inout_s(inout S s) {} + +void foo() { + // This case should fail because we cannot writeback to `cb` after the call. + // expected-error@+1 {{no viable constructor copying parameter of type 'const hlsl_constant S'}} + takes_inout_s(cb); +} + +void test_direct_assignment() { + // expected-error@+2 {{cannot assign to return value because function 'operator const hlsl_constant S &' returns a const value}} + // expected-note@* {{function 'operator const hlsl_constant S &' which returns const-qualified type 'const hlsl_constant S &' declared here}} + cb.a = 5.0; +}