clang 22.0.0git
ParseHLSL.cpp
Go to the documentation of this file.
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"
16#include "clang/Parse/Parser.h"
18#include "clang/Sema/SemaHLSL.h"
19
20using namespace clang;
21
23 SourceLocation BufferLoc,
24 bool IsCBuffer, Parser &P) {
25 // The parse is failed, just return false.
26 if (!DG)
27 return false;
28 DeclGroupRef Decls = DG.get();
29 bool IsValid = true;
30 // Only allow function, variable, record, and empty decls inside HLSLBuffer.
31 for (DeclGroupRef::iterator I = Decls.begin(), E = Decls.end(); I != E; ++I) {
32 Decl *D = *I;
34 continue;
35
36 // FIXME: support nested HLSLBuffer and namespace inside HLSLBuffer.
38 P.Diag(D->getLocation(), diag::err_invalid_declaration_in_hlsl_buffer)
39 << IsCBuffer;
40 IsValid = false;
41 continue;
42 }
43
44 IsValid = false;
45 P.Diag(D->getLocation(), diag::err_invalid_declaration_in_hlsl_buffer)
46 << IsCBuffer;
47 }
48 return IsValid;
49}
50
51Decl *Parser::ParseHLSLBuffer(SourceLocation &DeclEnd,
52 ParsedAttributes &Attrs) {
53 assert((Tok.is(tok::kw_cbuffer) || Tok.is(tok::kw_tbuffer)) &&
54 "Not a cbuffer or tbuffer!");
55 bool IsCBuffer = Tok.is(tok::kw_cbuffer);
56 SourceLocation BufferLoc = ConsumeToken(); // Eat the 'cbuffer' or 'tbuffer'.
57
58 if (!Tok.is(tok::identifier)) {
59 Diag(Tok, diag::err_expected) << tok::identifier;
60 return nullptr;
61 }
62
63 IdentifierInfo *Identifier = Tok.getIdentifierInfo();
64 SourceLocation IdentifierLoc = ConsumeToken();
65
66 MaybeParseHLSLAnnotations(Attrs, nullptr);
67
68 ParseScope BufferScope(this, Scope::DeclScope);
69 BalancedDelimiterTracker T(*this, tok::l_brace);
70 if (T.consumeOpen()) {
71 Diag(Tok, diag::err_expected) << tok::l_brace;
72 return nullptr;
73 }
74
75 Decl *D = Actions.HLSL().ActOnStartBuffer(getCurScope(), IsCBuffer, BufferLoc,
76 Identifier, IdentifierLoc,
77 T.getOpenLocation());
78 Actions.ProcessDeclAttributeList(Actions.CurScope, D, Attrs);
79
80 while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) {
81 // FIXME: support attribute on constants inside cbuffer/tbuffer.
82 ParsedAttributes DeclAttrs(AttrFactory);
83 ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
84
86 ParseExternalDeclaration(DeclAttrs, EmptyDeclSpecAttrs);
87 if (!validateDeclsInsideHLSLBuffer(Result, IdentifierLoc, IsCBuffer,
88 *this)) {
89 T.skipToEnd();
90 DeclEnd = T.getCloseLocation();
91 BufferScope.Exit();
92 Actions.HLSL().ActOnFinishBuffer(D, DeclEnd);
93 return nullptr;
94 }
95 }
96
97 T.consumeClose();
98 DeclEnd = T.getCloseLocation();
99 BufferScope.Exit();
100 Actions.HLSL().ActOnFinishBuffer(D, DeclEnd);
101
102 return D;
103}
104
105static void fixSeparateAttrArgAndNumber(StringRef ArgStr, SourceLocation ArgLoc,
106 Token Tok, ArgsVector &ArgExprs,
107 Parser &P, ASTContext &Ctx,
108 Preprocessor &PP) {
109 StringRef Num = StringRef(Tok.getLiteralData(), Tok.getLength());
110 SourceLocation EndNumLoc = Tok.getEndLoc();
111
112 P.ConsumeToken(); // consume constant.
113 std::string FixedArg = ArgStr.str() + Num.str();
114 P.Diag(ArgLoc, diag::err_hlsl_separate_attr_arg_and_number)
115 << FixedArg
116 << FixItHint::CreateReplacement(SourceRange(ArgLoc, EndNumLoc), FixedArg);
117 ArgsUnion &Slot = ArgExprs.back();
118 Slot = new (Ctx) IdentifierLoc(ArgLoc, PP.getIdentifierInfo(FixedArg));
119}
120
121Parser::ParsedSemantic Parser::ParseHLSLSemantic() {
122 assert(Tok.is(tok::identifier) && "Not a HLSL Annotation");
123
124 // Semantic pattern: [A-Za-z_]([A-Za-z_0-9]*[A-Za-z_])?[0-9]*
125 // The first part is the semantic name, the second is the optional
126 // semantic index. The semantic index is the number at the end of
127 // the semantic, including leading zeroes. Digits located before
128 // the last letter are part of the semantic name.
129 bool Invalid = false;
130 SmallString<256> Buffer;
131 Buffer.resize(Tok.getLength() + 1);
132 StringRef Identifier = PP.getSpelling(Tok, Buffer);
133 if (Invalid) {
134 Diag(Tok.getLocation(), diag::err_expected_semantic_identifier);
135 return {};
136 }
137
138 assert(Identifier.size() > 0);
139 // Determine the start of the semantic index.
140 unsigned IndexIndex = Identifier.find_last_not_of("0123456789") + 1;
141
142 // ParseHLSLSemantic being called on an indentifier, the first
143 // character cannot be a digit. This error should be handled by
144 // the caller. We can assert here.
145 StringRef SemanticName = Identifier.take_front(IndexIndex);
146 assert(SemanticName.size() > 0);
147
148 unsigned Index = 0;
149 bool Explicit = false;
150 if (IndexIndex != Identifier.size()) {
151 Explicit = true;
152 [[maybe_unused]] bool Failure =
153 Identifier.substr(IndexIndex).getAsInteger(10, Index);
154 // Given the logic above, this should never fail.
155 assert(!Failure);
156 }
157
158 return {SemanticName, Index, Explicit};
159}
160
161void Parser::ParseHLSLAnnotations(ParsedAttributes &Attrs,
162 SourceLocation *EndLoc,
163 bool CouldBeBitField) {
164
165 assert(Tok.is(tok::colon) && "Not a HLSL Annotation");
166 Token OldToken = Tok;
167 ConsumeToken();
168
169 IdentifierInfo *II = nullptr;
170 if (Tok.is(tok::kw_register))
171 II = PP.getIdentifierInfo("register");
172 else if (Tok.is(tok::identifier))
173 II = Tok.getIdentifierInfo();
174
175 if (!II) {
176 if (CouldBeBitField) {
177 UnconsumeToken(OldToken);
178 return;
179 }
180 Diag(Tok.getLocation(), diag::err_expected_semantic_identifier);
181 return;
182 }
183
184 ParsedAttr::Kind AttrKind =
186 Parser::ParsedSemantic Semantic;
187 if (AttrKind == ParsedAttr::AT_HLSLUnparsedSemantic)
188 Semantic = ParseHLSLSemantic();
189
190 SourceLocation Loc = ConsumeToken();
191 if (EndLoc)
192 *EndLoc = Tok.getLocation();
193
194 ArgsVector ArgExprs;
195 switch (AttrKind) {
196 case ParsedAttr::AT_HLSLResourceBinding: {
197 if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after)) {
198 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
199 return;
200 }
201 if (!Tok.is(tok::identifier)) {
202 Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
203 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
204 return;
205 }
206 StringRef SlotStr = Tok.getIdentifierInfo()->getName();
207 SourceLocation SlotLoc = Tok.getLocation();
208 ArgExprs.push_back(ParseIdentifierLoc());
209
210 if (SlotStr.size() == 1) {
211 if (!Tok.is(tok::numeric_constant)) {
212 Diag(Tok.getLocation(), diag::err_expected) << tok::numeric_constant;
213 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
214 return;
215 }
216 // Add numeric_constant for fix-it.
217 fixSeparateAttrArgAndNumber(SlotStr, SlotLoc, Tok, ArgExprs, *this,
218 Actions.Context, PP);
219 }
220 if (Tok.is(tok::comma)) {
221 ConsumeToken(); // consume comma
222 if (!Tok.is(tok::identifier)) {
223 Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
224 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
225 return;
226 }
227 StringRef SpaceStr = Tok.getIdentifierInfo()->getName();
228 SourceLocation SpaceLoc = Tok.getLocation();
229 ArgExprs.push_back(ParseIdentifierLoc());
230
231 // Add numeric_constant for fix-it.
232 if (SpaceStr == "space" && Tok.is(tok::numeric_constant))
233 fixSeparateAttrArgAndNumber(SpaceStr, SpaceLoc, Tok, ArgExprs, *this,
234 Actions.Context, PP);
235 }
236 if (ExpectAndConsume(tok::r_paren, diag::err_expected)) {
237 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
238 return;
239 }
240 } break;
241 case ParsedAttr::AT_HLSLPackOffset: {
242 // Parse 'packoffset( c[Subcomponent][.component] )'.
243 // Check '('.
244 if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after)) {
245 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
246 return;
247 }
248 // Check c[Subcomponent] as an identifier.
249 if (!Tok.is(tok::identifier)) {
250 Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
251 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
252 return;
253 }
254 StringRef OffsetStr = Tok.getIdentifierInfo()->getName();
255 SourceLocation SubComponentLoc = Tok.getLocation();
256 if (OffsetStr[0] != 'c') {
257 Diag(Tok.getLocation(), diag::err_hlsl_packoffset_invalid_reg)
258 << OffsetStr;
259 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
260 return;
261 }
262 OffsetStr = OffsetStr.substr(1);
263 unsigned SubComponent = 0;
264 if (!OffsetStr.empty()) {
265 // Make sure SubComponent is a number.
266 if (OffsetStr.getAsInteger(10, SubComponent)) {
267 Diag(SubComponentLoc.getLocWithOffset(1),
268 diag::err_hlsl_unsupported_register_number);
269 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
270 return;
271 }
272 }
273 unsigned Component = 0;
274 ConsumeToken(); // consume identifier.
275 SourceLocation ComponentLoc;
276 if (Tok.is(tok::period)) {
277 ConsumeToken(); // consume period.
278 if (!Tok.is(tok::identifier)) {
279 Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
280 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
281 return;
282 }
283 StringRef ComponentStr = Tok.getIdentifierInfo()->getName();
284 ComponentLoc = Tok.getLocation();
285 ConsumeToken(); // consume identifier.
286 // Make sure Component is a single character.
287 if (ComponentStr.size() != 1) {
288 Diag(ComponentLoc, diag::err_hlsl_unsupported_component)
289 << ComponentStr;
290 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
291 return;
292 }
293 switch (ComponentStr[0]) {
294 case 'x':
295 case 'r':
296 Component = 0;
297 break;
298 case 'y':
299 case 'g':
300 Component = 1;
301 break;
302 case 'z':
303 case 'b':
304 Component = 2;
305 break;
306 case 'w':
307 case 'a':
308 Component = 3;
309 break;
310 default:
311 Diag(ComponentLoc, diag::err_hlsl_unsupported_component)
312 << ComponentStr;
313 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
314 return;
315 }
316 }
317 ASTContext &Ctx = Actions.getASTContext();
318 QualType SizeTy = Ctx.getSizeType();
319 uint64_t SizeTySize = Ctx.getTypeSize(SizeTy);
320 ArgExprs.push_back(IntegerLiteral::Create(
321 Ctx, llvm::APInt(SizeTySize, SubComponent), SizeTy, SubComponentLoc));
322 ArgExprs.push_back(IntegerLiteral::Create(
323 Ctx, llvm::APInt(SizeTySize, Component), SizeTy, ComponentLoc));
324 if (ExpectAndConsume(tok::r_paren, diag::err_expected)) {
325 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
326 return;
327 }
328 } break;
329 case ParsedAttr::AT_HLSLUnparsedSemantic: {
330 ASTContext &Ctx = Actions.getASTContext();
331 ArgExprs.push_back(IntegerLiteral::Create(
332 Ctx, llvm::APInt(Ctx.getTypeSize(Ctx.IntTy), Semantic.Index), Ctx.IntTy,
333 SourceLocation()));
334 ArgExprs.push_back(IntegerLiteral::Create(
335 Ctx, llvm::APInt(1, Semantic.Explicit), Ctx.BoolTy, SourceLocation()));
336 II = PP.getIdentifierInfo(Semantic.Name);
337 break;
338 }
339 case ParsedAttr::UnknownAttribute: // FIXME: maybe this is obsolete?
340 break;
341 default:
342 llvm_unreachable("invalid HLSL Annotation");
343 break;
344 }
345
346 Attrs.addNew(II, Loc, AttributeScopeInfo(), ArgExprs.data(), ArgExprs.size(),
347 ParsedAttr::Form::HLSLAnnotation());
348}
Token Tok
The Token.
static bool validateDeclsInsideHLSLBuffer(Parser::DeclGroupPtrTy DG, SourceLocation BufferLoc, bool IsCBuffer, Parser &P)
Definition ParseHLSL.cpp:22
static void fixSeparateAttrArgAndNumber(StringRef ArgStr, SourceLocation ArgLoc, Token Tok, ArgsVector &ArgExprs, Parser &P, ASTContext &Ctx, Preprocessor &PP)
This file declares semantic analysis for HLSL constructs.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition ASTContext.h:188
CanQualType BoolTy
CanQualType IntTy
uint64_t getTypeSize(QualType T) const
Return the size of the specified (complete) type T, in bits.
QualType getSizeType() const
Return the unique type for "size_t" (C99 7.17), defined in <stddef.h>.
@ AS_HLSLAnnotation
<vardecl> : <annotation>
Decl - This represents one declaration (or definition), e.g.
Definition DeclBase.h:86
SourceLocation getLocation() const
Definition DeclBase.h:439
static FixItHint CreateReplacement(CharSourceRange RemoveRange, StringRef Code)
Create a code modification hint that replaces the given source range with the given code string.
Definition Diagnostic.h:139
A simple pair of identifier info and location.
static IntegerLiteral * Create(const ASTContext &C, const llvm::APInt &V, QualType type, SourceLocation l)
Returns a new integer literal with value 'V' and type 'type'.
Definition Expr.cpp:971
PtrTy get() const
Definition Ownership.h:81
ParsedAttributes - A collection of parsed attributes.
Definition ParsedAttr.h:937
ParsedAttr * addNew(IdentifierInfo *attrName, SourceRange attrRange, AttributeScopeInfo scope, ArgsUnion *args, unsigned numArgs, ParsedAttr::Form form, SourceLocation ellipsisLoc=SourceLocation())
Add attribute with expression arguments.
Definition ParsedAttr.h:978
ParseScope - Introduces a new scope for parsing.
Definition Parser.h:396
Parser - This implements a parser for the C family of languages.
Definition Parser.h:171
DiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID)
Definition Parser.cpp:85
SourceLocation ConsumeToken()
ConsumeToken - Consume the current 'peek token' and lex the next one.
Definition Parser.h:262
OpaquePtr< DeclGroupRef > DeclGroupPtrTy
Definition Parser.h:219
Scope * getCurScope() const
Definition Parser.h:211
bool SkipUntil(tok::TokenKind T, SkipUntilFlags Flags=static_cast< SkipUntilFlags >(0))
SkipUntil - Read tokens until we get to the specified token, then consume it (unless StopBeforeMatch ...
Definition Parser.h:495
@ StopAtSemi
Stop skipping at semicolon.
Definition Parser.h:474
friend class BalancedDelimiterTracker
Definition Parser.h:199
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
IdentifierInfo * getIdentifierInfo(StringRef Name) const
Return information about the specified preprocessor identifier token.
@ DeclScope
This is a scope that can contain a declaration.
Definition Scope.h:63
Encodes a location in the source.
SourceLocation getLocWithOffset(IntTy Offset) const
Return a source location with the specified offset from this SourceLocation.
A trivial tuple used to represent a source range.
Token - This structure provides full information about a lexed token.
Definition Token.h:36
std::variant< struct RequiresDecl, struct HeaderDecl, struct UmbrellaDirDecl, struct ModuleDecl, struct ExcludeDecl, struct ExportDecl, struct ExportAsDecl, struct ExternModuleDecl, struct UseDecl, struct LinkDecl, struct ConfigMacrosDecl, struct ConflictDecl > Decl
All declarations that can appear in a module declaration.
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
Definition Address.h:330
llvm::PointerUnion< Expr *, IdentifierLoc * > ArgsUnion
A union of the various pointer types that can be passed to an ParsedAttr as an argument.
Definition ParsedAttr.h:103
llvm::SmallVector< ArgsUnion, 12U > ArgsVector
Definition ParsedAttr.h:104
@ Result
The result type of a method or function.
Definition TypeBase.h:905
const FunctionProtoType * T
unsigned long uint64_t