Skip to content
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

[HLSL] Add implicit resource element type concepts to AST #112600

Merged
merged 27 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
ab60544
ConceptSpecializationExpr shows up in AST!!!
bob80905 Oct 10, 2024
b43675f
CSE is in the right spot in the AST
bob80905 Oct 10, 2024
4307e45
templateArg is aligned correctly on the AST
bob80905 Oct 11, 2024
4beb3a3
template arguments are in the right spot!
bob80905 Oct 15, 2024
98200c0
template arguments are almost done
bob80905 Oct 15, 2024
f70fb48
IT WORKS! updated template arg name, just need to remove extraneous t…
bob80905 Oct 15, 2024
54917b1
break down constraint expression into simpler function
bob80905 Oct 16, 2024
6ebe14a
remove useless function
bob80905 Oct 16, 2024
1ecd544
some variable renaming, function rearranging
bob80905 Oct 16, 2024
19664f5
clang format
bob80905 Oct 16, 2024
ea0ac08
fix / add test cases
bob80905 Oct 16, 2024
d220a73
add default nullptr, remove comments
bob80905 Oct 17, 2024
8014a47
remove unused var, use vector to extend memory lifetime, may need to …
bob80905 Oct 17, 2024
8516483
address everything
bob80905 Oct 18, 2024
defc84e
remove clang::, add c++ ast generation code, fix var names
bob80905 Oct 18, 2024
b373f15
undo formatting problem
bob80905 Oct 18, 2024
4fecdc4
get rid of clang:: specifier for enum values
bob80905 Oct 18, 2024
80d2d25
change some function names
bob80905 Oct 18, 2024
d770236
clang format
bob80905 Oct 18, 2024
1159b02
remove referenced, update tests
bob80905 Oct 31, 2024
cf1a7fd
address Chris
bob80905 Nov 5, 2024
17696d8
fix typo
bob80905 Nov 5, 2024
6edf031
address Helena
bob80905 Nov 7, 2024
fae51c5
address damyan, clarify comments
bob80905 Nov 7, 2024
1722578
nfc to kick off bots again
bob80905 Nov 8, 2024
f50b917
clarify comment, add concept AST test
bob80905 Nov 8, 2024
47f106b
use namespace decl as context
bob80905 Nov 13, 2024
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
217 changes: 205 additions & 12 deletions clang/lib/Sema/HLSLExternalSemaSource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -300,8 +300,9 @@ struct BuiltinTypeDeclBuilder {
}

TemplateParameterListBuilder addTemplateArgumentList(Sema &S);
BuiltinTypeDeclBuilder &addSimpleTemplateParams(Sema &S,
ArrayRef<StringRef> Names);
BuiltinTypeDeclBuilder &
addSimpleTemplateParams(Sema &S, ArrayRef<StringRef> Names, ConceptDecl *CD);
BuiltinTypeDeclBuilder &addConceptSpecializationExpr(Sema &S);
};

struct TemplateParameterListBuilder {
Expand All @@ -323,30 +324,129 @@ struct TemplateParameterListBuilder {
S.Context, Builder.Record->getDeclContext(), SourceLocation(),
SourceLocation(), /* TemplateDepth */ 0, Position,
&S.Context.Idents.get(Name, tok::TokenKind::identifier),
/* Typename */ false,
/* ParameterPack */ false);
/* Typename */ true,
/* ParameterPack */ false,
/* HasTypeConstraint*/ false);
if (!DefaultValue.isNull())
Decl->setDefaultArgument(
S.Context, S.getTrivialTemplateArgumentLoc(DefaultValue, QualType(),
SourceLocation()));

Params.emplace_back(Decl);
return *this;
}

BuiltinTypeDeclBuilder &finalizeTemplateArgs() {
/*
The concept specialization expression (CSE) constructed in
constructConceptSpecializationExpr is constructed so that it
matches the CSE that is constructed when parsing the below C++ code:

template<typename T>
concept is_typed_resource_element_compatible = sizeof(T) <= 16;

template<typename element_type> requires
is_typed_resource_element_compatible<element_type>
struct RWBuffer {
element_type Val;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that the code in this comment really matches the AST that's being built.

RWBuffer doesn't have a member of type element_type, for example.

I think that this function is just building up the AST that corresponds to the requires is_typed_resource_element_compatible<element_Type> part?

The AST nodes for template<typename T> concept is_typed_resource_element_compatible =sizeof(T) <= 16;
itself is created in constructTypedBufferConceptDecl?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that when parsing the C++ code in the comment, it produces more of the AST than the function is producing, but I still believe that including that extra context in the comments is helpful. The C++ code adds the structure "RWBuffer", and though it isn't being produced by constructConceptSpecializationExpr, it helps to know what code can be copy pasted into godbolt, for example, to see the AST that would be produced.
I had originally wanted to paste the AST that would be produced, but figured getting the source code would help explain the code better and also allow those who are interested to get the AST from the code.

The first point of the comment says that
"The concept specialization expression (CSE) constructed below is constructed
so that it matches the CSE that is constructed when parsing
the below C++ code:"
Which is still accurate. The code in the function isn't claiming to be responsible for the whole AST. I will reword it slightly for more clarity.

For your last 2 questions, yes I think your statements are accurate.

};

int fn() {
RWBuffer<int> Buf;
}

When dumping the AST and filtering for "RWBuffer", the resulting AST
structure is what we're trying to construct below, specifically the
CSE portion.
*/
ConceptSpecializationExpr *
constructConceptSpecializationExpr(Sema &S, ConceptDecl *CD) {
ASTContext &Context = S.getASTContext();
SourceLocation Loc = Builder.Record->getBeginLoc();
DeclarationNameInfo DNI(CD->getDeclName(), Loc);
NestedNameSpecifierLoc NNSLoc;
DeclContext *DC = Builder.Record->getDeclContext();
TemplateArgumentListInfo TALI(Loc, Loc);

// Assume that the concept decl has just one template parameter
// This parameter should have been added when CD was constructed
// in getTypedBufferConceptDecl
assert(CD->getTemplateParameters()->size() == 1 &&
"unexpected concept decl parameter count");
TemplateTypeParmDecl *ConceptTTPD = dyn_cast<TemplateTypeParmDecl>(
CD->getTemplateParameters()->getParam(0));

// this TemplateTypeParmDecl is the template for the resource, and is
// used to construct a template argumentthat will be used
// to construct the ImplicitConceptSpecializationDecl
TemplateTypeParmDecl *T = TemplateTypeParmDecl::Create(
Context, // AST context
Builder.Record->getDeclContext(), // DeclContext
SourceLocation(), SourceLocation(),
/*depth=*/0, // Depth in the template parameter list
/*position=*/0, // Position in the template parameter list
/*id=*/nullptr, // Identifier for 'T'
/*Typename=*/true, // Indicates this is a 'typename' or 'class'
/*ParameterPack=*/false, // Not a parameter pack
/*HasTypeConstraint=*/false // Has no type constraint
);

T->setDeclContext(DC);

QualType ConceptTType = Context.getTypeDeclType(ConceptTTPD);

// this is the 2nd template argument node, on which
// the concept constraint is actually being applied: 'element_type'
TemplateArgument ConceptTA = TemplateArgument(ConceptTType);

QualType CSETType = Context.getTypeDeclType(T);

// this is the 1st template argument node, which represents
// the abstract type that a concept would refer to: 'T'
TemplateArgument CSETA = TemplateArgument(CSETType);

ImplicitConceptSpecializationDecl *ImplicitCSEDecl =
ImplicitConceptSpecializationDecl::Create(
Context, Builder.Record->getDeclContext(), Loc, {CSETA});

// Constraint satisfaction is used to construct the
// ConceptSpecailizationExpr, and represents the 2nd Template Argument,
// located at the bottom of the sample AST above.
const ConstraintSatisfaction CS(CD, {ConceptTA});
TemplateArgumentLoc TAL = S.getTrivialTemplateArgumentLoc(
ConceptTA, QualType(), SourceLocation());

TALI.addArgument(TAL);
const ASTTemplateArgumentListInfo *ATALI =
ASTTemplateArgumentListInfo::Create(Context, TALI);

// In the concept reference, ATALI is what adds the extra
// TemplateArgument node underneath CSE
ConceptReference *CR =
ConceptReference::Create(Context, NNSLoc, Loc, DNI, CD, CD, ATALI);

ConceptSpecializationExpr *CSE =
ConceptSpecializationExpr::Create(Context, CR, ImplicitCSEDecl, &CS);

return CSE;
}

BuiltinTypeDeclBuilder &finalizeTemplateArgs(ConceptDecl *CD = nullptr) {
if (Params.empty())
return Builder;
ConceptSpecializationExpr *CSE =
CD ? constructConceptSpecializationExpr(S, CD) : nullptr;

auto *ParamList = TemplateParameterList::Create(S.Context, SourceLocation(),
SourceLocation(), Params,
SourceLocation(), nullptr);
SourceLocation(), CSE);
Builder.Template = ClassTemplateDecl::Create(
S.Context, Builder.Record->getDeclContext(), SourceLocation(),
DeclarationName(Builder.Record->getIdentifier()), ParamList,
Builder.Record);

Builder.Record->setDescribedClassTemplate(Builder.Template);
Builder.Template->setImplicit(true);
Builder.Template->setLexicalDeclContext(Builder.Record->getDeclContext());

// NOTE: setPreviousDecl before addDecl so new decl replace old decl when
// make visible.
Builder.Template->setPreviousDecl(Builder.PrevTemplate);
Expand All @@ -366,13 +466,13 @@ BuiltinTypeDeclBuilder::addTemplateArgumentList(Sema &S) {
return TemplateParameterListBuilder(S, *this);
}

BuiltinTypeDeclBuilder &
BuiltinTypeDeclBuilder::addSimpleTemplateParams(Sema &S,
ArrayRef<StringRef> Names) {
BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addSimpleTemplateParams(
Sema &S, ArrayRef<StringRef> Names, ConceptDecl *CD = nullptr) {
TemplateParameterListBuilder Builder = this->addTemplateArgumentList(S);
for (StringRef Name : Names)
Builder.addTypeParameter(Name);
return Builder.finalizeTemplateArgs();

return Builder.finalizeTemplateArgs(CD);
}

HLSLExternalSemaSource::~HLSLExternalSemaSource() {}
Expand Down Expand Up @@ -483,10 +583,103 @@ static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S,
.addDefaultHandleConstructor(S, RC);
}

BinaryOperator *constructSizeOfLEQ16Expr(ASTContext &Context,
SourceLocation NameLoc,
TemplateTypeParmDecl *T) {
// Obtain the QualType for 'unsigned long'
QualType UnsignedLongType = Context.UnsignedLongTy;

// 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);

UnaryExprOrTypeTraitExpr *sizeOfExpr = new (Context) UnaryExprOrTypeTraitExpr(
UETT_SizeOf, TTypeSourceInfo, UnsignedLongType, NameLoc, NameLoc);

// Create an IntegerLiteral for the value '16' with size type
QualType SizeType = Context.getSizeType();
llvm::APInt SizeValue = llvm::APInt(Context.getTypeSize(SizeType), 16);
IntegerLiteral *SizeLiteral =
new (Context) IntegerLiteral(Context, SizeValue, SizeType, NameLoc);

QualType BoolTy = Context.BoolTy;

BinaryOperator *binaryOperator =
BinaryOperator::Create(Context, sizeOfExpr, // Left-hand side expression
SizeLiteral, // Right-hand side expression
BO_LE, // Binary operator kind (<=)
BoolTy, // Result type (bool)
VK_LValue, // Value kind
OK_Ordinary, // Object kind
NameLoc, // Source location of operator
FPOptionsOverride());

return binaryOperator;
}

Expr *constructTypedBufferConstraintExpr(Sema &S, SourceLocation NameLoc,
TemplateTypeParmDecl *T) {
ASTContext &Context = S.getASTContext();

// first get the "sizeof(T) <= 16" expression, as a binary operator
BinaryOperator *SizeOfLEQ16 = constructSizeOfLEQ16Expr(Context, NameLoc, T);
// TODO: add the 'builtin_hlsl_is_typed_resource_element_compatible' builtin
// and return a binary operator that evaluates the builtin on the given
// template type parameter 'T'.
// Defined in issue https://github.com/llvm/llvm-project/issues/113223
return SizeOfLEQ16;
}

ConceptDecl *constructTypedBufferConceptDecl(Sema &S, NamespaceDecl *NSD) {
ASTContext &Context = S.getASTContext();
DeclContext *DC = NSD->getDeclContext();
SourceLocation DeclLoc = SourceLocation();

IdentifierInfo &ElementTypeII = Context.Idents.get("element_type");
TemplateTypeParmDecl *T = TemplateTypeParmDecl::Create(
Context, NSD->getDeclContext(), DeclLoc, DeclLoc,
/*depth=*/0,
/*position=*/0,
/*id=*/&ElementTypeII,
/*Typename=*/true,
/*ParameterPack=*/false);

T->setDeclContext(DC);
T->setReferenced();

// Create and Attach Template Parameter List to ConceptDecl
TemplateParameterList *ConceptParams = TemplateParameterList::Create(
Context, DeclLoc, DeclLoc, {T}, DeclLoc, nullptr);

DeclarationName DeclName = DeclarationName(
&Context.Idents.get("__is_typed_resource_element_compatible"));
Expr *ConstraintExpr = constructTypedBufferConstraintExpr(S, DeclLoc, T);

// Create a ConceptDecl
ConceptDecl *CD =
ConceptDecl::Create(Context, NSD->getDeclContext(), DeclLoc, DeclName,
ConceptParams, ConstraintExpr);

// Attach the template parameter list to the ConceptDecl
CD->setTemplateParameters(ConceptParams);

// Add the concept declaration to the Translation Unit Decl
NSD->getDeclContext()->addDecl(CD);

return CD;
}

void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
CXXRecordDecl *Decl;
ConceptDecl *TypedBufferConcept =
constructTypedBufferConceptDecl(*SemaPtr, HLSLNamespace);

Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWBuffer")
.addSimpleTemplateParams(*SemaPtr, {"element_type"})
.addSimpleTemplateParams(*SemaPtr, {"element_type"},
TypedBufferConcept)
.Record;

onCompletion(Decl, [this](CXXRecordDecl *Decl) {
Expand Down
20 changes: 18 additions & 2 deletions clang/test/AST/HLSL/RWBuffer-AST.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,15 @@
// instantiated specialization.

// EMPTY: ClassTemplateDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit RWBuffer
// EMPTY-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> class depth 0 index 0 element_type
// EMPTY-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> typename depth 0 index 0 element_type
// EMPTY-NEXT: ConceptSpecializationExpr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> 'bool' Concept 0x{{[0-9A-Fa-f]+}} '__is_typed_resource_element_compatible'
// EMPTY-NEXT: ImplicitConceptSpecializationDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc>
// EMPTY-NEXT: TemplateArgument type 'type-parameter-0-0'
// EMPTY-NEXT: TemplateTypeParmType 0x{{[0-9A-Fa-f]+}} 'type-parameter-0-0' dependent depth 0 index 0
// EMPTY-NEXT: TemplateTypeParm 0x{{[0-9A-Fa-f]+}} ''
// EMPTY-NEXT: TemplateArgument type 'element_type':'type-parameter-0-0'
// EMPTY-NEXT: TemplateTypeParmType 0x{{[0-9A-Fa-f]+}} 'element_type' dependent depth 0 index 0
// EMPTY-NEXT: TemplateTypeParm 0x{{[0-9A-Fa-f]+}} 'element_type'
// EMPTY-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit <undeserialized declarations> class RWBuffer
// EMPTY-NEXT: FinalAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit final

Expand All @@ -25,7 +33,15 @@ RWBuffer<float> Buffer;
#endif

// CHECK: ClassTemplateDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit RWBuffer
// CHECK-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> class depth 0 index 0 element_type
// CHECK-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> typename depth 0 index 0 element_type
// CHECK-NEXT: ConceptSpecializationExpr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> 'bool' Concept 0x{{[0-9A-Fa-f]+}} '__is_typed_resource_element_compatible'
// CHECK-NEXT: ImplicitConceptSpecializationDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc>
// CHECK-NEXT: TemplateArgument type 'type-parameter-0-0'
// CHECK-NEXT: TemplateTypeParmType 0x{{[0-9A-Fa-f]+}} 'type-parameter-0-0' dependent depth 0 index 0
// CHECK-NEXT: TemplateTypeParm 0x{{[0-9A-Fa-f]+}} ''
// CHECK-NEXT: TemplateArgument type 'element_type':'type-parameter-0-0'
// CHECK-NEXT: TemplateTypeParmType 0x{{[0-9A-Fa-f]+}} 'element_type' dependent depth 0 index 0
// CHECK-NEXT: TemplateTypeParm 0x{{[0-9A-Fa-f]+}} 'element_type'
// CHECK-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit class RWBuffer definition

// CHECK: FinalAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit final
Expand Down
4 changes: 2 additions & 2 deletions clang/test/AST/HLSL/StructuredBuffer-AST.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// instantiated specialization.

// EMPTY: ClassTemplateDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit StructuredBuffer
// EMPTY-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> class depth 0 index 0 element_type
// EMPTY-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> typename depth 0 index 0 element_type
// EMPTY-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit <undeserialized declarations> class StructuredBuffer
// EMPTY-NEXT: FinalAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit final

Expand All @@ -26,7 +26,7 @@ StructuredBuffer<float> Buffer;
#endif

// CHECK: ClassTemplateDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit StructuredBuffer
// CHECK-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> class depth 0 index 0 element_type
// CHECK-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> typename depth 0 index 0 element_type
// CHECK-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit class StructuredBuffer definition

// CHECK: FinalAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit final
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -ast-dump -ast-dump-filter=__is_typed_resource_element_compatible %s | FileCheck %s

// CHECK: ConceptDecl 0x{{[0-9a-f]+}} <<invalid sloc>> <invalid sloc> __is_typed_resource_element_compatible
// CHECK: |-TemplateTypeParmDecl 0x{{[0-9a-f]+}} <<invalid sloc>> <invalid sloc> referenced typename depth 0 index 0 element_type
// CHECK: `-BinaryOperator 0x{{[0-9a-f]+}} <<invalid sloc>> 'bool' lvalue '<='
// CHECK: |-UnaryExprOrTypeTraitExpr 0x{{[0-9a-f]+}} <<invalid sloc>> 'unsigned long' sizeof 'element_type'
// CHECK: `-IntegerLiteral 0x{{[0-9a-f]+}} <<invalid sloc>> 'unsigned long' 16


RWBuffer<float> Buffer;
15 changes: 13 additions & 2 deletions clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,24 @@ typedef vector<float, 3> float3;
RWBuffer<float3> Buffer;

// expected-error@+2 {{class template 'RWBuffer' requires template arguments}}
// expected-note@*:* {{template declaration from hidden source: template <class element_type> class RWBuffer}}
// expected-note@*:* {{template declaration from hidden source: template <typename element_type> requires __is_typed_resource_element_compatible<element_type> class RWBuffer {}}}
RWBuffer BufferErr1;

// expected-error@+2 {{too few template arguments for class template 'RWBuffer'}}
// expected-note@*:* {{template declaration from hidden source: template <class element_type> class RWBuffer}}
// expected-note@*:* {{template declaration from hidden source: template <typename element_type> requires __is_typed_resource_element_compatible<element_type> class RWBuffer {}}}
RWBuffer<> BufferErr2;

struct threeDoubles {
double a;
double b;
double c;
};

// expected-error@+3 {{constraints not satisfied for class template 'RWBuffer'}}
// expected-note@*:* {{because 'threeDoubles' does not satisfy '__is_typed_resource_element_compatible'}}
// expected-note@*:* {{because 'sizeof(threeDoubles) <= 16UL' (24 <= 16) evaluated to false}}
RWBuffer<threeDoubles> BufferErr3;

[numthreads(1,1,1)]
void main() {
(void)Buffer.h; // expected-error {{'h' is a private member of 'hlsl::RWBuffer<vector<float, 3>>'}}
Expand Down
4 changes: 2 additions & 2 deletions clang/test/SemaHLSL/BuiltIns/StructuredBuffers.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ typedef vector<float, 3> float3;
StructuredBuffer<float3> Buffer;

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

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

[numthreads(1,1,1)]
Expand Down