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