diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -4065,6 +4065,13 @@ let Documentation = [InternalOnly]; } +def HLSLConstantBufferView : InheritableAttr { + let Spellings = []; + let Subjects = SubjectList<[Struct]>; + let LangOpts = [HLSL]; + let Documentation = [InternalOnly]; +} + def HLSLGroupSharedAddressSpace : TypeAttr { let Spellings = [Keyword<"groupshared">]; let Subjects = SubjectList<[Var]>; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11710,6 +11710,8 @@ "attribute %0 only applies to a field or parameter of type '%1'">; def err_hlsl_attr_invalid_ast_node : Error< "attribute %0 only applies to %1">; +def err_hlsl_typeintemplateargument_requires_struct : Error< + "%0 cannot be used as a type parameter where a struct is required">; def err_hlsl_numthreads_argument_oor : Error<"argument '%select{X|Y|Z}0' to numthreads attribute cannot exceed %1">; def err_hlsl_numthreads_invalid : Error<"total number of threads cannot exceed %0">; def err_hlsl_missing_numthreads : Error<"missing numthreads attribute for %0 shader entry">; diff --git a/clang/include/clang/Sema/HLSLExternalSemaSource.h b/clang/include/clang/Sema/HLSLExternalSemaSource.h --- a/clang/include/clang/Sema/HLSLExternalSemaSource.h +++ b/clang/include/clang/Sema/HLSLExternalSemaSource.h @@ -30,6 +30,7 @@ void defineHLSLVectorAlias(); void defineTrivialHLSLTypes(); + void defineConstantBufferView(); void forwardDeclareHLSLTypes(); void completeBufferType(CXXRecordDecl *Record); diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -13388,6 +13388,9 @@ CallExpr *TheCall); bool CheckLoongArchBuiltinFunctionCall(const TargetInfo &TI, unsigned BuiltinID, CallExpr *TheCall); + bool CheckHLSLConstantBufferView(TypeAliasDecl *Pattern, + TemplateArgumentListInfo &TemplateArgs, + SourceLocation TemplateLoc); bool SemaBuiltinVAStart(unsigned BuiltinID, CallExpr *TheCall); bool SemaBuiltinVAStartARMMicrosoft(CallExpr *Call); diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1701,6 +1701,11 @@ return; } + if (T->getAttrKind() == attr::HLSLConstantBufferView) { + OS << " [[hlsl::ConstantBufferView]]"; + return; + } + // The printing of the address_space attribute is handled by the qualifier // since it is still stored in the qualifier. Return early to prevent printing // this twice. diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -462,14 +462,66 @@ HLSLNamespace->addDecl(Template); } +void HLSLExternalSemaSource::defineConstantBufferView() { + // Add CBV as + // template + // using ConstantBuffer = T; + + // TypeAliasTemplateDecl ConstantBuf + //| |-TemplateTypeParmDecl referenced typename depth 0 index 0 T + //| `- TypeAliasDecl ConstantBuf 'T' + //| `- TemplateTypeParmType 0x273ef3655f0 'T' dependent depth 0 index 0 + //| `- TemplateTypeParm 0x273ef365568 'T' + ASTContext &AST = SemaPtr->getASTContext(); + + llvm::SmallVector TemplateParams; + + auto *TypeParam = TemplateTypeParmDecl::Create( + AST, HLSLNamespace, SourceLocation(), SourceLocation(), 0, 0, + &AST.Idents.get("element", tok::TokenKind::identifier), false, false); + + TemplateParams.emplace_back(TypeParam); + + auto *ParamList = + TemplateParameterList::Create(AST, SourceLocation(), SourceLocation(), + TemplateParams, SourceLocation(), nullptr); + + IdentifierInfo &II = AST.Idents.get("ConstantBuffer", tok::TokenKind::identifier); + + QualType AliasType = AST.getTemplateTypeParmType(0, 0, false, TypeParam); + // Use AttributedType to mark ConstantBufferView. + AliasType = + AST.getAttributedType(attr::HLSLConstantBufferView, AliasType, AliasType); + + auto *Record = TypeAliasDecl::Create(AST, HLSLNamespace, SourceLocation(), + SourceLocation(), &II, + AST.getTrivialTypeSourceInfo(AliasType)); + + Record->setImplicit(true); + + auto *Template = + TypeAliasTemplateDecl::Create(AST, HLSLNamespace, SourceLocation(), + Record->getIdentifier(), ParamList, Record); + + Record->setDescribedAliasTemplate(Template); + Template->setImplicit(true); + Template->setLexicalDeclContext(Record->getDeclContext()); + + + HLSLNamespace->addDecl(Template); +} + void HLSLExternalSemaSource::defineTrivialHLSLTypes() { defineHLSLVectorAlias(); + defineConstantBufferView(); + ResourceDecl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "Resource") .startDefinition() .addHandleMember(AccessSpecifier::AS_public) .completeDefinition() .Record; + } void HLSLExternalSemaSource::forwardDeclareHLSLTypes() { diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -3815,6 +3815,31 @@ return { FailedCond, Description }; } +bool Sema::CheckHLSLConstantBufferView(TypeAliasDecl *Pattern, + TemplateArgumentListInfo &TemplateArgs, + SourceLocation TemplateLoc) { + if (TemplateArgs.size() != 1) + return true; + QualType PatType = Pattern->getUnderlyingType(); + const auto *AttrTy = PatType->getAs(); + if (!AttrTy) + return true; + + if (AttrTy->getAttrKind() != attr::HLSLConstantBufferView) + return true; + // Make sure ConstantBufferView element type is record. + const TemplateArgument &Arg = TemplateArgs.arguments()[0].getArgument(); + if (Arg.getKind() == TemplateArgument::ArgKind::Type) { + const auto *RD = Arg.getAsType()->getAsCXXRecordDecl(); + if (!RD) { + Diag(TemplateLoc, diag::err_hlsl_typeintemplateargument_requires_struct) + << Arg.getAsType(); + return false; + } + } + return true; +} + QualType Sema::CheckTemplateIdType(TemplateName Name, SourceLocation TemplateLoc, TemplateArgumentListInfo &TemplateArgs) { @@ -3878,6 +3903,10 @@ if (Inst.isInvalid()) return QualType(); + if (getLangOpts().HLSL && + !CheckHLSLConstantBufferView(Pattern, TemplateArgs, TemplateLoc)) + return QualType(); + CanonType = SubstType(Pattern->getUnderlyingType(), TemplateArgLists, AliasTemplate->getLocation(), AliasTemplate->getDeclName()); diff --git a/clang/test/AST/HLSL/CBV.hlsl b/clang/test/AST/HLSL/CBV.hlsl new file mode 100644 --- /dev/null +++ b/clang/test/AST/HLSL/CBV.hlsl @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -fsyntax-only -ast-dump %s | FileCheck %s + +// CHECK:TypeAliasTemplateDecl 0x{{[0-9a-f]+}} <> implicit ConstantBuffer +// CHECK-NEXT:-TemplateTypeParmDecl 0x[[T:[0-9a-f]+]] <> class depth 0 index 0 element +// CHECK-NEXT:-TypeAliasDecl 0x{{[0-9a-f]+}} <> implicit ConstantBuffer 'element {{(.{2})}}hlsl::ConstantBufferView{{(.{2})}}':'element' +// CHECK-NEXT:-AttributedType 0x{{[0-9a-f]+}} 'element {{(.{2})}}hlsl::ConstantBufferView{{(.{2})}}' sugar dependent +// CHECK-NEXT: -TemplateTypeParmType 0x{{[0-9a-f]+}} 'element' dependent depth 0 index 0 +// CHECK-NEXT: -TemplateTypeParm 0x[[T]] 'element' + +// CHECK:-CXXRecordDecl 0x{{[0-9a-f]+}} col:8 implicit struct S +// CHECK-NEXT:-FieldDecl 0x[[A:[0-9a-f]+]] col:11 referenced a 'float' +struct S { + float a; +}; + +// CHECK:VarDecl 0x[[CB:[0-9a-f]+]] col:19 used CB 'ConstantBuffer':'S' +ConstantBuffer CB; + +float foo() { +// CHECK:ReturnStmt 0x{{[0-9a-f]+}} +// CHECK-NEXT:-ImplicitCastExpr 0x{{[0-9a-f]+}} 'float' +// CHECK-NEXT:-MemberExpr 0x{{[0-9a-f]+}} 'float' lvalue .a 0x[[A]] +// CHECK-NEXT:-DeclRefExpr 0x{{[0-9a-f]+}} 'ConstantBuffer':'S' lvalue Var 0x[[CB]] 'CB' 'ConstantBuffer' + return CB.a; +} diff --git a/clang/test/SemaHLSL/BuiltIns/CBV.hlsl b/clang/test/SemaHLSL/BuiltIns/CBV.hlsl new file mode 100644 --- /dev/null +++ b/clang/test/SemaHLSL/BuiltIns/CBV.hlsl @@ -0,0 +1,5 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -fsyntax-only -verify %s + +// expected-error@+2{{'float' cannot be used as a type parameter where a struct is required}} +// expected-note@+1{{in instantiation of template type alias 'ConstantBuffer' requested here}} +ConstantBuffer CB;