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#include "clang/Sema/SemaHLSL.h"
19
20using namespace clang;
21
22static bool validateDeclsInsideHLSLBuffer(Parser::DeclGroupPtrTy DG,
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>(Val: D))
34 continue;
35
36 // FIXME: support nested HLSLBuffer and namespace inside HLSLBuffer.
37 if (isa<HLSLBufferDecl, NamespaceDecl>(Val: 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(K: tok::kw_cbuffer);
55 SourceLocation BufferLoc = ConsumeToken(); // Eat the 'cbuffer' or 'tbuffer'.
56
57 if (!Tok.is(K: tok::identifier)) {
58 Diag(Tok, diag::err_expected) << tok::identifier;
59 return nullptr;
60 }
61
62 IdentifierInfo *Identifier = Tok.getIdentifierInfo();
63 SourceLocation IdentifierLoc = ConsumeToken();
64
65 ParsedAttributes Attrs(AttrFactory);
66 MaybeParseHLSLAnnotations(Attrs, EndLoc: 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(BufferScope: getCurScope(), CBuffer: IsCBuffer, KwLoc: BufferLoc,
76 Ident: Identifier, IdentLoc: IdentifierLoc,
77 LBrace: T.getOpenLocation());
78
79 while (Tok.isNot(K: tok::r_brace) && Tok.isNot(K: tok::eof)) {
80 // FIXME: support attribute on constants inside cbuffer/tbuffer.
81 ParsedAttributes DeclAttrs(AttrFactory);
82 ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
83
84 DeclGroupPtrTy Result =
85 ParseExternalDeclaration(DeclAttrs, DeclSpecAttrs&: EmptyDeclSpecAttrs);
86 if (!validateDeclsInsideHLSLBuffer(DG: Result, BufferLoc: IdentifierLoc, IsCBuffer,
87 P&: *this)) {
88 T.skipToEnd();
89 DeclEnd = T.getCloseLocation();
90 BufferScope.Exit();
91 Actions.HLSL().ActOnFinishBuffer(Dcl: D, RBrace: DeclEnd);
92 return nullptr;
93 }
94 }
95
96 T.consumeClose();
97 DeclEnd = T.getCloseLocation();
98 BufferScope.Exit();
99 Actions.HLSL().ActOnFinishBuffer(Dcl: D, RBrace: DeclEnd);
100
101 Actions.ProcessDeclAttributeList(S: Actions.CurScope, D, AttrList: 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, Loc: ArgLoc, Ident: PP.getIdentifierInfo(Name: FixedArg));
119}
120
121void Parser::ParseHLSLAnnotations(ParsedAttributes &Attrs,
122 SourceLocation *EndLoc) {
123
124 assert(Tok.is(tok::colon) && "Not a HLSL Annotation");
125 ConsumeToken();
126
127 IdentifierInfo *II = nullptr;
128 if (Tok.is(K: tok::kw_register))
129 II = PP.getIdentifierInfo(Name: "register");
130 else if (Tok.is(K: tok::identifier))
131 II = Tok.getIdentifierInfo();
132
133 if (!II) {
134 Diag(Tok.getLocation(), diag::err_expected_semantic_identifier);
135 return;
136 }
137
138 SourceLocation Loc = ConsumeToken();
139 if (EndLoc)
140 *EndLoc = Tok.getLocation();
141 ParsedAttr::Kind AttrKind =
142 ParsedAttr::getParsedKind(Name: II, Scope: nullptr, SyntaxUsed: ParsedAttr::AS_HLSLAnnotation);
143
144 ArgsVector ArgExprs;
145 switch (AttrKind) {
146 case ParsedAttr::AT_HLSLResourceBinding: {
147 if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after)) {
148 SkipUntil(T: tok::r_paren, Flags: StopAtSemi); // skip through )
149 return;
150 }
151 if (!Tok.is(K: tok::identifier)) {
152 Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
153 SkipUntil(T: tok::r_paren, Flags: StopAtSemi); // skip through )
154 return;
155 }
156 StringRef SlotStr = Tok.getIdentifierInfo()->getName();
157 SourceLocation SlotLoc = Tok.getLocation();
158 ArgExprs.push_back(Elt: ParseIdentifierLoc());
159
160 // Add numeric_constant for fix-it.
161 if (SlotStr.size() == 1 && Tok.is(K: tok::numeric_constant))
162 fixSeparateAttrArgAndNumber(ArgStr: SlotStr, ArgLoc: SlotLoc, Tok, ArgExprs, P&: *this,
163 Ctx&: Actions.Context, PP);
164
165 if (Tok.is(K: tok::comma)) {
166 ConsumeToken(); // consume comma
167 if (!Tok.is(K: tok::identifier)) {
168 Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
169 SkipUntil(T: tok::r_paren, Flags: StopAtSemi); // skip through )
170 return;
171 }
172 StringRef SpaceStr = Tok.getIdentifierInfo()->getName();
173 SourceLocation SpaceLoc = Tok.getLocation();
174 ArgExprs.push_back(Elt: ParseIdentifierLoc());
175
176 // Add numeric_constant for fix-it.
177 if (SpaceStr.equals(RHS: "space") && Tok.is(K: tok::numeric_constant))
178 fixSeparateAttrArgAndNumber(ArgStr: SpaceStr, ArgLoc: SpaceLoc, Tok, ArgExprs, P&: *this,
179 Ctx&: Actions.Context, PP);
180 }
181 if (ExpectAndConsume(tok::r_paren, diag::err_expected)) {
182 SkipUntil(T: tok::r_paren, Flags: StopAtSemi); // skip through )
183 return;
184 }
185 } break;
186 case ParsedAttr::UnknownAttribute:
187 Diag(Loc, diag::err_unknown_hlsl_semantic) << II;
188 return;
189 case ParsedAttr::AT_HLSLSV_GroupIndex:
190 case ParsedAttr::AT_HLSLSV_DispatchThreadID:
191 break;
192 default:
193 llvm_unreachable("invalid HLSL Annotation");
194 break;
195 }
196
197 Attrs.addNew(attrName: II, attrRange: Loc, scopeName: nullptr, scopeLoc: SourceLocation(), args: ArgExprs.data(),
198 numArgs: ArgExprs.size(), form: ParsedAttr::Form::HLSLAnnotation());
199}
200

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