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 | |
20 | using namespace clang; |
21 | |
22 | static 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 | |
51 | Decl *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 | |
105 | static 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 | |
121 | void 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 | |