1//===--- ParseHLSL.cpp - HLSL-specific parsing support --------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements the parsing logic for HLSL language features.
10//
11//===----------------------------------------------------------------------===//
12
13#include "clang/AST/Attr.h"
14#include "clang/Basic/AttributeCommonInfo.h"
15#include "clang/Parse/ParseDiagnostic.h"
16#include "clang/Parse/Parser.h"
17#include "clang/Parse/RAIIObjectsForParser.h"
18
19using namespace clang;
20
21static bool validateDeclsInsideHLSLBuffer(Parser::DeclGroupPtrTy DG,
22 SourceLocation BufferLoc,
23 bool IsCBuffer, Parser &P) {
24 // The parse is failed, just return false.
25 if (!DG)
26 return false;
27 DeclGroupRef Decls = DG.get();
28 bool IsValid = true;
29 // Only allow function, variable, record decls inside HLSLBuffer.
30 for (DeclGroupRef::iterator I = Decls.begin(), E = Decls.end(); I != E; ++I) {
31 Decl *D = *I;
32 if (isa<CXXRecordDecl, RecordDecl, FunctionDecl, VarDecl>(Val: D))
33 continue;
34
35 // FIXME: support nested HLSLBuffer and namespace inside HLSLBuffer.
36 if (isa<HLSLBufferDecl, NamespaceDecl>(Val: D)) {
37 P.Diag(D->getLocation(), diag::err_invalid_declaration_in_hlsl_buffer)
38 << IsCBuffer;
39 IsValid = false;
40 continue;
41 }
42
43 IsValid = false;
44 P.Diag(D->getLocation(), diag::err_invalid_declaration_in_hlsl_buffer)
45 << IsCBuffer;
46 }
47 return IsValid;
48}
49
50Decl *Parser::ParseHLSLBuffer(SourceLocation &DeclEnd) {
51 assert((Tok.is(tok::kw_cbuffer) || Tok.is(tok::kw_tbuffer)) &&
52 "Not a cbuffer or tbuffer!");
53 bool IsCBuffer = Tok.is(K: tok::kw_cbuffer);
54 SourceLocation BufferLoc = ConsumeToken(); // Eat the 'cbuffer' or 'tbuffer'.
55
56 if (!Tok.is(K: tok::identifier)) {
57 Diag(Tok, diag::err_expected) << tok::identifier;
58 return nullptr;
59 }
60
61 IdentifierInfo *Identifier = Tok.getIdentifierInfo();
62 SourceLocation IdentifierLoc = ConsumeToken();
63
64 ParsedAttributes Attrs(AttrFactory);
65 MaybeParseHLSLSemantics(Attrs, EndLoc: nullptr);
66
67 ParseScope BufferScope(this, Scope::DeclScope);
68 BalancedDelimiterTracker T(*this, tok::l_brace);
69 if (T.consumeOpen()) {
70 Diag(Tok, diag::err_expected) << tok::l_brace;
71 return nullptr;
72 }
73
74 Decl *D = Actions.ActOnStartHLSLBuffer(BufferScope: getCurScope(), CBuffer: IsCBuffer, KwLoc: BufferLoc,
75 Ident: Identifier, IdentLoc: IdentifierLoc,
76 LBrace: T.getOpenLocation());
77
78 while (Tok.isNot(K: tok::r_brace) && Tok.isNot(K: tok::eof)) {
79 // FIXME: support attribute on constants inside cbuffer/tbuffer.
80 ParsedAttributes DeclAttrs(AttrFactory);
81 ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
82
83 DeclGroupPtrTy Result =
84 ParseExternalDeclaration(DeclAttrs, DeclSpecAttrs&: EmptyDeclSpecAttrs);
85 if (!validateDeclsInsideHLSLBuffer(DG: Result, BufferLoc: IdentifierLoc, IsCBuffer,
86 P&: *this)) {
87 T.skipToEnd();
88 DeclEnd = T.getCloseLocation();
89 BufferScope.Exit();
90 Actions.ActOnFinishHLSLBuffer(Dcl: D, RBrace: DeclEnd);
91 return nullptr;
92 }
93 }
94
95 T.consumeClose();
96 DeclEnd = T.getCloseLocation();
97 BufferScope.Exit();
98 Actions.ActOnFinishHLSLBuffer(Dcl: D, RBrace: DeclEnd);
99
100 Actions.ProcessDeclAttributeList(S: Actions.CurScope, D, AttrList: Attrs);
101 return D;
102}
103
104static void fixSeparateAttrArgAndNumber(StringRef ArgStr, SourceLocation ArgLoc,
105 Token Tok, ArgsVector &ArgExprs,
106 Parser &P, ASTContext &Ctx,
107 Preprocessor &PP) {
108 StringRef Num = StringRef(Tok.getLiteralData(), Tok.getLength());
109 SourceLocation EndNumLoc = Tok.getEndLoc();
110
111 P.ConsumeToken(); // consume constant.
112 std::string FixedArg = ArgStr.str() + Num.str();
113 P.Diag(ArgLoc, diag::err_hlsl_separate_attr_arg_and_number)
114 << FixedArg
115 << FixItHint::CreateReplacement(SourceRange(ArgLoc, EndNumLoc), FixedArg);
116 ArgsUnion &Slot = ArgExprs.back();
117 Slot = IdentifierLoc::create(Ctx, Loc: ArgLoc, Ident: PP.getIdentifierInfo(Name: FixedArg));
118}
119
120void Parser::ParseHLSLSemantics(ParsedAttributes &Attrs,
121 SourceLocation *EndLoc) {
122 // FIXME: HLSLSemantic is shared for Semantic and resource binding which is
123 // confusing. Need a better name to avoid misunderstanding. Issue
124 // https://github.com/llvm/llvm-project/issues/57882
125 assert(Tok.is(tok::colon) && "Not a HLSL Semantic");
126 ConsumeToken();
127
128 IdentifierInfo *II = nullptr;
129 if (Tok.is(K: tok::kw_register))
130 II = PP.getIdentifierInfo(Name: "register");
131 else if (Tok.is(K: tok::identifier))
132 II = Tok.getIdentifierInfo();
133
134 if (!II) {
135 Diag(Tok.getLocation(), diag::err_expected_semantic_identifier);
136 return;
137 }
138
139 SourceLocation Loc = ConsumeToken();
140 if (EndLoc)
141 *EndLoc = Tok.getLocation();
142 ParsedAttr::Kind AttrKind =
143 ParsedAttr::getParsedKind(Name: II, Scope: nullptr, SyntaxUsed: ParsedAttr::AS_HLSLSemantic);
144
145 ArgsVector ArgExprs;
146 switch (AttrKind) {
147 case ParsedAttr::AT_HLSLResourceBinding: {
148 if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after)) {
149 SkipUntil(T: tok::r_paren, Flags: StopAtSemi); // skip through )
150 return;
151 }
152 if (!Tok.is(K: tok::identifier)) {
153 Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
154 SkipUntil(T: tok::r_paren, Flags: StopAtSemi); // skip through )
155 return;
156 }
157 StringRef SlotStr = Tok.getIdentifierInfo()->getName();
158 SourceLocation SlotLoc = Tok.getLocation();
159 ArgExprs.push_back(Elt: ParseIdentifierLoc());
160
161 // Add numeric_constant for fix-it.
162 if (SlotStr.size() == 1 && Tok.is(K: tok::numeric_constant))
163 fixSeparateAttrArgAndNumber(ArgStr: SlotStr, ArgLoc: SlotLoc, Tok, ArgExprs, P&: *this,
164 Ctx&: Actions.Context, PP);
165
166 if (Tok.is(K: tok::comma)) {
167 ConsumeToken(); // consume comma
168 if (!Tok.is(K: tok::identifier)) {
169 Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
170 SkipUntil(T: tok::r_paren, Flags: StopAtSemi); // skip through )
171 return;
172 }
173 StringRef SpaceStr = Tok.getIdentifierInfo()->getName();
174 SourceLocation SpaceLoc = Tok.getLocation();
175 ArgExprs.push_back(Elt: ParseIdentifierLoc());
176
177 // Add numeric_constant for fix-it.
178 if (SpaceStr.equals(RHS: "space") && Tok.is(K: tok::numeric_constant))
179 fixSeparateAttrArgAndNumber(ArgStr: SpaceStr, ArgLoc: SpaceLoc, Tok, ArgExprs, P&: *this,
180 Ctx&: Actions.Context, PP);
181 }
182 if (ExpectAndConsume(tok::r_paren, diag::err_expected)) {
183 SkipUntil(T: tok::r_paren, Flags: StopAtSemi); // skip through )
184 return;
185 }
186 } break;
187 case ParsedAttr::UnknownAttribute:
188 Diag(Loc, diag::err_unknown_hlsl_semantic) << II;
189 return;
190 case ParsedAttr::AT_HLSLSV_GroupIndex:
191 case ParsedAttr::AT_HLSLSV_DispatchThreadID:
192 break;
193 default:
194 llvm_unreachable("invalid HLSL Semantic");
195 break;
196 }
197
198 Attrs.addNew(attrName: II, attrRange: Loc, scopeName: nullptr, scopeLoc: SourceLocation(), args: ArgExprs.data(),
199 numArgs: ArgExprs.size(), form: ParsedAttr::Form::HLSLSemantic());
200}
201

source code of clang/lib/Parse/ParseHLSL.cpp