clang 19.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 decls inside HLSLBuffer.
31 for (DeclGroupRef::iterator I = Decls.begin(), E = Decls.end(); I != E; ++I) {
32 Decl *D = *I;
33 if (isa<CXXRecordDecl, RecordDecl, FunctionDecl, VarDecl>(D))
34 continue;
35
36 // FIXME: support nested HLSLBuffer and namespace inside HLSLBuffer.
37 if (isa<HLSLBufferDecl, NamespaceDecl>(D)) {
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 assert((Tok.is(tok::kw_cbuffer) || Tok.is(tok::kw_tbuffer)) &&
53 "Not a cbuffer or tbuffer!");
54 bool IsCBuffer = Tok.is(tok::kw_cbuffer);
55 SourceLocation BufferLoc = ConsumeToken(); // Eat the 'cbuffer' or 'tbuffer'.
56
57 if (!Tok.is(tok::identifier)) {
58 Diag(Tok, diag::err_expected) << tok::identifier;
59 return nullptr;
60 }
61
64
65 ParsedAttributes Attrs(AttrFactory);
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,
77 T.getOpenLocation());
78
79 while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) {
80 // FIXME: support attribute on constants inside cbuffer/tbuffer.
81 ParsedAttributes DeclAttrs(AttrFactory);
82 ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
83
85 ParseExternalDeclaration(DeclAttrs, EmptyDeclSpecAttrs);
87 *this)) {
88 T.skipToEnd();
89 DeclEnd = T.getCloseLocation();
90 BufferScope.Exit();
91 Actions.HLSL().ActOnFinishBuffer(D, DeclEnd);
92 return nullptr;
93 }
94 }
95
96 T.consumeClose();
97 DeclEnd = T.getCloseLocation();
98 BufferScope.Exit();
99 Actions.HLSL().ActOnFinishBuffer(D, DeclEnd);
100
101 Actions.ProcessDeclAttributeList(Actions.CurScope, D, Attrs);
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 = IdentifierLoc::create(Ctx, ArgLoc, PP.getIdentifierInfo(FixedArg));
119}
120
121void Parser::ParseHLSLAnnotations(ParsedAttributes &Attrs,
122 SourceLocation *EndLoc,
123 bool CouldBeBitField) {
124
125 assert(Tok.is(tok::colon) && "Not a HLSL Annotation");
126 Token OldToken = Tok;
127 ConsumeToken();
128
129 IdentifierInfo *II = nullptr;
130 if (Tok.is(tok::kw_register))
131 II = PP.getIdentifierInfo("register");
132 else if (Tok.is(tok::identifier))
133 II = Tok.getIdentifierInfo();
134
135 if (!II) {
136 if (CouldBeBitField) {
137 UnconsumeToken(OldToken);
138 return;
139 }
140 Diag(Tok.getLocation(), diag::err_expected_semantic_identifier);
141 return;
142 }
143
145 if (EndLoc)
146 *EndLoc = Tok.getLocation();
147 ParsedAttr::Kind AttrKind =
149
150 ArgsVector ArgExprs;
151 switch (AttrKind) {
152 case ParsedAttr::AT_HLSLResourceBinding: {
153 if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after)) {
154 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
155 return;
156 }
157 if (!Tok.is(tok::identifier)) {
158 Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
159 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
160 return;
161 }
162 StringRef SlotStr = Tok.getIdentifierInfo()->getName();
163 SourceLocation SlotLoc = Tok.getLocation();
164 ArgExprs.push_back(ParseIdentifierLoc());
165
166 // Add numeric_constant for fix-it.
167 if (SlotStr.size() == 1 && Tok.is(tok::numeric_constant))
168 fixSeparateAttrArgAndNumber(SlotStr, SlotLoc, Tok, ArgExprs, *this,
169 Actions.Context, PP);
170
171 if (Tok.is(tok::comma)) {
172 ConsumeToken(); // consume comma
173 if (!Tok.is(tok::identifier)) {
174 Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
175 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
176 return;
177 }
178 StringRef SpaceStr = Tok.getIdentifierInfo()->getName();
179 SourceLocation SpaceLoc = Tok.getLocation();
180 ArgExprs.push_back(ParseIdentifierLoc());
181
182 // Add numeric_constant for fix-it.
183 if (SpaceStr == "space" && Tok.is(tok::numeric_constant))
184 fixSeparateAttrArgAndNumber(SpaceStr, SpaceLoc, Tok, ArgExprs, *this,
185 Actions.Context, PP);
186 }
187 if (ExpectAndConsume(tok::r_paren, diag::err_expected)) {
188 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
189 return;
190 }
191 } break;
192 case ParsedAttr::AT_HLSLPackOffset: {
193 // Parse 'packoffset( c[Subcomponent][.component] )'.
194 // Check '('.
195 if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after)) {
196 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
197 return;
198 }
199 // Check c[Subcomponent] as an identifier.
200 if (!Tok.is(tok::identifier)) {
201 Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
202 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
203 return;
204 }
205 StringRef OffsetStr = Tok.getIdentifierInfo()->getName();
206 SourceLocation SubComponentLoc = Tok.getLocation();
207 if (OffsetStr[0] != 'c') {
208 Diag(Tok.getLocation(), diag::err_hlsl_packoffset_invalid_reg)
209 << OffsetStr;
210 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
211 return;
212 }
213 OffsetStr = OffsetStr.substr(1);
214 unsigned SubComponent = 0;
215 if (!OffsetStr.empty()) {
216 // Make sure SubComponent is a number.
217 if (OffsetStr.getAsInteger(10, SubComponent)) {
218 Diag(SubComponentLoc.getLocWithOffset(1),
219 diag::err_hlsl_unsupported_register_number);
220 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
221 return;
222 }
223 }
224 unsigned Component = 0;
225 ConsumeToken(); // consume identifier.
226 SourceLocation ComponentLoc;
227 if (Tok.is(tok::period)) {
228 ConsumeToken(); // consume period.
229 if (!Tok.is(tok::identifier)) {
230 Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
231 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
232 return;
233 }
234 StringRef ComponentStr = Tok.getIdentifierInfo()->getName();
235 ComponentLoc = Tok.getLocation();
236 ConsumeToken(); // consume identifier.
237 // Make sure Component is a single character.
238 if (ComponentStr.size() != 1) {
239 Diag(ComponentLoc, diag::err_hlsl_unsupported_component)
240 << ComponentStr;
241 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
242 return;
243 }
244 switch (ComponentStr[0]) {
245 case 'x':
246 case 'r':
247 Component = 0;
248 break;
249 case 'y':
250 case 'g':
251 Component = 1;
252 break;
253 case 'z':
254 case 'b':
255 Component = 2;
256 break;
257 case 'w':
258 case 'a':
259 Component = 3;
260 break;
261 default:
262 Diag(ComponentLoc, diag::err_hlsl_unsupported_component)
263 << ComponentStr;
264 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
265 return;
266 }
267 }
268 ASTContext &Ctx = Actions.getASTContext();
269 QualType SizeTy = Ctx.getSizeType();
270 uint64_t SizeTySize = Ctx.getTypeSize(SizeTy);
271 ArgExprs.push_back(IntegerLiteral::Create(
272 Ctx, llvm::APInt(SizeTySize, SubComponent), SizeTy, SubComponentLoc));
273 ArgExprs.push_back(IntegerLiteral::Create(
274 Ctx, llvm::APInt(SizeTySize, Component), SizeTy, ComponentLoc));
275 if (ExpectAndConsume(tok::r_paren, diag::err_expected)) {
276 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
277 return;
278 }
279 } break;
281 Diag(Loc, diag::err_unknown_hlsl_semantic) << II;
282 return;
283 case ParsedAttr::AT_HLSLSV_GroupIndex:
284 case ParsedAttr::AT_HLSLSV_DispatchThreadID:
285 break;
286 default:
287 llvm_unreachable("invalid HLSL Annotation");
288 break;
289 }
290
291 Attrs.addNew(II, Loc, nullptr, SourceLocation(), ArgExprs.data(),
292 ArgExprs.size(), ParsedAttr::Form::HLSLAnnotation());
293}
StringRef P
const Decl * D
Expr * E
StringRef Identifier
Definition: Format.cpp:2997
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)
Definition: ParseHLSL.cpp:105
This file declares semantic analysis for HLSL constructs.
SourceLocation Loc
Definition: SemaObjC.cpp:758
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:186
CanQualType getSizeType() const
Return the unique type for "size_t" (C99 7.17), defined in <stddef.h>.
uint64_t getTypeSize(QualType T) const
Return the size of the specified (complete) type T, in bits.
Definition: ASTContext.h:2385
@ AS_HLSLAnnotation
<vardecl> : <annotation>
RAII class that helps handle the parsing of an open/close delimiter pair, such as braces { ....
iterator begin()
Definition: DeclGroup.h:99
iterator end()
Definition: DeclGroup.h:105
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
SourceLocation getLocation() const
Definition: DeclBase.h:445
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:134
One of these records is kept for each identifier that is lexed.
StringRef getName() const
Return the actual identifier string.
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:977
Wrapper for void* pointer.
Definition: Ownership.h:50
PtrTy get() const
Definition: Ownership.h:80
ParsedAttributes - A collection of parsed attributes.
Definition: ParsedAttr.h:958
ParsedAttr * addNew(IdentifierInfo *attrName, SourceRange attrRange, IdentifierInfo *scopeName, SourceLocation scopeLoc, ArgsUnion *args, unsigned numArgs, ParsedAttr::Form form, SourceLocation ellipsisLoc=SourceLocation())
Add attribute with expression arguments.
Definition: ParsedAttr.h:991
Parser - This implements a parser for the C family of languages.
Definition: Parser.h:58
DiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID)
Definition: Parser.cpp:81
SourceLocation ConsumeToken()
ConsumeToken - Consume the current 'peek token' and lex the next one.
Definition: Parser.h:548
OpaquePtr< DeclGroupRef > DeclGroupPtrTy
Definition: Parser.h:513
Scope * getCurScope() const
Definition: Parser.h:502
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:1294
@ StopAtSemi
Stop skipping at semicolon.
Definition: Parser.h:1273
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
Definition: Preprocessor.h:137
IdentifierInfo * getIdentifierInfo(StringRef Name) const
Return information about the specified preprocessor identifier token.
A (possibly-)qualified type.
Definition: Type.h:941
@ DeclScope
This is a scope that can contain a declaration.
Definition: Scope.h:63
void ActOnFinishBuffer(Decl *Dcl, SourceLocation RBrace)
Definition: SemaHLSL.cpp:82
Decl * ActOnStartBuffer(Scope *BufferScope, bool CBuffer, SourceLocation KwLoc, IdentifierInfo *Ident, SourceLocation IdentLoc, SourceLocation LBrace)
Definition: SemaHLSL.cpp:32
ASTContext & Context
Definition: Sema.h:1002
ASTContext & getASTContext() const
Definition: Sema.h:600
SemaHLSL & HLSL()
Definition: Sema.h:1169
void ProcessDeclAttributeList(Scope *S, Decl *D, const ParsedAttributesView &AttrList, const ProcessDeclAttributeOptions &Options=ProcessDeclAttributeOptions())
ProcessDeclAttributeList - Apply all the decl attributes in the specified attribute list to the speci...
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
IdentifierInfo * getIdentifierInfo() const
Definition: Token.h:187
SourceLocation getEndLoc() const
Definition: Token.h:159
SourceLocation getLocation() const
Return a source location identifier for the specified offset in the current file.
Definition: Token.h:132
unsigned getLength() const
Definition: Token.h:135
bool is(tok::TokenKind K) const
is/isNot - Predicates to check if this token is a specific kind, as in "if (Tok.is(tok::l_brace)) {....
Definition: Token.h:99
bool isNot(tok::TokenKind K) const
Definition: Token.h:100
const char * getLiteralData() const
getLiteralData - For a literal token (numeric constant, string, etc), this returns a pointer to the s...
Definition: Token.h:225
The JSON file list parser is used to communicate input to InstallAPI.
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:113
@ Result
The result type of a method or function.
const FunctionProtoType * T
unsigned long uint64_t
Wraps an identifier and optional source location for the identifier.
Definition: ParsedAttr.h:103
static IdentifierLoc * create(ASTContext &Ctx, SourceLocation Loc, IdentifierInfo *Ident)
Definition: ParsedAttr.cpp:28