[clang] 782ac21 - [HLSL] Support cbuffer/tbuffer for hlsl.
Xiang Li via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 21 10:07:54 PDT 2022
Author: Xiang Li
Date: 2022-09-21T10:07:43-07:00
New Revision: 782ac2182c2b02de775c0f5a3d935613f2b748f5
URL: https://github.com/llvm/llvm-project/commit/782ac2182c2b02de775c0f5a3d935613f2b748f5
DIFF: https://github.com/llvm/llvm-project/commit/782ac2182c2b02de775c0f5a3d935613f2b748f5.diff
LOG: [HLSL] Support cbuffer/tbuffer for hlsl.
This is first part for support cbuffer/tbuffer.
The format for cbuffer/tbuffer is
BufferType [Name] [: register(b#)] { VariableDeclaration [: packoffset(c#.xyzw)]; ... };
More details at https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-constants
New keyword 'cbuffer' and 'tbuffer' are added.
New AST node HLSLBufferDecl is added.
Build AST for simple cbuffer/tbuffer without attribute support.
The special thing is variables declared inside cbuffer is exposed into global scope.
So isTransparentContext should return true for HLSLBuffer.
Reviewed By: aaron.ballman
Differential Revision: https://reviews.llvm.org/D129883
Added:
clang/lib/Sema/SemaHLSL.cpp
clang/test/AST/HLSL/Inputs/empty.hlsl
clang/test/AST/HLSL/ast-dump-comment-cbuffe-tbufferr.hlsl
clang/test/AST/HLSL/cbuffer_tbuffer.hlsl
clang/test/AST/HLSL/pch_hlsl_buffer.hlsl
clang/test/ParserHLSL/cb_error.hlsl
clang/test/ParserHLSL/invalid_inside_cb.hlsl
clang/test/SemaHLSL/cb_error.hlsl
Modified:
clang/include/clang/AST/Decl.h
clang/include/clang/AST/JSONNodeDumper.h
clang/include/clang/AST/RecursiveASTVisitor.h
clang/include/clang/AST/TextNodeDumper.h
clang/include/clang/Basic/DeclNodes.td
clang/include/clang/Basic/DiagnosticParseKinds.td
clang/include/clang/Basic/TokenKinds.def
clang/include/clang/Parse/Parser.h
clang/include/clang/Sema/Sema.h
clang/include/clang/Serialization/ASTBitCodes.h
clang/lib/AST/Decl.cpp
clang/lib/AST/DeclBase.cpp
clang/lib/AST/DeclPrinter.cpp
clang/lib/AST/JSONNodeDumper.cpp
clang/lib/AST/TextNodeDumper.cpp
clang/lib/Basic/IdentifierTable.cpp
clang/lib/Parse/ParseDecl.cpp
clang/lib/Parse/ParseHLSL.cpp
clang/lib/Parse/Parser.cpp
clang/lib/Sema/CMakeLists.txt
clang/lib/Sema/IdentifierResolver.cpp
clang/lib/Sema/SemaDecl.cpp
clang/lib/Sema/SemaLookup.cpp
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
clang/lib/Serialization/ASTCommon.cpp
clang/lib/Serialization/ASTReaderDecl.cpp
clang/lib/Serialization/ASTWriter.cpp
clang/lib/Serialization/ASTWriterDecl.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 1b414b5c0c194..c850fa3f5d41e 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -4671,6 +4671,51 @@ class EmptyDecl : public Decl {
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<DeclContext *>(const_cast<HLSLBufferDecl *>(D));
+ }
+ static HLSLBufferDecl *castFromDeclContext(const DeclContext *DC) {
+ return static_cast<HLSLBufferDecl *>(const_cast<DeclContext *>(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
index 3597903695797..c0c902da7c0d9 100644
--- a/clang/include/clang/AST/JSONNodeDumper.h
+++ b/clang/include/clang/AST/JSONNodeDumper.h
@@ -246,6 +246,7 @@ class JSONNodeDumper
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
index 054a7436ff749..c4017b7b64315 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1538,6 +1538,8 @@ DEF_TRAVERSE_DECL(CapturedDecl, {
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
index e6853b12ae7e5..c7d9dce9c6b2d 100644
--- a/clang/include/clang/AST/TextNodeDumper.h
+++ b/clang/include/clang/AST/TextNodeDumper.h
@@ -381,6 +381,7 @@ class TextNodeDumper
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
index 1ac9b399aba24..386c0a1b3dbaa 100644
--- a/clang/include/clang/Basic/DeclNodes.td
+++ b/clang/include/clang/Basic/DeclNodes.td
@@ -108,4 +108,4 @@ def OMPRequires : DeclNode<Decl>;
def Empty : DeclNode<Decl>;
def RequiresExprBody : DeclNode<Decl>, DeclContext;
def LifetimeExtendedTemporary : DeclNode<Decl>;
-
+def HLSLBuffer : DeclNode<Named, "HLSLBuffer">, DeclContext;
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 0293c2c03b529..76d40c338d641 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1618,6 +1618,8 @@ def note_max_tokens_total_override : Note<"total token limit set here">;
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
index f6a16cc019de2..0a3a30514de34 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -604,6 +604,10 @@ KEYWORD(addrspace_cast , KEYOPENCLCXX)
// 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
index 273af97ef241b..72123993be6b2 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -2821,6 +2821,7 @@ class Parser : public CodeCompletionHandler {
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
index e880ee61d5d21..2ca7a20a99545 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -5962,6 +5962,12 @@ class Sema final {
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
index 8b0d2c0de1fe5..3bbd2723be676 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1511,7 +1511,10 @@ enum DeclCode {
/// 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
index 957610ef36b74..47da043b0ad99 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -5210,6 +5210,40 @@ EmptyDecl *EmptyDecl::CreateDeserialized(ASTContext &C, unsigned ID) {
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
index 837ff90d6e34f..6c6ffc8c45716 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -750,6 +750,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
case ObjCMethod:
case ObjCProperty:
case MSProperty:
+ case HLSLBuffer:
return IDNS_Ordinary;
case Label:
return IDNS_Label;
@@ -1193,7 +1194,7 @@ bool DeclContext::isTransparentContext() const {
if (getDeclKind() == Decl::Enum)
return !cast<EnumDecl>(this)->isScoped();
- return getDeclKind() == Decl::LinkageSpec || getDeclKind() == Decl::Export;
+ return isa<LinkageSpecDecl, ExportDecl, HLSLBufferDecl>(this);
}
static bool isLinkageSpecContext(const DeclContext *DC,
@@ -1258,6 +1259,15 @@ DeclContext *DeclContext::getPrimaryContext() {
// 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<TranslationUnitDecl *>(this)->getFirstDecl();
case Decl::Namespace:
diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index b041e2a67e95f..ad90e536c0def 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -108,6 +108,7 @@ namespace {
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 @@ void DeclPrinter::VisitDeclContext(DeclContext *DC, bool Indent) {
Terminator = nullptr;
else
Terminator = ";";
- } else if (isa<NamespaceDecl>(*D) || isa<LinkageSpecDecl>(*D) ||
- isa<ObjCImplementationDecl>(*D) ||
- isa<ObjCInterfaceDecl>(*D) ||
- isa<ObjCProtocolDecl>(*D) ||
- isa<ObjCCategoryImplDecl>(*D) ||
- isa<ObjCCategoryDecl>(*D))
+ } else if (isa<NamespaceDecl, LinkageSpecDecl, ObjCImplementationDecl,
+ ObjCInterfaceDecl, ObjCProtocolDecl, ObjCCategoryImplDecl,
+ ObjCCategoryDecl, HLSLBufferDecl>(*D))
Terminator = nullptr;
else if (isa<EnumConstantDecl>(*D)) {
DeclContext::decl_iterator Next = D;
@@ -1658,6 +1656,21 @@ void DeclPrinter::VisitOMPThreadPrivateDecl(OMPThreadPrivateDecl *D) {
}
}
+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
index 1f063e83d6f76..14828e2f5670c 100644
--- a/clang/lib/AST/JSONNodeDumper.cpp
+++ b/clang/lib/AST/JSONNodeDumper.cpp
@@ -901,6 +901,11 @@ void JSONNodeDumper::VisitCXXRecordDecl(const CXXRecordDecl *RD) {
}
}
+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
index f9f149bc883f6..9bc643426c9e8 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -2388,3 +2388,11 @@ void TextNodeDumper::VisitCompoundStmt(const CompoundStmt *S) {
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
index 2035ca2f7ce17..63b08d8d04596 100644
--- a/clang/lib/Basic/IdentifierTable.cpp
+++ b/clang/lib/Basic/IdentifierTable.cpp
@@ -109,7 +109,8 @@ namespace {
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 @@ static KeywordStatus getKeywordStatusHelper(const LangOptions &LangOpts,
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/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index b18f5f2a85e3d..d6a2a08bf28b7 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -1787,6 +1787,11 @@ Parser::DeclGroupPtrTy Parser::ParseDeclaration(DeclaratorContext Context,
}
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
index ed1f81dc4ee8b..1d74aa0b09078 100644
--- a/clang/lib/Parse/ParseHLSL.cpp
+++ b/clang/lib/Parse/ParseHLSL.cpp
@@ -13,9 +13,89 @@
#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<CXXRecordDecl, RecordDecl, FunctionDecl, VarDecl>(D))
+ continue;
+
+ // FIXME: support nested HLSLBuffer and namespace inside HLSLBuffer.
+ if (isa<HLSLBufferDecl, NamespaceDecl>(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);
+
+ 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
index 249341ebc755a..73e5e1b5f15a0 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -947,6 +947,16 @@ Parser::DeclGroupPtrTy Parser::ParseExternalDeclaration(ParsedAttributes &Attrs,
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
index ffe3e1bb9e2e6..a7a97a1c53e77 100644
--- a/clang/lib/Sema/CMakeLists.txt
+++ b/clang/lib/Sema/CMakeLists.txt
@@ -44,6 +44,7 @@ add_clang_library(clangSema
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
index 4ee10f9fddfbe..607dc3111e9d0 100644
--- a/clang/lib/Sema/IdentifierResolver.cpp
+++ b/clang/lib/Sema/IdentifierResolver.cpp
@@ -99,7 +99,11 @@ IdentifierResolver::~IdentifierResolver() {
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<HLSLBufferDecl>(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
index 865388ccd5fa8..a916db238389f 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -7123,6 +7123,9 @@ static bool shouldConsiderLinkage(const VarDecl *VD) {
return true;
if (DC->isRecord())
return false;
+ if (DC->getDeclKind() == Decl::HLSLBuffer)
+ return false;
+
if (isa<RequiresExprBodyDecl>(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
index 0000000000000..cf82cc9bccdf5
--- /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<HLSLBufferDecl>(Dcl);
+ BufDecl->setRBraceLoc(RBrace);
+ PopDeclContext();
+}
diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index 70a32bd737160..70d0ce0baae06 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -519,7 +519,8 @@ void LookupResult::resolveKind() {
D = cast<NamedDecl>(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<HLSLBufferDecl>(D)) && !(I == 0 && N == 1)) {
Decls[I] = Decls[--N];
continue;
}
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index b0256a16babe7..4ffc23f643416 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -876,6 +876,10 @@ TemplateDeclInstantiator::VisitTranslationUnitDecl(TranslationUnitDecl *D) {
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
index 26b722b6b14a4..53e4c889f5298 100644
--- a/clang/lib/Serialization/ASTCommon.cpp
+++ b/clang/lib/Serialization/ASTCommon.cpp
@@ -433,6 +433,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) {
case Decl::LifetimeExtendedTemporary:
case Decl::RequiresExprBody:
case Decl::UnresolvedUsingIfExists:
+ case Decl::HLSLBuffer:
return false;
// These indirectly derive from Redeclarable<T> but are not actually
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 674b524a00e38..0b51b4ed8863b 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -322,6 +322,7 @@ namespace clang {
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::VisitNamespaceDecl(NamespaceDecl *D) {
}
}
+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);
@@ -3855,6 +3865,9 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) {
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
index 838c6e306cfb0..76a997c864e5f 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -1017,6 +1017,7 @@ void ASTWriter::WriteBlockInfoBlock() {
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
index 14985cc32d0e4..802b00884633f 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -131,10 +131,9 @@ namespace clang {
void VisitCapturedDecl(CapturedDecl *D);
void VisitEmptyDecl(EmptyDecl *D);
void VisitLifetimeExtendedTemporaryDecl(LifetimeExtendedTemporaryDecl *D);
-
void VisitDeclContext(DeclContext *DC);
template <typename T> void VisitRedeclarable(Redeclarable<T> *D);
-
+ void VisitHLSLBufferDecl(HLSLBufferDecl *D);
// FIXME: Put in the same order is DeclNodes.td?
void VisitObjCMethodDecl(ObjCMethodDecl *D);
@@ -1864,6 +1863,17 @@ void ASTDeclWriter::VisitRedeclarable(Redeclarable<T> *D) {
}
}
+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
index 0000000000000..e69de29bb2d1d
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
index 0000000000000..a98dc0f4ce431
--- /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 {{.*}}<line:10:4, col:17>
+// AST-NEXT:`-ParagraphComment {{.*}}<col:4, col:17>
+// AST-NEXT:`-TextComment {{.*}}<col:4, col:17> Text=" CBuffer decl."
+// AST-NEXT:-VarDecl {{.*}}<line:15:5, col:11> col:11 a 'float'
+// AST-NEXT:`-VarDecl {{.*}}<line:19:5, col:9> col:9 b 'int'
+// AST-NEXT:HLSLBufferDecl {{.*}}<line:29:1, line:38:1> line:29:9 tbuffer B
+// AST-NEXT:-FullComment {{.*}}<line:28:4, col:17>
+// AST-NEXT: `-ParagraphComment {{.*}}<col:4, col:17>
+// AST-NEXT: `-TextComment {{.*}}<col:4, col:17> Text=" TBuffer decl."
+// AST-NEXT:-VarDecl {{.*}}<line:33:5, col:11> col:11 c 'float'
+// AST-NEXT:`-VarDecl {{.*}} <line:37:5, col:9> 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
index 0000000000000..7204dcd16e0a9
--- /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]+}} <col:10, col:14> 'float' '+'
+// CHECK-NEXT: ImplicitCastExpr 0x{{[0-9a-f]+}} <col:10> 'float' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr 0x{{[0-9a-f]+}} <col:10> 'float' lvalue Var 0x[[A]] 'a' 'float'
+// CHECK-NEXT: ImplicitCastExpr 0x{{[0-9a-f]+}} <col:14> 'float' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr 0x{{[0-9a-f]+}} <col:14> '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
index 0000000000000..0277d4756db88
--- /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 <undeserialized declarations> cbuffer A
+// CHECK-NEXT:`-VarDecl 0x[[A:[0-9a-f]+]] <line:8:3, col:9> col:9 imported used a 'float'
+// CHECK-NEXT:HLSLBufferDecl 0x{{[0-9a-f]+}} <line:11:1, line:13:1> line:11:9 imported <undeserialized declarations> tbuffer B
+// CHECK-NEXT:`-VarDecl 0x[[B:[0-9a-f]+]] <line:12:3, col:9> col:9 imported used b 'float'
+// CHECK-NEXT:FunctionDecl 0x{{[0-9a-f]+}} <line:15:1, line:17:1> line:15:7 imported foo 'float ()'
+// CHECK-NEXT:CompoundStmt 0x{{[0-9a-f]+}} <col:13, line:17:1>
+// CHECK-NEXT:ReturnStmt 0x{{[0-9a-f]+}} <line:16:3, col:14>
+// CHECK-NEXT:BinaryOperator 0x{{[0-9a-f]+}} <col:10, col:14> 'float' '+'
+// CHECK-NEXT:ImplicitCastExpr 0x{{[0-9a-f]+}} <col:10> 'float' <LValueToRValue>
+// CHECK-NEXT:`-DeclRefExpr 0x{{[0-9a-f]+}} <col:10> 'float' lvalue Var 0x[[A]] 'a' 'float'
+// CHECK-NEXT:`-ImplicitCastExpr 0x{{[0-9a-f]+}} <col:14> 'float' <LValueToRValue>
+// CHECK-NEXT:`-DeclRefExpr 0x{{[0-9a-f]+}} <col:14> '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
index 0000000000000..245bf0a03fac9
--- /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 at +2 {{expected identifier}}
+// expected-error at +1 {{expected unqualified-id}}
+cbuffer { ... };
+// expected-error at +1 {{expected '{'}}
+cbuffer missing_definition;
+// expected-error at +1 {{expected unqualified-id}}
+int cbuffer;
+// expected-error at +1 {{expected identifier}}
+cbuffer;
+
+// expected-error at +2 {{expected identifier}}
+// expected-error at +1 {{expected unqualified-id}}
+tbuffer { ... };
+// expected-error at +1 {{expected '{'}}
+tbuffer missing_definition;
+// expected-error at +1 {{expected unqualified-id}}
+int tbuffer;
+// expected-error at +1 {{expected identifier}}
+tbuffer;
+
+// expected-error at +1 {{expected unqualified-id}}
+cbuffer A {}, B{}
+
+// cbuffer inside namespace is supported.
+namespace N {
+ cbuffer A {
+ float g;
+ }
+}
+
+cbuffer A {
+ // expected-error at +1 {{invalid declaration inside cbuffer}}
+ namespace N {
+ }
+}
+
+cbuffer A {
+ // expected-error at +1 {{invalid declaration inside cbuffer}}
+ cbuffer Nested {
+ }
+}
+
+struct S {
+ // expected-error at +1 {{expected member name or ';' after declaration specifiers}}
+ cbuffer what {
+ int y;
+ }
+};
+
+void func() {
+ // expected-error at +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
index 0000000000000..af35a301c21e7
--- /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 at +2 {{invalid declaration inside cbuffer}}
+ template<typename T>
+ T foo(T t) { return t;}
+}
+
+cbuffer A {
+ // expected-error at +2 {{invalid declaration inside cbuffer}}
+ template<typename T>
+ struct S { float s;};
+}
+
+// typealias not allowed inside cbuffer.
+cbuffer A {
+ // expected-error at +2 {{invalid declaration inside cbuffer}}
+ // expected-warning at +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
index 0000000000000..133adeeb2068b
--- /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 at +1 {{declared here}}
+cbuffer a {
+ int x;
+};
+
+int foo() {
+ // expected-error at +1 {{'a' does not refer to a value}}
+ return sizeof(a);
+}
+
+// expected-error at +1 {{expected unqualified-id}}
+template <typename Ty> 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 at +2 {{unknown type name 'oh'}}
+ // expected-error at +1 {{expected ';' after top level declarator}}
+ oh no!
+ // expected-warning at +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 at +1 {{unknown type name 'flaot'}}
+ flaot f;
+}
More information about the cfe-commits
mailing list