1 //===--- HLSLExternalSemaSource.cpp - HLSL Sema Source --------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
10 //===----------------------------------------------------------------------===//
12 #include "clang/Sema/HLSLExternalSemaSource.h"
13 #include "clang/AST/ASTContext.h"
14 #include "clang/AST/Attr.h"
15 #include "clang/AST/DeclCXX.h"
16 #include "clang/Basic/AttrKinds.h"
17 #include "clang/Basic/HLSLRuntime.h"
18 #include "clang/Sema/Lookup.h"
19 #include "clang/Sema/Sema.h"
20 #include "llvm/Frontend/HLSL/HLSLResource.h"
24 using namespace clang
;
25 using namespace llvm::hlsl
;
29 struct TemplateParameterListBuilder
;
31 struct BuiltinTypeDeclBuilder
{
32 CXXRecordDecl
*Record
= nullptr;
33 ClassTemplateDecl
*Template
= nullptr;
34 ClassTemplateDecl
*PrevTemplate
= nullptr;
35 NamespaceDecl
*HLSLNamespace
= nullptr;
36 llvm::StringMap
<FieldDecl
*> Fields
;
38 BuiltinTypeDeclBuilder(CXXRecordDecl
*R
) : Record(R
) {
39 Record
->startDefinition();
40 Template
= Record
->getDescribedClassTemplate();
43 BuiltinTypeDeclBuilder(Sema
&S
, NamespaceDecl
*Namespace
, StringRef Name
)
44 : HLSLNamespace(Namespace
) {
45 ASTContext
&AST
= S
.getASTContext();
46 IdentifierInfo
&II
= AST
.Idents
.get(Name
, tok::TokenKind::identifier
);
48 LookupResult
Result(S
, &II
, SourceLocation(), Sema::LookupTagName
);
49 CXXRecordDecl
*PrevDecl
= nullptr;
50 if (S
.LookupQualifiedName(Result
, HLSLNamespace
)) {
51 NamedDecl
*Found
= Result
.getFoundDecl();
52 if (auto *TD
= dyn_cast
<ClassTemplateDecl
>(Found
)) {
53 PrevDecl
= TD
->getTemplatedDecl();
56 PrevDecl
= dyn_cast
<CXXRecordDecl
>(Found
);
57 assert(PrevDecl
&& "Unexpected lookup result type.");
60 if (PrevDecl
&& PrevDecl
->isCompleteDefinition()) {
65 Record
= CXXRecordDecl::Create(AST
, TagDecl::TagKind::Class
, HLSLNamespace
,
66 SourceLocation(), SourceLocation(), &II
,
68 Record
->setImplicit(true);
69 Record
->setLexicalDeclContext(HLSLNamespace
);
70 Record
->setHasExternalLexicalStorage();
72 // Don't let anyone derive from built-in types.
73 Record
->addAttr(FinalAttr::CreateImplicit(AST
, SourceRange(),
74 FinalAttr::Keyword_final
));
77 ~BuiltinTypeDeclBuilder() {
78 if (HLSLNamespace
&& !Template
&& Record
->getDeclContext() == HLSLNamespace
)
79 HLSLNamespace
->addDecl(Record
);
82 BuiltinTypeDeclBuilder
&
83 addMemberVariable(StringRef Name
, QualType Type
,
84 AccessSpecifier Access
= AccessSpecifier::AS_private
) {
85 if (Record
->isCompleteDefinition())
87 assert(Record
->isBeingDefined() &&
88 "Definition must be started before adding members!");
89 ASTContext
&AST
= Record
->getASTContext();
91 IdentifierInfo
&II
= AST
.Idents
.get(Name
, tok::TokenKind::identifier
);
92 TypeSourceInfo
*MemTySource
=
93 AST
.getTrivialTypeSourceInfo(Type
, SourceLocation());
94 auto *Field
= FieldDecl::Create(
95 AST
, Record
, SourceLocation(), SourceLocation(), &II
, Type
, MemTySource
,
96 nullptr, false, InClassInitStyle::ICIS_NoInit
);
97 Field
->setAccess(Access
);
98 Field
->setImplicit(true);
99 Record
->addDecl(Field
);
100 Fields
[Name
] = Field
;
104 BuiltinTypeDeclBuilder
&
105 addHandleMember(AccessSpecifier Access
= AccessSpecifier::AS_private
) {
106 if (Record
->isCompleteDefinition())
108 QualType Ty
= Record
->getASTContext().VoidPtrTy
;
110 if (const auto *TTD
= dyn_cast
<TemplateTypeParmDecl
>(
111 Template
->getTemplateParameters()->getParam(0)))
112 Ty
= Record
->getASTContext().getPointerType(
113 QualType(TTD
->getTypeForDecl(), 0));
115 return addMemberVariable("h", Ty
, Access
);
118 BuiltinTypeDeclBuilder
&annotateResourceClass(ResourceClass RC
,
120 if (Record
->isCompleteDefinition())
123 HLSLResourceAttr::CreateImplicit(Record
->getASTContext(), RC
, RK
));
127 static DeclRefExpr
*lookupBuiltinFunction(ASTContext
&AST
, Sema
&S
,
130 IdentifierInfo
&II
= AST
.Idents
.get(Name
, tok::TokenKind::identifier
);
131 DeclarationNameInfo NameInfo
=
132 DeclarationNameInfo(DeclarationName(&II
), SourceLocation());
133 LookupResult
R(S
, NameInfo
, Sema::LookupOrdinaryName
);
134 S
.LookupParsedName(R
, S
.getCurScope(), &SS
, false);
135 assert(R
.isSingleResult() &&
136 "Since this is a builtin it should always resolve!");
137 auto *VD
= cast
<ValueDecl
>(R
.getFoundDecl());
138 QualType Ty
= VD
->getType();
139 return DeclRefExpr::Create(AST
, NestedNameSpecifierLoc(), SourceLocation(),
140 VD
, false, NameInfo
, Ty
, VK_PRValue
);
143 static Expr
*emitResourceClassExpr(ASTContext
&AST
, ResourceClass RC
) {
144 return IntegerLiteral::Create(
146 llvm::APInt(AST
.getIntWidth(AST
.UnsignedCharTy
),
147 static_cast<uint8_t>(RC
)),
148 AST
.UnsignedCharTy
, SourceLocation());
151 BuiltinTypeDeclBuilder
&addDefaultHandleConstructor(Sema
&S
,
153 if (Record
->isCompleteDefinition())
155 ASTContext
&AST
= Record
->getASTContext();
157 QualType ConstructorType
=
158 AST
.getFunctionType(AST
.VoidTy
, {}, FunctionProtoType::ExtProtoInfo());
160 CanQualType CanTy
= Record
->getTypeForDecl()->getCanonicalTypeUnqualified();
161 DeclarationName Name
= AST
.DeclarationNames
.getCXXConstructorName(CanTy
);
162 CXXConstructorDecl
*Constructor
= CXXConstructorDecl::Create(
163 AST
, Record
, SourceLocation(),
164 DeclarationNameInfo(Name
, SourceLocation()), ConstructorType
,
165 AST
.getTrivialTypeSourceInfo(ConstructorType
, SourceLocation()),
166 ExplicitSpecifier(), false, true, false,
167 ConstexprSpecKind::Unspecified
);
170 lookupBuiltinFunction(AST
, S
, "__builtin_hlsl_create_handle");
172 Expr
*RCExpr
= emitResourceClassExpr(AST
, RC
);
173 Expr
*Call
= CallExpr::Create(AST
, Fn
, {RCExpr
}, AST
.VoidPtrTy
, VK_PRValue
,
174 SourceLocation(), FPOptionsOverride());
176 CXXThisExpr
*This
= CXXThisExpr::Create(
177 AST
, SourceLocation(), Constructor
->getFunctionObjectParameterType(),
179 Expr
*Handle
= MemberExpr::CreateImplicit(AST
, This
, false, Fields
["h"],
180 Fields
["h"]->getType(), VK_LValue
,
183 // If the handle isn't a void pointer, cast the builtin result to the
185 if (Handle
->getType().getCanonicalType() != AST
.VoidPtrTy
) {
186 Call
= CXXStaticCastExpr::Create(
187 AST
, Handle
->getType(), VK_PRValue
, CK_Dependent
, Call
, nullptr,
188 AST
.getTrivialTypeSourceInfo(Handle
->getType(), SourceLocation()),
189 FPOptionsOverride(), SourceLocation(), SourceLocation(),
193 BinaryOperator
*Assign
= BinaryOperator::Create(
194 AST
, Handle
, Call
, BO_Assign
, Handle
->getType(), VK_LValue
, OK_Ordinary
,
195 SourceLocation(), FPOptionsOverride());
197 Constructor
->setBody(
198 CompoundStmt::Create(AST
, {Assign
}, FPOptionsOverride(),
199 SourceLocation(), SourceLocation()));
200 Constructor
->setAccess(AccessSpecifier::AS_public
);
201 Record
->addDecl(Constructor
);
205 BuiltinTypeDeclBuilder
&addArraySubscriptOperators() {
206 if (Record
->isCompleteDefinition())
208 addArraySubscriptOperator(true);
209 addArraySubscriptOperator(false);
213 BuiltinTypeDeclBuilder
&addArraySubscriptOperator(bool IsConst
) {
214 if (Record
->isCompleteDefinition())
216 assert(Fields
.count("h") > 0 &&
217 "Subscript operator must be added after the handle.");
219 FieldDecl
*Handle
= Fields
["h"];
220 ASTContext
&AST
= Record
->getASTContext();
222 assert(Handle
->getType().getCanonicalType() != AST
.VoidPtrTy
&&
223 "Not yet supported for void pointer handles.");
226 QualType(Handle
->getType()->getPointeeOrArrayElementType(), 0);
227 QualType ReturnTy
= ElemTy
;
229 FunctionProtoType::ExtProtoInfo ExtInfo
;
231 // Subscript operators return references to elements, const makes the
232 // reference and method const so that the underlying data is not mutable.
233 ReturnTy
= AST
.getLValueReferenceType(ReturnTy
);
235 ExtInfo
.TypeQuals
.addConst();
240 AST
.getFunctionType(ReturnTy
, {AST
.UnsignedIntTy
}, ExtInfo
);
241 auto *TSInfo
= AST
.getTrivialTypeSourceInfo(MethodTy
, SourceLocation());
242 auto *MethodDecl
= CXXMethodDecl::Create(
243 AST
, Record
, SourceLocation(),
245 AST
.DeclarationNames
.getCXXOperatorName(OO_Subscript
),
247 MethodTy
, TSInfo
, SC_None
, false, false, ConstexprSpecKind::Unspecified
,
250 IdentifierInfo
&II
= AST
.Idents
.get("Idx", tok::TokenKind::identifier
);
251 auto *IdxParam
= ParmVarDecl::Create(
252 AST
, MethodDecl
->getDeclContext(), SourceLocation(), SourceLocation(),
253 &II
, AST
.UnsignedIntTy
,
254 AST
.getTrivialTypeSourceInfo(AST
.UnsignedIntTy
, SourceLocation()),
256 MethodDecl
->setParams({IdxParam
});
258 // Also add the parameter to the function prototype.
259 auto FnProtoLoc
= TSInfo
->getTypeLoc().getAs
<FunctionProtoTypeLoc
>();
260 FnProtoLoc
.setParam(0, IdxParam
);
263 CXXThisExpr::Create(AST
, SourceLocation(),
264 MethodDecl
->getFunctionObjectParameterType(), true);
265 auto *HandleAccess
= MemberExpr::CreateImplicit(
266 AST
, This
, false, Handle
, Handle
->getType(), VK_LValue
, OK_Ordinary
);
268 auto *IndexExpr
= DeclRefExpr::Create(
269 AST
, NestedNameSpecifierLoc(), SourceLocation(), IdxParam
, false,
270 DeclarationNameInfo(IdxParam
->getDeclName(), SourceLocation()),
271 AST
.UnsignedIntTy
, VK_PRValue
);
274 new (AST
) ArraySubscriptExpr(HandleAccess
, IndexExpr
, ElemTy
, VK_LValue
,
275 OK_Ordinary
, SourceLocation());
277 auto *Return
= ReturnStmt::Create(AST
, SourceLocation(), Array
, nullptr);
279 MethodDecl
->setBody(CompoundStmt::Create(AST
, {Return
}, FPOptionsOverride(),
282 MethodDecl
->setLexicalDeclContext(Record
);
283 MethodDecl
->setAccess(AccessSpecifier::AS_public
);
284 MethodDecl
->addAttr(AlwaysInlineAttr::CreateImplicit(
285 AST
, SourceRange(), AlwaysInlineAttr::CXX11_clang_always_inline
));
286 Record
->addDecl(MethodDecl
);
291 BuiltinTypeDeclBuilder
&startDefinition() {
292 if (Record
->isCompleteDefinition())
294 Record
->startDefinition();
298 BuiltinTypeDeclBuilder
&completeDefinition() {
299 if (Record
->isCompleteDefinition())
301 assert(Record
->isBeingDefined() &&
302 "Definition must be started before completing it.");
304 Record
->completeDefinition();
308 TemplateParameterListBuilder
addTemplateArgumentList();
311 struct TemplateParameterListBuilder
{
312 BuiltinTypeDeclBuilder
&Builder
;
314 llvm::SmallVector
<NamedDecl
*> Params
;
316 TemplateParameterListBuilder(BuiltinTypeDeclBuilder
&RB
)
317 : Builder(RB
), AST(RB
.Record
->getASTContext()) {}
319 ~TemplateParameterListBuilder() { finalizeTemplateArgs(); }
321 TemplateParameterListBuilder
&
322 addTypeParameter(StringRef Name
, QualType DefaultValue
= QualType()) {
323 if (Builder
.Record
->isCompleteDefinition())
325 unsigned Position
= static_cast<unsigned>(Params
.size());
326 auto *Decl
= TemplateTypeParmDecl::Create(
327 AST
, Builder
.Record
->getDeclContext(), SourceLocation(),
328 SourceLocation(), /* TemplateDepth */ 0, Position
,
329 &AST
.Idents
.get(Name
, tok::TokenKind::identifier
), /* Typename */ false,
330 /* ParameterPack */ false);
331 if (!DefaultValue
.isNull())
332 Decl
->setDefaultArgument(AST
.getTrivialTypeSourceInfo(DefaultValue
));
334 Params
.emplace_back(Decl
);
338 BuiltinTypeDeclBuilder
&finalizeTemplateArgs() {
342 TemplateParameterList::Create(AST
, SourceLocation(), SourceLocation(),
343 Params
, SourceLocation(), nullptr);
344 Builder
.Template
= ClassTemplateDecl::Create(
345 AST
, Builder
.Record
->getDeclContext(), SourceLocation(),
346 DeclarationName(Builder
.Record
->getIdentifier()), ParamList
,
348 Builder
.Record
->setDescribedClassTemplate(Builder
.Template
);
349 Builder
.Template
->setImplicit(true);
350 Builder
.Template
->setLexicalDeclContext(Builder
.Record
->getDeclContext());
351 // NOTE: setPreviousDecl before addDecl so new decl replace old decl when
353 Builder
.Template
->setPreviousDecl(Builder
.PrevTemplate
);
354 Builder
.Record
->getDeclContext()->addDecl(Builder
.Template
);
357 QualType T
= Builder
.Template
->getInjectedClassNameSpecialization();
358 T
= AST
.getInjectedClassNameType(Builder
.Record
, T
);
364 TemplateParameterListBuilder
BuiltinTypeDeclBuilder::addTemplateArgumentList() {
365 return TemplateParameterListBuilder(*this);
369 HLSLExternalSemaSource::~HLSLExternalSemaSource() {}
371 void HLSLExternalSemaSource::InitializeSema(Sema
&S
) {
373 ASTContext
&AST
= SemaPtr
->getASTContext();
374 // If the translation unit has external storage force external decls to load.
375 if (AST
.getTranslationUnitDecl()->hasExternalLexicalStorage())
376 (void)AST
.getTranslationUnitDecl()->decls_begin();
378 IdentifierInfo
&HLSL
= AST
.Idents
.get("hlsl", tok::TokenKind::identifier
);
379 LookupResult
Result(S
, &HLSL
, SourceLocation(), Sema::LookupNamespaceName
);
380 NamespaceDecl
*PrevDecl
= nullptr;
381 if (S
.LookupQualifiedName(Result
, AST
.getTranslationUnitDecl()))
382 PrevDecl
= Result
.getAsSingle
<NamespaceDecl
>();
383 HLSLNamespace
= NamespaceDecl::Create(
384 AST
, AST
.getTranslationUnitDecl(), /*Inline=*/false, SourceLocation(),
385 SourceLocation(), &HLSL
, PrevDecl
, /*Nested=*/false);
386 HLSLNamespace
->setImplicit(true);
387 HLSLNamespace
->setHasExternalLexicalStorage();
388 AST
.getTranslationUnitDecl()->addDecl(HLSLNamespace
);
390 // Force external decls in the HLSL namespace to load from the PCH.
391 (void)HLSLNamespace
->getCanonicalDecl()->decls_begin();
392 defineTrivialHLSLTypes();
393 forwardDeclareHLSLTypes();
395 // This adds a `using namespace hlsl` directive. In DXC, we don't put HLSL's
396 // built in types inside a namespace, but we are planning to change that in
397 // the near future. In order to be source compatible older versions of HLSL
398 // will need to implicitly use the hlsl namespace. For now in clang everything
399 // will get added to the namespace, and we can remove the using directive for
400 // future language versions to match HLSL's evolution.
401 auto *UsingDecl
= UsingDirectiveDecl::Create(
402 AST
, AST
.getTranslationUnitDecl(), SourceLocation(), SourceLocation(),
403 NestedNameSpecifierLoc(), SourceLocation(), HLSLNamespace
,
404 AST
.getTranslationUnitDecl());
406 AST
.getTranslationUnitDecl()->addDecl(UsingDecl
);
409 void HLSLExternalSemaSource::defineHLSLVectorAlias() {
410 ASTContext
&AST
= SemaPtr
->getASTContext();
412 llvm::SmallVector
<NamedDecl
*> TemplateParams
;
414 auto *TypeParam
= TemplateTypeParmDecl::Create(
415 AST
, HLSLNamespace
, SourceLocation(), SourceLocation(), 0, 0,
416 &AST
.Idents
.get("element", tok::TokenKind::identifier
), false, false);
417 TypeParam
->setDefaultArgument(AST
.getTrivialTypeSourceInfo(AST
.FloatTy
));
419 TemplateParams
.emplace_back(TypeParam
);
421 auto *SizeParam
= NonTypeTemplateParmDecl::Create(
422 AST
, HLSLNamespace
, SourceLocation(), SourceLocation(), 0, 1,
423 &AST
.Idents
.get("element_count", tok::TokenKind::identifier
), AST
.IntTy
,
424 false, AST
.getTrivialTypeSourceInfo(AST
.IntTy
));
426 IntegerLiteral::Create(AST
, llvm::APInt(AST
.getIntWidth(AST
.IntTy
), 4),
427 AST
.IntTy
, SourceLocation());
428 SizeParam
->setDefaultArgument(LiteralExpr
);
429 TemplateParams
.emplace_back(SizeParam
);
432 TemplateParameterList::Create(AST
, SourceLocation(), SourceLocation(),
433 TemplateParams
, SourceLocation(), nullptr);
435 IdentifierInfo
&II
= AST
.Idents
.get("vector", tok::TokenKind::identifier
);
437 QualType AliasType
= AST
.getDependentSizedExtVectorType(
438 AST
.getTemplateTypeParmType(0, 0, false, TypeParam
),
440 AST
, NestedNameSpecifierLoc(), SourceLocation(), SizeParam
, false,
441 DeclarationNameInfo(SizeParam
->getDeclName(), SourceLocation()),
442 AST
.IntTy
, VK_LValue
),
445 auto *Record
= TypeAliasDecl::Create(AST
, HLSLNamespace
, SourceLocation(),
446 SourceLocation(), &II
,
447 AST
.getTrivialTypeSourceInfo(AliasType
));
448 Record
->setImplicit(true);
451 TypeAliasTemplateDecl::Create(AST
, HLSLNamespace
, SourceLocation(),
452 Record
->getIdentifier(), ParamList
, Record
);
454 Record
->setDescribedAliasTemplate(Template
);
455 Template
->setImplicit(true);
456 Template
->setLexicalDeclContext(Record
->getDeclContext());
457 HLSLNamespace
->addDecl(Template
);
460 void HLSLExternalSemaSource::defineTrivialHLSLTypes() {
461 defineHLSLVectorAlias();
463 ResourceDecl
= BuiltinTypeDeclBuilder(*SemaPtr
, HLSLNamespace
, "Resource")
465 .addHandleMember(AccessSpecifier::AS_public
)
466 .completeDefinition()
470 void HLSLExternalSemaSource::forwardDeclareHLSLTypes() {
472 Decl
= BuiltinTypeDeclBuilder(*SemaPtr
, HLSLNamespace
, "RWBuffer")
473 .addTemplateArgumentList()
474 .addTypeParameter("element_type", SemaPtr
->getASTContext().FloatTy
)
475 .finalizeTemplateArgs()
477 if (!Decl
->isCompleteDefinition())
479 std::make_pair(Decl
->getCanonicalDecl(),
480 std::bind(&HLSLExternalSemaSource::completeBufferType
,
481 this, std::placeholders::_1
)));
484 void HLSLExternalSemaSource::CompleteType(TagDecl
*Tag
) {
485 if (!isa
<CXXRecordDecl
>(Tag
))
487 auto Record
= cast
<CXXRecordDecl
>(Tag
);
489 // If this is a specialization, we need to get the underlying templated
490 // declaration and complete that.
491 if (auto TDecl
= dyn_cast
<ClassTemplateSpecializationDecl
>(Record
))
492 Record
= TDecl
->getSpecializedTemplate()->getTemplatedDecl();
493 Record
= Record
->getCanonicalDecl();
494 auto It
= Completions
.find(Record
);
495 if (It
== Completions
.end())
500 void HLSLExternalSemaSource::completeBufferType(CXXRecordDecl
*Record
) {
501 BuiltinTypeDeclBuilder(Record
)
503 .addDefaultHandleConstructor(*SemaPtr
, ResourceClass::UAV
)
504 .addArraySubscriptOperators()
505 .annotateResourceClass(ResourceClass::UAV
, ResourceKind::TypedBuffer
)
506 .completeDefinition();