[ParserHLSL] Attempt to parse HLSL annotations on Field Decls. (#96346)
`MaybeParseHLSLAnnotations` should be run on Field Decls instead of just assuming that any colon after a field decl is a bitfield. In the case that HLSL is the language, the code after the colon may be an annotation. This PR gives the parser a chance to parse the subsequent text as if it was an HLSL annotation. The burden of parsing is now on the HLSL parser, but the actual work needs to be done in handling every case of an hlsl annotation on a field decl. SV_DispatchThreadID was straightforward enough to implement in this PR, and tests have been added that the annotation appears as an attribute in the AST. Previously, the `hlsl_annotations_on_struct_members.hlsl` test would result in an error shown below on the line that declares variable `a` in struct Eg9: error: use of undeclared identifier 'SV_DispatchThreadID' This is because the annotation is parsed as if it was a c++ bit field, and an identifier that represents an integer is expected, but not found. This test ensures that hlsl annotations are parsed when parsing struct decls. This test not only ensures we make progress by moving the validation error from the realm of C++ and expecting bitfields, to HLSL and a specialized error for the recognized annotation, but also validates that the parser does parse the annotation and adds an attribute to the field decl in the AST. Fixes https://github.com/llvm/llvm-project/issues/57889
This commit is contained in:
parent
afa3d58ee2
commit
c50ef30cce
@ -3017,11 +3017,12 @@ private:
|
||||
const IdentifierInfo *EnclosingScope = nullptr);
|
||||
|
||||
void MaybeParseHLSLAnnotations(Declarator &D,
|
||||
SourceLocation *EndLoc = nullptr) {
|
||||
SourceLocation *EndLoc = nullptr,
|
||||
bool CouldBeBitField = false) {
|
||||
assert(getLangOpts().HLSL && "MaybeParseHLSLAnnotations is for HLSL only");
|
||||
if (Tok.is(tok::colon)) {
|
||||
ParsedAttributes Attrs(AttrFactory);
|
||||
ParseHLSLAnnotations(Attrs, EndLoc);
|
||||
ParseHLSLAnnotations(Attrs, EndLoc, CouldBeBitField);
|
||||
D.takeAttributes(Attrs);
|
||||
}
|
||||
}
|
||||
@ -3029,12 +3030,13 @@ private:
|
||||
void MaybeParseHLSLAnnotations(ParsedAttributes &Attrs,
|
||||
SourceLocation *EndLoc = nullptr) {
|
||||
assert(getLangOpts().HLSL && "MaybeParseHLSLAnnotations is for HLSL only");
|
||||
if (getLangOpts().HLSL && Tok.is(tok::colon))
|
||||
if (Tok.is(tok::colon))
|
||||
ParseHLSLAnnotations(Attrs, EndLoc);
|
||||
}
|
||||
|
||||
void ParseHLSLAnnotations(ParsedAttributes &Attrs,
|
||||
SourceLocation *EndLoc = nullptr);
|
||||
SourceLocation *EndLoc = nullptr,
|
||||
bool CouldBeBitField = false);
|
||||
Decl *ParseHLSLBuffer(SourceLocation &DeclEnd);
|
||||
|
||||
void MaybeParseMicrosoftAttributes(ParsedAttributes &Attrs) {
|
||||
|
@ -2681,6 +2681,10 @@ bool Parser::ParseCXXMemberDeclaratorBeforeInitializer(
|
||||
else
|
||||
DeclaratorInfo.SetIdentifier(nullptr, Tok.getLocation());
|
||||
|
||||
if (getLangOpts().HLSL)
|
||||
MaybeParseHLSLAnnotations(DeclaratorInfo, nullptr,
|
||||
/*CouldBeBitField*/ true);
|
||||
|
||||
if (!DeclaratorInfo.isFunctionDeclarator() && TryConsumeToken(tok::colon)) {
|
||||
assert(DeclaratorInfo.isPastIdentifier() &&
|
||||
"don't know where identifier would go yet?");
|
||||
|
@ -119,9 +119,11 @@ static void fixSeparateAttrArgAndNumber(StringRef ArgStr, SourceLocation ArgLoc,
|
||||
}
|
||||
|
||||
void Parser::ParseHLSLAnnotations(ParsedAttributes &Attrs,
|
||||
SourceLocation *EndLoc) {
|
||||
SourceLocation *EndLoc,
|
||||
bool CouldBeBitField) {
|
||||
|
||||
assert(Tok.is(tok::colon) && "Not a HLSL Annotation");
|
||||
Token OldToken = Tok;
|
||||
ConsumeToken();
|
||||
|
||||
IdentifierInfo *II = nullptr;
|
||||
@ -131,6 +133,10 @@ void Parser::ParseHLSLAnnotations(ParsedAttributes &Attrs,
|
||||
II = Tok.getIdentifierInfo();
|
||||
|
||||
if (!II) {
|
||||
if (CouldBeBitField) {
|
||||
UnconsumeToken(OldToken);
|
||||
return;
|
||||
}
|
||||
Diag(Tok.getLocation(), diag::err_expected_semantic_identifier);
|
||||
return;
|
||||
}
|
||||
|
@ -357,14 +357,6 @@ static bool isLegalTypeForHLSLSV_DispatchThreadID(QualType T) {
|
||||
}
|
||||
|
||||
void SemaHLSL::handleSV_DispatchThreadIDAttr(Decl *D, const ParsedAttr &AL) {
|
||||
// FIXME: support semantic on field.
|
||||
// See https://github.com/llvm/llvm-project/issues/57889.
|
||||
if (isa<FieldDecl>(D)) {
|
||||
Diag(AL.getLoc(), diag::err_hlsl_attr_invalid_ast_node)
|
||||
<< AL << "parameter";
|
||||
return;
|
||||
}
|
||||
|
||||
auto *VD = cast<ValueDecl>(D);
|
||||
if (!isLegalTypeForHLSLSV_DispatchThreadID(VD->getType())) {
|
||||
Diag(AL.getLoc(), diag::err_hlsl_attr_invalid_type)
|
||||
|
31
clang/test/ParserHLSL/bitfields.hlsl
Normal file
31
clang/test/ParserHLSL/bitfields.hlsl
Normal file
@ -0,0 +1,31 @@
|
||||
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -ast-dump -x hlsl -o - %s | FileCheck %s
|
||||
|
||||
|
||||
struct MyBitFields {
|
||||
// CHECK: FieldDecl 0x{{[0-9a-f]+}} <line:9:3, col:25> col:16 referenced field1 'unsigned int'
|
||||
// CHECK:-ConstantExpr 0x{{[0-9a-f]+}} <col:25> 'int'
|
||||
// CHECK:-value: Int 3
|
||||
// CHECK:-IntegerLiteral 0x{{[0-9a-f]+}} <col:25> 'int' 3
|
||||
unsigned int field1 : 3; // 3 bits for field1
|
||||
|
||||
// CHECK:FieldDecl 0x{{[0-9a-f]+}} <line:15:3, col:25> col:16 referenced field2 'unsigned int'
|
||||
// CHECK:-ConstantExpr 0x{{[0-9a-f]+}} <col:25> 'int'
|
||||
// CHECK:-value: Int 4
|
||||
// CHECK:-IntegerLiteral 0x{{[0-9a-f]+}} <col:25> 'int' 4
|
||||
unsigned int field2 : 4; // 4 bits for field2
|
||||
|
||||
// CHECK:FieldDecl 0x{{[0-9a-f]+}} <line:21:3, col:16> col:7 field3 'int'
|
||||
// CHECK:-ConstantExpr 0x{{[0-9a-f]+}} <col:16> 'int'
|
||||
// CHECK:-value: Int 5
|
||||
// CHECK:-IntegerLiteral 0x{{[0-9a-f]+}} <col:16> 'int' 5
|
||||
int field3 : 5; // 5 bits for field3 (signed)
|
||||
};
|
||||
|
||||
|
||||
|
||||
[numthreads(1,1,1)]
|
||||
void main() {
|
||||
MyBitFields m;
|
||||
m.field1 = 4;
|
||||
m.field2 = m.field1*2;
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -x hlsl -ast-dump -o - %s | FileCheck %s
|
||||
|
||||
// tests that hlsl annotations are properly parsed when applied on field decls,
|
||||
// and that the annotation gets properly placed on the AST.
|
||||
|
||||
struct Eg9{
|
||||
// CHECK: CXXRecordDecl 0x{{[0-9a-f]+}} <col:1, col:8> col:8 implicit struct Eg9
|
||||
// CHECK: FieldDecl 0x{{[0-9a-f]+}} <line:10:3, col:16> col:16 referenced a 'unsigned int'
|
||||
// CHECK: -HLSLSV_DispatchThreadIDAttr 0x{{[0-9a-f]+}} <col:20>
|
||||
unsigned int a : SV_DispatchThreadID;
|
||||
};
|
||||
Eg9 e9;
|
||||
|
||||
|
||||
RWBuffer<int> In : register(u1);
|
||||
|
||||
|
||||
[numthreads(1,1,1)]
|
||||
void main() {
|
||||
In[0] = e9.a;
|
||||
}
|
@ -23,8 +23,7 @@ void foo() {
|
||||
}
|
||||
|
||||
struct ST2 {
|
||||
// expected-error@+1 {{use of undeclared identifier 'SV_DispatchThreadID'}}
|
||||
// expected-warning@+1 {{'SV_DispatchThreadID' attribute only applies to parameters and non-static data members}}
|
||||
static uint X : SV_DispatchThreadID;
|
||||
// expected-error@+1 {{use of undeclared identifier 'SV_DispatchThreadID'}}
|
||||
uint s : SV_DispatchThreadID;
|
||||
};
|
||||
|
@ -55,6 +55,6 @@ void bar(RWBuffer<float> U : register(u3)) {
|
||||
struct S {
|
||||
// FIXME: generate better error when support semantic on struct field.
|
||||
// See https://github.com/llvm/llvm-project/issues/57889.
|
||||
// expected-error@+1 {{expected expression}}
|
||||
// expected-warning@+1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
|
||||
RWBuffer<float> U : register(u3);
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user