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@+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 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@+2 {{invalid declaration inside cbuffer}} + template<typename T> + T foo(T t) { return t;} +} + +cbuffer A { + // expected-error@+2 {{invalid declaration inside cbuffer}} + template<typename T> + 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 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@+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 <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@+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; +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits