//===--- ParseHLSL.cpp - HLSL-specific parsing support --------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements the parsing logic for HLSL language features. // //===----------------------------------------------------------------------===// #include "clang/AST/Attr.h" #include "clang/Basic/AttributeCommonInfo.h" #include "clang/Parse/ParseDiagnostic.h" #include "clang/Parse/Parser.h" #include "clang/Parse/RAIIObjectsForParser.h" using namespace clang; static bool validateDeclsInsideHLSLBuffer(Parser::DeclGroupPtrTy DG, SourceLocation BufferLoc, bool IsCBuffer, Parser &P) { // The parse is failed, just return false. if (!DG) return false; DeclGroupRef Decls = DG.get(); bool IsValid = true; // Only allow function, variable, record decls inside HLSLBuffer. for (DeclGroupRef::iterator I = Decls.begin(), E = Decls.end(); I != E; ++I) { Decl *D = *I; if (isa(D)) continue; // FIXME: support nested HLSLBuffer and namespace inside HLSLBuffer. if (isa(D)) { P.Diag(D->getLocation(), diag::err_invalid_declaration_in_hlsl_buffer) << IsCBuffer; IsValid = false; continue; } IsValid = false; P.Diag(D->getLocation(), diag::err_invalid_declaration_in_hlsl_buffer) << IsCBuffer; } return IsValid; } Decl *Parser::ParseHLSLBuffer(SourceLocation &DeclEnd) { assert((Tok.is(tok::kw_cbuffer) || Tok.is(tok::kw_tbuffer)) && "Not a cbuffer or tbuffer!"); bool IsCBuffer = Tok.is(tok::kw_cbuffer); SourceLocation BufferLoc = ConsumeToken(); // Eat the 'cbuffer' or 'tbuffer'. if (!Tok.is(tok::identifier)) { Diag(Tok, diag::err_expected) << tok::identifier; return nullptr; } IdentifierInfo *Identifier = Tok.getIdentifierInfo(); SourceLocation IdentifierLoc = ConsumeToken(); ParsedAttributes Attrs(AttrFactory); MaybeParseHLSLSemantics(Attrs, nullptr); ParseScope BufferScope(this, Scope::DeclScope); BalancedDelimiterTracker T(*this, tok::l_brace); if (T.consumeOpen()) { Diag(Tok, diag::err_expected) << tok::l_brace; return nullptr; } Decl *D = Actions.ActOnStartHLSLBuffer(getCurScope(), IsCBuffer, BufferLoc, Identifier, IdentifierLoc, T.getOpenLocation()); while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) { // FIXME: support attribute on constants inside cbuffer/tbuffer. ParsedAttributes DeclAttrs(AttrFactory); ParsedAttributes EmptyDeclSpecAttrs(AttrFactory); DeclGroupPtrTy Result = ParseExternalDeclaration(DeclAttrs, EmptyDeclSpecAttrs); if (!validateDeclsInsideHLSLBuffer(Result, IdentifierLoc, IsCBuffer, *this)) { T.skipToEnd(); DeclEnd = T.getCloseLocation(); BufferScope.Exit(); Actions.ActOnFinishHLSLBuffer(D, DeclEnd); return nullptr; } } T.consumeClose(); DeclEnd = T.getCloseLocation(); BufferScope.Exit(); Actions.ActOnFinishHLSLBuffer(D, DeclEnd); Actions.ProcessDeclAttributeList(Actions.CurScope, D, Attrs); return D; } static void fixSeparateAttrArgAndNumber(StringRef ArgStr, SourceLocation ArgLoc, Token Tok, ArgsVector &ArgExprs, Parser &P, ASTContext &Ctx, Preprocessor &PP) { StringRef Num = StringRef(Tok.getLiteralData(), Tok.getLength()); SourceLocation EndNumLoc = Tok.getEndLoc(); P.ConsumeToken(); // consume constant. std::string FixedArg = ArgStr.str() + Num.str(); P.Diag(ArgLoc, diag::err_hlsl_separate_attr_arg_and_number) << FixedArg << FixItHint::CreateReplacement(SourceRange(ArgLoc, EndNumLoc), FixedArg); ArgsUnion &Slot = ArgExprs.back(); Slot = IdentifierLoc::create(Ctx, ArgLoc, PP.getIdentifierInfo(FixedArg)); } void Parser::ParseHLSLSemantics(ParsedAttributes &Attrs, SourceLocation *EndLoc) { // FIXME: HLSLSemantic is shared for Semantic and resource binding which is // confusing. Need a better name to avoid misunderstanding. Issue // https://github.com/llvm/llvm-project/issues/57882 assert(Tok.is(tok::colon) && "Not a HLSL Semantic"); ConsumeToken(); IdentifierInfo *II = nullptr; if (Tok.is(tok::kw_register)) II = PP.getIdentifierInfo("register"); else if (Tok.is(tok::identifier)) II = Tok.getIdentifierInfo(); if (!II) { Diag(Tok.getLocation(), diag::err_expected_semantic_identifier); return; } SourceLocation Loc = ConsumeToken(); if (EndLoc) *EndLoc = Tok.getLocation(); ParsedAttr::Kind AttrKind = ParsedAttr::getParsedKind(II, nullptr, ParsedAttr::AS_HLSLSemantic); ArgsVector ArgExprs; switch (AttrKind) { case ParsedAttr::AT_HLSLResourceBinding: { if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after)) { SkipUntil(tok::r_paren, StopAtSemi); // skip through ) return; } if (!Tok.is(tok::identifier)) { Diag(Tok.getLocation(), diag::err_expected) << tok::identifier; SkipUntil(tok::r_paren, StopAtSemi); // skip through ) return; } StringRef SlotStr = Tok.getIdentifierInfo()->getName(); SourceLocation SlotLoc = Tok.getLocation(); ArgExprs.push_back(ParseIdentifierLoc()); // Add numeric_constant for fix-it. if (SlotStr.size() == 1 && Tok.is(tok::numeric_constant)) fixSeparateAttrArgAndNumber(SlotStr, SlotLoc, Tok, ArgExprs, *this, Actions.Context, PP); if (Tok.is(tok::comma)) { ConsumeToken(); // consume comma if (!Tok.is(tok::identifier)) { Diag(Tok.getLocation(), diag::err_expected) << tok::identifier; SkipUntil(tok::r_paren, StopAtSemi); // skip through ) return; } StringRef SpaceStr = Tok.getIdentifierInfo()->getName(); SourceLocation SpaceLoc = Tok.getLocation(); ArgExprs.push_back(ParseIdentifierLoc()); // Add numeric_constant for fix-it. if (SpaceStr.equals("space") && Tok.is(tok::numeric_constant)) fixSeparateAttrArgAndNumber(SpaceStr, SpaceLoc, Tok, ArgExprs, *this, Actions.Context, PP); } if (ExpectAndConsume(tok::r_paren, diag::err_expected)) { SkipUntil(tok::r_paren, StopAtSemi); // skip through ) return; } } break; case ParsedAttr::UnknownAttribute: Diag(Loc, diag::err_unknown_hlsl_semantic) << II; return; case ParsedAttr::AT_HLSLSV_GroupIndex: case ParsedAttr::AT_HLSLSV_DispatchThreadID: break; default: llvm_unreachable("invalid HLSL Semantic"); break; } Attrs.addNew(II, Loc, nullptr, SourceLocation(), ArgExprs.data(), ArgExprs.size(), ParsedAttr::AS_HLSLSemantic); }