Coverage Report

Created: 2024-01-17 10:31

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