diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -4671,6 +4671,51 @@ static bool classofKind(Kind K) { return K == Empty; } }; +/// HLSLBufferDecl - Represent a cbuffer or tbuffer declaration. +class HLSLBufferDecl final : public NamedDecl, public DeclContext { + /// LBraceLoc - The ending location of the source range. + SourceLocation LBraceLoc; + /// RBraceLoc - The ending location of the source range. + SourceLocation RBraceLoc; + /// KwLoc - The location of the cbuffer or tbuffer keyword. + SourceLocation KwLoc; + /// IsCBuffer - Whether the buffer is a cbuffer (and not a tbuffer). + bool IsCBuffer; + + HLSLBufferDecl(DeclContext *DC, bool CBuffer, SourceLocation KwLoc, + IdentifierInfo *ID, SourceLocation IDLoc, + SourceLocation LBrace); + +public: + static HLSLBufferDecl *Create(ASTContext &C, DeclContext *LexicalParent, + bool CBuffer, SourceLocation KwLoc, + IdentifierInfo *ID, SourceLocation IDLoc, + SourceLocation LBrace); + static HLSLBufferDecl *CreateDeserialized(ASTContext &C, unsigned ID); + + SourceRange getSourceRange() const LLVM_READONLY { + return SourceRange(getLocStart(), RBraceLoc); + } + SourceLocation getLocStart() const LLVM_READONLY { return KwLoc; } + SourceLocation getLBraceLoc() const { return LBraceLoc; } + SourceLocation getRBraceLoc() const { return RBraceLoc; } + void setRBraceLoc(SourceLocation L) { RBraceLoc = L; } + bool isCBuffer() const { return IsCBuffer; } + + // Implement isa/cast/dyncast/etc. + static bool classof(const Decl *D) { return classofKind(D->getKind()); } + static bool classofKind(Kind K) { return K == HLSLBuffer; } + static DeclContext *castToDeclContext(const HLSLBufferDecl *D) { + return static_cast(const_cast(D)); + } + static HLSLBufferDecl *castFromDeclContext(const DeclContext *DC) { + return static_cast(const_cast(DC)); + } + + friend class ASTDeclReader; + friend class ASTDeclWriter; +}; + /// Insertion operator for diagnostics. This allows sending NamedDecl's /// into a diagnostic with <<. inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &PD, diff --git a/clang/include/clang/AST/JSONNodeDumper.h b/clang/include/clang/AST/JSONNodeDumper.h --- a/clang/include/clang/AST/JSONNodeDumper.h +++ b/clang/include/clang/AST/JSONNodeDumper.h @@ -246,6 +246,7 @@ void VisitEnumConstantDecl(const EnumConstantDecl *ECD); void VisitRecordDecl(const RecordDecl *RD); void VisitCXXRecordDecl(const CXXRecordDecl *RD); + void VisitHLSLBufferDecl(const HLSLBufferDecl *D); void VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D); void VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *D); void VisitTemplateTemplateParmDecl(const TemplateTemplateParmDecl *D); diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1551,6 +1551,8 @@ DEF_TRAVERSE_DECL(EmptyDecl, {}) +DEF_TRAVERSE_DECL(HLSLBufferDecl, {}) + DEF_TRAVERSE_DECL(LifetimeExtendedTemporaryDecl, { TRY_TO(TraverseStmt(D->getTemporaryExpr())); }) diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h --- a/clang/include/clang/AST/TextNodeDumper.h +++ b/clang/include/clang/AST/TextNodeDumper.h @@ -381,6 +381,7 @@ void VisitConceptDecl(const ConceptDecl *D); void VisitLifetimeExtendedTemporaryDecl(const LifetimeExtendedTemporaryDecl *D); + void VisitHLSLBufferDecl(const HLSLBufferDecl *D); }; } // namespace clang diff --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td --- a/clang/include/clang/Basic/DeclNodes.td +++ b/clang/include/clang/Basic/DeclNodes.td @@ -108,4 +108,4 @@ def Empty : DeclNode; def RequiresExprBody : DeclNode, DeclContext; def LifetimeExtendedTemporary : DeclNode; - +def HLSLBuffer : DeclNode, DeclContext; diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1605,6 +1605,8 @@ def err_expected_semantic_identifier : Error< "expected HLSL Semantic identifier">; +def err_invalid_declaration_in_hlsl_buffer : Error< + "invalid declaration inside %select{tbuffer|cbuffer}0">; def err_unknown_hlsl_semantic : Error<"unknown HLSL semantic %0">; def ext_hlsl_access_specifiers : ExtWarn< "access specifiers are a clang HLSL extension">, diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -595,6 +595,10 @@ // CUDA/HIP function attributes KEYWORD(__noinline__ , KEYCUDA) +// HLSL keywords. +KEYWORD(cbuffer , KEYHLSL) +KEYWORD(tbuffer , KEYHLSL) + // OpenMP Type Traits UNARY_EXPR_OR_TYPE_TRAIT(__builtin_omp_required_simd_align, OpenMPRequiredSimdAlign, KEYALL) diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2818,6 +2818,7 @@ void ParseHLSLSemantics(ParsedAttributes &Attrs, SourceLocation *EndLoc = nullptr); + Decl *ParseHLSLBuffer(SourceLocation &DeclEnd); void MaybeParseMicrosoftAttributes(ParsedAttributes &Attrs) { if ((getLangOpts().MicrosoftExt || getLangOpts().HLSL) && diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5926,6 +5926,12 @@ SourceLocation BuiltinLoc, SourceLocation RParenLoc); + //===---------------------------- HLSL Features -------------------------===// + Decl *ActOnStartHLSLBuffer(Scope *BufferScope, bool CBuffer, + SourceLocation KwLoc, IdentifierInfo *Ident, + SourceLocation IdentLoc, SourceLocation LBrace); + void ActOnFinishHLSLBuffer(Decl *Dcl, SourceLocation RBrace); + //===---------------------------- C++ Features --------------------------===// // Act on C++ namespaces diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1508,7 +1508,10 @@ /// A UnnamedGlobalConstantDecl record. DECL_UNNAMED_GLOBAL_CONSTANT, - DECL_LAST = DECL_UNNAMED_GLOBAL_CONSTANT + /// A HLSLBufferDecl record. + DECL_HLSL_BUFFER, + + DECL_LAST = DECL_HLSL_BUFFER }; /// Record codes for each kind of statement or expression. diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -5210,6 +5210,40 @@ return new (C, ID) EmptyDecl(nullptr, SourceLocation()); } +HLSLBufferDecl::HLSLBufferDecl(DeclContext *DC, bool CBuffer, + SourceLocation KwLoc, IdentifierInfo *ID, + SourceLocation IDLoc, SourceLocation LBrace) + : NamedDecl(Decl::Kind::HLSLBuffer, DC, IDLoc, DeclarationName(ID)), + DeclContext(Decl::Kind::HLSLBuffer), LBraceLoc(LBrace), KwLoc(KwLoc), + IsCBuffer(CBuffer) {} + +HLSLBufferDecl *HLSLBufferDecl::Create(ASTContext &C, + DeclContext *LexicalParent, bool CBuffer, + SourceLocation KwLoc, IdentifierInfo *ID, + SourceLocation IDLoc, + SourceLocation LBrace) { + // For hlsl like this + // cbuffer A { + // cbuffer B { + // } + // } + // compiler should treat it as + // cbuffer A { + // } + // cbuffer B { + // } + // FIXME: support nested buffers if required for back-compat. + DeclContext *DC = LexicalParent; + HLSLBufferDecl *Result = + new (C, DC) HLSLBufferDecl(DC, CBuffer, KwLoc, ID, IDLoc, LBrace); + return Result; +} + +HLSLBufferDecl *HLSLBufferDecl::CreateDeserialized(ASTContext &C, unsigned ID) { + return new (C, ID) HLSLBufferDecl(nullptr, false, SourceLocation(), nullptr, + SourceLocation(), SourceLocation()); +} + //===----------------------------------------------------------------------===// // ImportDecl Implementation //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -750,6 +750,7 @@ case ObjCMethod: case ObjCProperty: case MSProperty: + case HLSLBuffer: return IDNS_Ordinary; case Label: return IDNS_Label; @@ -1193,7 +1194,7 @@ if (getDeclKind() == Decl::Enum) return !cast(this)->isScoped(); - return getDeclKind() == Decl::LinkageSpec || getDeclKind() == Decl::Export; + return isa(this); } static bool isLinkageSpecContext(const DeclContext *DC, @@ -1258,6 +1259,15 @@ // There is only one DeclContext for these entities. return this; + case Decl::HLSLBuffer: + // Each buffer, even with the same name, is a distinct construct. + // Multiple buffers with the same name are allowed for backward + // compatibility. + // As long as buffers have unique resource bindings the names don't matter. + // The names get exposed via the CPU-side reflection API which + // supports querying bindings, so we cannot remove them. + return this; + case Decl::TranslationUnit: return static_cast(this)->getFirstDecl(); case Decl::Namespace: diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -108,6 +108,7 @@ void VisitOMPCapturedExprDecl(OMPCapturedExprDecl *D); void VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *TTP); void VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *NTTP); + void VisitHLSLBufferDecl(HLSLBufferDecl *D); void printTemplateParameters(const TemplateParameterList *Params, bool OmitTemplateKW = false); @@ -462,12 +463,9 @@ Terminator = nullptr; else Terminator = ";"; - } else if (isa(*D) || isa(*D) || - isa(*D) || - isa(*D) || - isa(*D) || - isa(*D) || - isa(*D)) + } else if (isa(*D)) Terminator = nullptr; else if (isa(*D)) { DeclContext::decl_iterator Next = D; @@ -1658,6 +1656,21 @@ } } +void DeclPrinter::VisitHLSLBufferDecl(HLSLBufferDecl *D) { + if (D->isCBuffer()) + Out << "cbuffer "; + else + Out << "tbuffer "; + + Out << *D; + + prettyPrintAttributes(D); + + Out << " {\n"; + VisitDeclContext(D); + Indent() << "}"; +} + void DeclPrinter::VisitOMPAllocateDecl(OMPAllocateDecl *D) { Out << "#pragma omp allocate"; if (!D->varlist_empty()) { diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp --- a/clang/lib/AST/JSONNodeDumper.cpp +++ b/clang/lib/AST/JSONNodeDumper.cpp @@ -899,6 +899,11 @@ } } +void JSONNodeDumper::VisitHLSLBufferDecl(const HLSLBufferDecl *D) { + VisitNamedDecl(D); + JOS.attribute("bufferKind", D->isCBuffer() ? "cbuffer" : "tbuffer"); +} + void JSONNodeDumper::VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D) { VisitNamedDecl(D); JOS.attribute("tagUsed", D->wasDeclaredWithTypename() ? "typename" : "class"); diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -2386,3 +2386,11 @@ if (S->hasStoredFPFeatures()) printFPOptions(S->getStoredFPFeatures()); } + +void TextNodeDumper::VisitHLSLBufferDecl(const HLSLBufferDecl *D) { + if (D->isCBuffer()) + OS << " cbuffer"; + else + OS << " tbuffer"; + dumpName(D); +} diff --git a/clang/lib/Basic/IdentifierTable.cpp b/clang/lib/Basic/IdentifierTable.cpp --- a/clang/lib/Basic/IdentifierTable.cpp +++ b/clang/lib/Basic/IdentifierTable.cpp @@ -108,7 +108,8 @@ KEYMSCOMPAT = 0x400000, KEYSYCL = 0x800000, KEYCUDA = 0x1000000, - KEYMAX = KEYCUDA, // The maximum key + KEYHLSL = 0x2000000, + KEYMAX = KEYHLSL, // The maximum key KEYALLCXX = KEYCXX | KEYCXX11 | KEYCXX20, KEYALL = (KEYMAX | (KEYMAX-1)) & ~KEYNOMS18 & ~KEYNOOPENCL // KEYNOMS18 and KEYNOOPENCL are used to exclude. @@ -199,6 +200,8 @@ return LangOpts.isSYCL() ? KS_Enabled : KS_Unknown; case KEYCUDA: return LangOpts.CUDA ? KS_Enabled : KS_Unknown; + case KEYHLSL: + return LangOpts.HLSL ? KS_Enabled : KS_Unknown; case KEYNOCXX: // This is enabled in all non-C++ modes, but might be enabled for other // reasons as well. diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp --- a/clang/lib/Frontend/FrontendAction.cpp +++ b/clang/lib/Frontend/FrontendAction.cpp @@ -1017,7 +1017,7 @@ } // Setup HLSL External Sema Source - if (CI.getLangOpts().HLSL && CI.hasASTContext()) { + if (CI.getLangOpts().HLSL && CI.hasASTContext() && !CI.getASTContext().getExternalSource()) { IntrusiveRefCntPtr HLSLSema( new HLSLExternalSemaSource()); CI.getASTContext().setExternalSource(HLSLSema); diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -1787,6 +1787,11 @@ } return ParseSimpleDeclaration(Context, DeclEnd, DeclAttrs, DeclSpecAttrs, true, nullptr, DeclSpecStart); + + case tok::kw_cbuffer: + case tok::kw_tbuffer: + SingleDecl = ParseHLSLBuffer(DeclEnd); + break; case tok::kw_namespace: ProhibitAttributes(DeclAttrs); ProhibitAttributes(DeclSpecAttrs); diff --git a/clang/lib/Parse/ParseHLSL.cpp b/clang/lib/Parse/ParseHLSL.cpp --- a/clang/lib/Parse/ParseHLSL.cpp +++ b/clang/lib/Parse/ParseHLSL.cpp @@ -13,9 +13,91 @@ #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(); + + 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()); + + // FIXME: support attribute on cbuffer/tbuffer. + while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) { + SourceLocation Loc = Tok.getLocation(); + // FIXME: support attribute on constants inside cbuffer/tbuffer. + ParsedAttributes Attrs(AttrFactory); + MaybeParseCXX11Attributes(Attrs); + MaybeParseMicrosoftAttributes(Attrs); + + DeclGroupPtrTy Result = ParseExternalDeclaration(Attrs); + 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); + + return D; +} + void Parser::ParseHLSLSemantics(ParsedAttributes &Attrs, SourceLocation *EndLoc) { assert(Tok.is(tok::colon) && "Not a HLSL Semantic"); diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -947,6 +947,16 @@ EmptyDeclSpecAttrs); } + case tok::kw_cbuffer: + case tok::kw_tbuffer: + if (getLangOpts().HLSL) { + SourceLocation DeclEnd; + ParsedAttributes EmptyDeclSpecAttrs(AttrFactory); + return ParseDeclaration(DeclaratorContext::File, DeclEnd, Attrs, + EmptyDeclSpecAttrs); + } + goto dont_know; + case tok::kw_static: // Parse (then ignore) 'static' prior to a template instantiation. This is // a GCC extension that we intentionally do not support. diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -44,6 +44,7 @@ SemaExprMember.cpp SemaExprObjC.cpp SemaFixItUtils.cpp + SemaHLSL.cpp SemaInit.cpp SemaLambda.cpp SemaLookup.cpp diff --git a/clang/lib/Sema/IdentifierResolver.cpp b/clang/lib/Sema/IdentifierResolver.cpp --- a/clang/lib/Sema/IdentifierResolver.cpp +++ b/clang/lib/Sema/IdentifierResolver.cpp @@ -99,7 +99,11 @@ bool IdentifierResolver::isDeclInScope(Decl *D, DeclContext *Ctx, Scope *S, bool AllowInlineNamespace) const { Ctx = Ctx->getRedeclContext(); - + // The names for HLSL cbuffer/tbuffers only used by the CPU-side + // reflection API which supports querying bindings. It will not have name + // conflict with other Decls. + if (LangOpt.HLSL && isa(D)) + return false; if (Ctx->isFunctionOrMethod() || (S && S->isFunctionPrototypeScope())) { // Ignore the scopes associated within transparent declaration contexts. while (S->getEntity() && S->getEntity()->isTransparentContext()) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -7122,6 +7122,9 @@ return true; if (DC->isRecord()) return false; + if (DC->getDeclKind() == Decl::HLSLBuffer) + return false; + if (isa(DC)) return false; llvm_unreachable("Unexpected context"); diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -0,0 +1,34 @@ +//===- SemaHLSL.cpp - Semantic Analysis for HLSL constructs ---------------===// +// +// 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 implements Semantic Analysis for HLSL constructs. +//===----------------------------------------------------------------------===// + +#include "clang/Sema/Sema.h" + +using namespace clang; + +Decl *Sema::ActOnStartHLSLBuffer(Scope *BufferScope, bool CBuffer, + SourceLocation KwLoc, IdentifierInfo *Ident, + SourceLocation IdentLoc, + SourceLocation LBrace) { + // For anonymous namespace, take the location of the left brace. + DeclContext *LexicalParent = getCurLexicalContext(); + HLSLBufferDecl *Result = HLSLBufferDecl::Create( + Context, LexicalParent, CBuffer, KwLoc, Ident, IdentLoc, LBrace); + + PushOnScopeChains(Result, BufferScope); + PushDeclContext(BufferScope, Result); + + return Result; +} + +void Sema::ActOnFinishHLSLBuffer(Decl *Dcl, SourceLocation RBrace) { + auto *BufDecl = cast(Dcl); + BufDecl->setRBraceLoc(RBrace); + PopDeclContext(); +} diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -519,7 +519,8 @@ D = cast(D->getCanonicalDecl()); // Ignore an invalid declaration unless it's the only one left. - if (D->isInvalidDecl() && !(I == 0 && N == 1)) { + // Also ignore HLSLBufferDecl which not have name conflict with other Decls. + if ((D->isInvalidDecl() || isa(D)) && !(I == 0 && N == 1)) { Decls[I] = Decls[--N]; continue; } diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -873,6 +873,10 @@ llvm_unreachable("Translation units cannot be instantiated"); } +Decl *TemplateDeclInstantiator::VisitHLSLBufferDecl(HLSLBufferDecl *Decl) { + llvm_unreachable("HLSL buffer declarations cannot be instantiated"); +} + Decl * TemplateDeclInstantiator::VisitPragmaCommentDecl(PragmaCommentDecl *D) { llvm_unreachable("pragma comment cannot be instantiated"); diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp --- a/clang/lib/Serialization/ASTCommon.cpp +++ b/clang/lib/Serialization/ASTCommon.cpp @@ -433,6 +433,7 @@ case Decl::LifetimeExtendedTemporary: case Decl::RequiresExprBody: case Decl::UnresolvedUsingIfExists: + case Decl::HLSLBuffer: return false; // These indirectly derive from Redeclarable but are not actually diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -322,6 +322,7 @@ void VisitNamedDecl(NamedDecl *ND); void VisitLabelDecl(LabelDecl *LD); void VisitNamespaceDecl(NamespaceDecl *D); + void VisitHLSLBufferDecl(HLSLBufferDecl *D); void VisitUsingDirectiveDecl(UsingDirectiveDecl *D); void VisitNamespaceAliasDecl(NamespaceAliasDecl *D); void VisitTypeDecl(TypeDecl *TD); @@ -1735,6 +1736,15 @@ } } +void ASTDeclReader::VisitHLSLBufferDecl(HLSLBufferDecl *D) { + VisitNamedDecl(D); + VisitDeclContext(D); + D->IsCBuffer = Record.readBool(); + D->KwLoc = readSourceLocation(); + D->LBraceLoc = readSourceLocation(); + D->RBraceLoc = readSourceLocation(); +} + void ASTDeclReader::VisitNamespaceAliasDecl(NamespaceAliasDecl *D) { RedeclarableResult Redecl = VisitRedeclarable(D); VisitNamedDecl(D); @@ -3853,6 +3863,9 @@ case DECL_OBJC_TYPE_PARAM: D = ObjCTypeParamDecl::CreateDeserialized(Context, ID); break; + case DECL_HLSL_BUFFER: + D = HLSLBufferDecl::CreateDeserialized(Context, ID); + break; } assert(D && "Unknown declaration reading AST file"); diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -1016,6 +1016,7 @@ RECORD(DECL_PRAGMA_DETECT_MISMATCH); RECORD(DECL_OMP_DECLARE_REDUCTION); RECORD(DECL_OMP_ALLOCATE); + RECORD(DECL_HLSL_BUFFER); // Statements and Exprs can occur in the Decls and Types block. AddStmtsExprs(Stream, Record); diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -131,10 +131,9 @@ void VisitCapturedDecl(CapturedDecl *D); void VisitEmptyDecl(EmptyDecl *D); void VisitLifetimeExtendedTemporaryDecl(LifetimeExtendedTemporaryDecl *D); - void VisitDeclContext(DeclContext *DC); template void VisitRedeclarable(Redeclarable *D); - + void VisitHLSLBufferDecl(HLSLBufferDecl *D); // FIXME: Put in the same order is DeclNodes.td? void VisitObjCMethodDecl(ObjCMethodDecl *D); @@ -1864,6 +1863,17 @@ } } +void ASTDeclWriter::VisitHLSLBufferDecl(HLSLBufferDecl *D) { + VisitNamedDecl(D); + VisitDeclContext(D); + Record.push_back(D->isCBuffer()); + Record.AddSourceLocation(D->getLocStart()); + Record.AddSourceLocation(D->getLBraceLoc()); + Record.AddSourceLocation(D->getRBraceLoc()); + + Code = serialization::DECL_HLSL_BUFFER; +} + void ASTDeclWriter::VisitOMPThreadPrivateDecl(OMPThreadPrivateDecl *D) { Record.writeOMPChildren(D->Data); VisitDecl(D); diff --git a/clang/test/AST/HLSL/Inputs/empty.hlsl b/clang/test/AST/HLSL/Inputs/empty.hlsl new file mode 100644 diff --git a/clang/test/AST/HLSL/ast-dump-comment-cbuffe-tbufferr.hlsl b/clang/test/AST/HLSL/ast-dump-comment-cbuffe-tbufferr.hlsl new file mode 100644 --- /dev/null +++ b/clang/test/AST/HLSL/ast-dump-comment-cbuffe-tbufferr.hlsl @@ -0,0 +1,51 @@ +// RUN: %clang_cc1 -Wdocumentation -ast-dump=json -x hlsl -triple dxil-pc-shadermodel6.3-library %s | FileCheck %s --check-prefix=JSON +// RUN: %clang_cc1 -Wdocumentation -ast-dump -x hlsl -triple dxil-pc-shadermodel6.3-library %s | FileCheck %s --check-prefix=AST + +// JSON:"kind": "HLSLBufferDecl", +// JSON:"name": "A", +// JSON-NEXT:"bufferKind": "cbuffer", +// JSON:"kind": "TextComment", +// JSON:"text": " CBuffer decl." + +/// CBuffer decl. +cbuffer A { + // JSON: "kind": "VarDecl", + // JSON: "name": "a", + // JSON: "qualType": "float" + float a; + // JSON: "kind": "VarDecl", + // JSON: "name": "b", + // JSON: "qualType": "int" + int b; +} + +// JSON:"kind": "HLSLBufferDecl", +// JSON:"name": "B", +// JSON-NEXT:"bufferKind": "tbuffer", +// JSON:"kind": "TextComment", +// JSON:"text": " TBuffer decl." + +/// TBuffer decl. +tbuffer B { + // JSON: "kind": "VarDecl", + // JSON: "name": "c", + // JSON: "qualType": "float" + float c; + // JSON: "kind": "VarDecl", + // JSON: "name": "d", + // JSON: "qualType": "int" + int d; +} + +// AST:HLSLBufferDecl {{.*}}:11:1, line:20:1> line:11:9 cbuffer A +// AST-NEXT:FullComment {{.*}} +// AST-NEXT:`-ParagraphComment {{.*}} +// AST-NEXT:`-TextComment {{.*}} Text=" CBuffer decl." +// AST-NEXT:-VarDecl {{.*}} col:11 a 'float' +// AST-NEXT:`-VarDecl {{.*}} col:9 b 'int' +// AST-NEXT:HLSLBufferDecl {{.*}} line:29:9 tbuffer B +// AST-NEXT:-FullComment {{.*}} +// AST-NEXT: `-ParagraphComment {{.*}} +// AST-NEXT: `-TextComment {{.*}} Text=" TBuffer decl." +// AST-NEXT:-VarDecl {{.*}} col:11 c 'float' +// AST-NEXT:`-VarDecl {{.*}} col:9 d 'int' diff --git a/clang/test/AST/HLSL/cbuffer_tbuffer.hlsl b/clang/test/AST/HLSL/cbuffer_tbuffer.hlsl new file mode 100644 --- /dev/null +++ b/clang/test/AST/HLSL/cbuffer_tbuffer.hlsl @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -ast-dump -o - %s | FileCheck %s + +// CHECK:HLSLBufferDecl 0x[[CB:[0-9a-f]+]] {{.*}} line:5:9 cbuffer CB +// CHECK-NEXT:VarDecl 0x[[A:[0-9a-f]+]] {{.*}} col:9 used a 'float' +cbuffer CB { + float a; +} + +// CHECK:HLSLBufferDecl 0x[[TB:[0-9a-f]+]] {{.*}} line:11:9 tbuffer TB +// CHECK-NEXT:VarDecl 0x[[B:[0-9a-f]+]] {{.*}} col:9 used b 'float' +tbuffer TB { + float b; +} + +float foo() { +// CHECK: BinaryOperator 0x{{[0-9a-f]+}} 'float' '+' +// CHECK-NEXT: ImplicitCastExpr 0x{{[0-9a-f]+}} 'float' +// CHECK-NEXT: DeclRefExpr 0x{{[0-9a-f]+}} 'float' lvalue Var 0x[[A]] 'a' 'float' +// CHECK-NEXT: ImplicitCastExpr 0x{{[0-9a-f]+}} 'float' +// CHECK-NEXT: DeclRefExpr 0x{{[0-9a-f]+}} 'float' lvalue Var 0x[[B]] 'b' 'float' + return a + b; +} diff --git a/clang/test/AST/HLSL/pch_hlsl_buffer.hlsl b/clang/test/AST/HLSL/pch_hlsl_buffer.hlsl new file mode 100644 --- /dev/null +++ b/clang/test/AST/HLSL/pch_hlsl_buffer.hlsl @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl \ +// RUN: -emit-pch -o %t %s +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl \ +// RUN: -include-pch %t -fsyntax-only -ast-dump-all %S/Inputs/empty.hlsl \ +// RUN: | FileCheck %s + +cbuffer A { + float a; +} + +tbuffer B { + float b; +} + +float foo() { + return a + b; +} +// Make sure cbuffer/tbuffer works for PCH. +// CHECK:HLSLBufferDecl 0x{{[0-9a-f]+}} <{{.*}}:7:1, line:9:1> line:7:9 imported cbuffer A +// CHECK-NEXT:`-VarDecl 0x[[A:[0-9a-f]+]] col:9 imported used a 'float' +// CHECK-NEXT:HLSLBufferDecl 0x{{[0-9a-f]+}} line:11:9 imported tbuffer B +// CHECK-NEXT:`-VarDecl 0x[[B:[0-9a-f]+]] col:9 imported used b 'float' +// CHECK-NEXT:FunctionDecl 0x{{[0-9a-f]+}} line:15:7 imported foo 'float ()' +// CHECK-NEXT:CompoundStmt 0x{{[0-9a-f]+}} +// CHECK-NEXT:ReturnStmt 0x{{[0-9a-f]+}} +// CHECK-NEXT:BinaryOperator 0x{{[0-9a-f]+}} 'float' '+' +// CHECK-NEXT:ImplicitCastExpr 0x{{[0-9a-f]+}} 'float' +// CHECK-NEXT:`-DeclRefExpr 0x{{[0-9a-f]+}} 'float' lvalue Var 0x[[A]] 'a' 'float' +// CHECK-NEXT:`-ImplicitCastExpr 0x{{[0-9a-f]+}} 'float' +// CHECK-NEXT:`-DeclRefExpr 0x{{[0-9a-f]+}} 'float' lvalue Var 0x[[B]] 'b' 'float' diff --git a/clang/test/ParserHLSL/cb_error.hlsl b/clang/test/ParserHLSL/cb_error.hlsl new file mode 100644 --- /dev/null +++ b/clang/test/ParserHLSL/cb_error.hlsl @@ -0,0 +1,74 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify + +// expected-error@+2 {{expected identifier}} +// expected-error@+1 {{expected unqualified-id}} +cbuffer { ... }; +// expected-error@+1 {{expected '{'}} +cbuffer missing_definition; +// expected-error@+1 {{expected unqualified-id}} +int cbuffer; +// expected-error@+1 {{expected identifier}} +cbuffer; + +// expected-error@+2 {{expected identifier}} +// expected-error@+1 {{expected unqualified-id}} +tbuffer { ... }; +// expected-error@+1 {{expected '{'}} +tbuffer missing_definition; +// expected-error@+1 {{expected unqualified-id}} +int tbuffer; +// expected-error@+1 {{expected identifier}} +tbuffer; + +// expected-error@+1 {{expected unqualified-id}} +cbuffer A {}, B{} + +// cbuffer inside namespace is supported. +namespace N { + cbuffer A { + float g; + } +} + +cbuffer A { + // expected-error@+1 {{invalid declaration inside cbuffer}} + namespace N { + } +} + +cbuffer A { + // expected-error@+1 {{invalid declaration inside cbuffer}} + cbuffer Nested { + } +} + +struct S { + // expected-error@+1 {{expected member name or ';' after declaration specifiers}} + cbuffer what { + int y; + } +}; + +void func() { + // expected-error@+1 {{expected expression}} + tbuffer derp { + int z; + } + + decltype(derp) another { + int a; + } +} + +// struct decl inside cb is supported. +cbuffer A { + struct S2 { + float s; + }; + S2 s; +} + +// function decl inside cb is supported. +cbuffer A { + float foo_inside_cb() { return 1.2;} +} diff --git a/clang/test/ParserHLSL/invalid_inside_cb.hlsl b/clang/test/ParserHLSL/invalid_inside_cb.hlsl new file mode 100644 --- /dev/null +++ b/clang/test/ParserHLSL/invalid_inside_cb.hlsl @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify + +// template not allowed inside cbuffer. +cbuffer A { + // expected-error@+2 {{invalid declaration inside cbuffer}} + template + T foo(T t) { return t;} +} + +cbuffer A { + // expected-error@+2 {{invalid declaration inside cbuffer}} + template + struct S { float s;}; +} + +// typealias not allowed inside cbuffer. +cbuffer A { + // expected-error@+2 {{invalid declaration inside cbuffer}} + // expected-warning@+1 {{alias declarations are a C++11 extension}} + using F32 = float; +} diff --git a/clang/test/SemaHLSL/cb_error.hlsl b/clang/test/SemaHLSL/cb_error.hlsl new file mode 100644 --- /dev/null +++ b/clang/test/SemaHLSL/cb_error.hlsl @@ -0,0 +1,49 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify + +// expected-note@+1 {{declared here}} +cbuffer a { + int x; +}; + +int foo() { + // expected-error@+1 {{'a' does not refer to a value}} + return sizeof(a); +} + +// expected-error@+1 {{expected unqualified-id}} +template cbuffer a { Ty f; }; + +// For back-compat reason, it is OK for multiple cbuffer/tbuffer use same name in hlsl. +// And these cbuffer name only used for reflection, cannot be removed. +cbuffer A { + float A; +} + +cbuffer A { + float b; +} + +tbuffer A { + float a; +} + +float bar() { + // cbuffer/tbuffer name will not conflict with other variables. + return A; +} + +cbuffer a { + // expected-error@+2 {{unknown type name 'oh'}} + // expected-error@+1 {{expected ';' after top level declarator}} + oh no! + // expected-warning@+1 {{missing terminating ' character}} + this isn't even valid HLSL code + despite seeming totally reasonable + once you understand that HLSL + is so flaming weird. +} + +tbuffer B { + // expected-error@+1 {{unknown type name 'flaot'}} + flaot f; +}