| //===--- HLSLExternalSemaSource.cpp - HLSL Sema Source --------------------===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "clang/Sema/HLSLExternalSemaSource.h" |
| #include "clang/AST/ASTContext.h" |
| #include "clang/AST/Attr.h" |
| #include "clang/AST/DeclCXX.h" |
| #include "clang/Basic/AttrKinds.h" |
| #include "clang/Basic/HLSLRuntime.h" |
| #include "clang/Sema/Lookup.h" |
| #include "clang/Sema/Sema.h" |
| |
| #include <functional> |
| |
| using namespace clang; |
| using namespace hlsl; |
| |
| namespace { |
| |
| struct TemplateParameterListBuilder; |
| |
| struct BuiltinTypeDeclBuilder { |
| CXXRecordDecl *Record = nullptr; |
| ClassTemplateDecl *Template = nullptr; |
| NamespaceDecl *HLSLNamespace = nullptr; |
| llvm::StringMap<FieldDecl *> Fields; |
| |
| BuiltinTypeDeclBuilder(CXXRecordDecl *R) : Record(R) { |
| Record->startDefinition(); |
| Template = Record->getDescribedClassTemplate(); |
| } |
| |
| BuiltinTypeDeclBuilder(Sema &S, NamespaceDecl *Namespace, StringRef Name) |
| : HLSLNamespace(Namespace) { |
| ASTContext &AST = S.getASTContext(); |
| IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier); |
| |
| Record = CXXRecordDecl::Create(AST, TagDecl::TagKind::TTK_Class, |
| HLSLNamespace, SourceLocation(), |
| SourceLocation(), &II, nullptr, true); |
| Record->setImplicit(true); |
| Record->setLexicalDeclContext(HLSLNamespace); |
| Record->setHasExternalLexicalStorage(); |
| |
| // Don't let anyone derive from built-in types |
| Record->addAttr(FinalAttr::CreateImplicit(AST, SourceRange(), |
| AttributeCommonInfo::AS_Keyword, |
| FinalAttr::Keyword_final)); |
| } |
| |
| ~BuiltinTypeDeclBuilder() { |
| if (HLSLNamespace && !Template) |
| HLSLNamespace->addDecl(Record); |
| } |
| |
| BuiltinTypeDeclBuilder & |
| addTemplateArgumentList(llvm::ArrayRef<NamedDecl *> TemplateArgs) { |
| ASTContext &AST = Record->getASTContext(); |
| |
| auto *ParamList = |
| TemplateParameterList::Create(AST, SourceLocation(), SourceLocation(), |
| TemplateArgs, SourceLocation(), nullptr); |
| Template = ClassTemplateDecl::Create( |
| AST, Record->getDeclContext(), SourceLocation(), |
| DeclarationName(Record->getIdentifier()), ParamList, Record); |
| Record->setDescribedClassTemplate(Template); |
| Template->setImplicit(true); |
| Template->setLexicalDeclContext(Record->getDeclContext()); |
| Record->getDeclContext()->addDecl(Template); |
| |
| // Requesting the class name specialization will fault in required types. |
| QualType T = Template->getInjectedClassNameSpecialization(); |
| T = AST.getInjectedClassNameType(Record, T); |
| return *this; |
| } |
| |
| BuiltinTypeDeclBuilder & |
| addMemberVariable(StringRef Name, QualType Type, |
| AccessSpecifier Access = AccessSpecifier::AS_private) { |
| assert(Record->isBeingDefined() && |
| "Definition must be started before adding members!"); |
| ASTContext &AST = Record->getASTContext(); |
| |
| IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier); |
| TypeSourceInfo *MemTySource = |
| AST.getTrivialTypeSourceInfo(Type, SourceLocation()); |
| auto *Field = FieldDecl::Create( |
| AST, Record, SourceLocation(), SourceLocation(), &II, Type, MemTySource, |
| nullptr, false, InClassInitStyle::ICIS_NoInit); |
| Field->setAccess(Access); |
| Field->setImplicit(true); |
| Record->addDecl(Field); |
| Fields[Name] = Field; |
| return *this; |
| } |
| |
| BuiltinTypeDeclBuilder & |
| addHandleMember(AccessSpecifier Access = AccessSpecifier::AS_private) { |
| return addMemberVariable("h", Record->getASTContext().VoidPtrTy, Access); |
| } |
| |
| BuiltinTypeDeclBuilder & |
| annotateResourceClass(HLSLResourceAttr::ResourceClass RC) { |
| Record->addAttr( |
| HLSLResourceAttr::CreateImplicit(Record->getASTContext(), RC)); |
| return *this; |
| } |
| |
| static DeclRefExpr *lookupBuiltinFunction(ASTContext &AST, Sema &S, |
| StringRef Name) { |
| CXXScopeSpec SS; |
| IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier); |
| DeclarationNameInfo NameInfo = |
| DeclarationNameInfo(DeclarationName(&II), SourceLocation()); |
| LookupResult R(S, NameInfo, Sema::LookupOrdinaryName); |
| S.LookupParsedName(R, S.getCurScope(), &SS, false); |
| assert(R.isSingleResult() && |
| "Since this is a builtin it should always resolve!"); |
| auto *VD = cast<ValueDecl>(R.getFoundDecl()); |
| QualType Ty = VD->getType(); |
| return DeclRefExpr::Create(AST, NestedNameSpecifierLoc(), SourceLocation(), |
| VD, false, NameInfo, Ty, VK_PRValue); |
| } |
| |
| static Expr *emitResourceClassExpr(ASTContext &AST, ResourceClass RC) { |
| return IntegerLiteral::Create( |
| AST, |
| llvm::APInt(AST.getIntWidth(AST.UnsignedCharTy), |
| static_cast<uint8_t>(RC)), |
| AST.UnsignedCharTy, SourceLocation()); |
| } |
| |
| BuiltinTypeDeclBuilder &addDefaultHandleConstructor(Sema &S, |
| ResourceClass RC) { |
| ASTContext &AST = Record->getASTContext(); |
| |
| QualType ConstructorType = |
| AST.getFunctionType(AST.VoidTy, {}, FunctionProtoType::ExtProtoInfo()); |
| |
| CanQualType CanTy = Record->getTypeForDecl()->getCanonicalTypeUnqualified(); |
| DeclarationName Name = AST.DeclarationNames.getCXXConstructorName(CanTy); |
| CXXConstructorDecl *Constructor = CXXConstructorDecl::Create( |
| AST, Record, SourceLocation(), |
| DeclarationNameInfo(Name, SourceLocation()), ConstructorType, |
| AST.getTrivialTypeSourceInfo(ConstructorType, SourceLocation()), |
| ExplicitSpecifier(), false, true, false, |
| ConstexprSpecKind::Unspecified); |
| |
| DeclRefExpr *Fn = |
| lookupBuiltinFunction(AST, S, "__builtin_hlsl_create_handle"); |
| |
| Expr *RCExpr = emitResourceClassExpr(AST, RC); |
| CallExpr *Call = |
| CallExpr::Create(AST, Fn, {RCExpr}, AST.VoidPtrTy, VK_PRValue, |
| SourceLocation(), FPOptionsOverride()); |
| |
| CXXThisExpr *This = new (AST) |
| CXXThisExpr(SourceLocation(), Constructor->getThisType(), true); |
| MemberExpr *Handle = MemberExpr::CreateImplicit( |
| AST, This, true, Fields["h"], Fields["h"]->getType(), VK_LValue, |
| OK_Ordinary); |
| BinaryOperator *Assign = BinaryOperator::Create( |
| AST, Handle, Call, BO_Assign, Handle->getType(), VK_LValue, OK_Ordinary, |
| SourceLocation(), FPOptionsOverride()); |
| |
| Constructor->setBody( |
| CompoundStmt::Create(AST, {Assign}, FPOptionsOverride(), |
| SourceLocation(), SourceLocation())); |
| Constructor->setAccess(AccessSpecifier::AS_public); |
| Record->addDecl(Constructor); |
| return *this; |
| } |
| |
| BuiltinTypeDeclBuilder &startDefinition() { |
| Record->startDefinition(); |
| return *this; |
| } |
| |
| BuiltinTypeDeclBuilder &completeDefinition() { |
| assert(Record->isBeingDefined() && |
| "Definition must be started before completing it."); |
| |
| Record->completeDefinition(); |
| return *this; |
| } |
| |
| TemplateParameterListBuilder addTemplateArgumentList(); |
| }; |
| |
| struct TemplateParameterListBuilder { |
| BuiltinTypeDeclBuilder &Builder; |
| ASTContext &AST; |
| llvm::SmallVector<NamedDecl *> Params; |
| |
| TemplateParameterListBuilder(BuiltinTypeDeclBuilder &RB) |
| : Builder(RB), AST(RB.Record->getASTContext()) {} |
| |
| ~TemplateParameterListBuilder() { finalizeTemplateArgs(); } |
| |
| TemplateParameterListBuilder & |
| addTypeParameter(StringRef Name, QualType DefaultValue = QualType()) { |
| unsigned Position = static_cast<unsigned>(Params.size()); |
| auto *Decl = TemplateTypeParmDecl::Create( |
| AST, Builder.Record->getDeclContext(), SourceLocation(), |
| SourceLocation(), /* TemplateDepth */ 0, Position, |
| &AST.Idents.get(Name, tok::TokenKind::identifier), /* Typename */ false, |
| /* ParameterPack */ false); |
| if (!DefaultValue.isNull()) |
| Decl->setDefaultArgument(AST.getTrivialTypeSourceInfo(DefaultValue)); |
| |
| Params.emplace_back(Decl); |
| return *this; |
| } |
| |
| BuiltinTypeDeclBuilder &finalizeTemplateArgs() { |
| if (Params.empty()) |
| return Builder; |
| auto *ParamList = |
| TemplateParameterList::Create(AST, SourceLocation(), SourceLocation(), |
| Params, SourceLocation(), nullptr); |
| Builder.Template = ClassTemplateDecl::Create( |
| AST, Builder.Record->getDeclContext(), SourceLocation(), |
| DeclarationName(Builder.Record->getIdentifier()), ParamList, |
| Builder.Record); |
| Builder.Record->setDescribedClassTemplate(Builder.Template); |
| Builder.Template->setImplicit(true); |
| Builder.Template->setLexicalDeclContext(Builder.Record->getDeclContext()); |
| Builder.Record->getDeclContext()->addDecl(Builder.Template); |
| Params.clear(); |
| |
| QualType T = Builder.Template->getInjectedClassNameSpecialization(); |
| T = AST.getInjectedClassNameType(Builder.Record, T); |
| |
| return Builder; |
| } |
| }; |
| |
| TemplateParameterListBuilder BuiltinTypeDeclBuilder::addTemplateArgumentList() { |
| return TemplateParameterListBuilder(*this); |
| } |
| } // namespace |
| |
| HLSLExternalSemaSource::~HLSLExternalSemaSource() {} |
| |
| void HLSLExternalSemaSource::InitializeSema(Sema &S) { |
| SemaPtr = &S; |
| ASTContext &AST = SemaPtr->getASTContext(); |
| IdentifierInfo &HLSL = AST.Idents.get("hlsl", tok::TokenKind::identifier); |
| HLSLNamespace = |
| NamespaceDecl::Create(AST, AST.getTranslationUnitDecl(), false, |
| SourceLocation(), SourceLocation(), &HLSL, nullptr); |
| HLSLNamespace->setImplicit(true); |
| AST.getTranslationUnitDecl()->addDecl(HLSLNamespace); |
| defineTrivialHLSLTypes(); |
| forwardDeclareHLSLTypes(); |
| |
| // This adds a `using namespace hlsl` directive. In DXC, we don't put HLSL's |
| // built in types inside a namespace, but we are planning to change that in |
| // the near future. In order to be source compatible older versions of HLSL |
| // will need to implicitly use the hlsl namespace. For now in clang everything |
| // will get added to the namespace, and we can remove the using directive for |
| // future language versions to match HLSL's evolution. |
| auto *UsingDecl = UsingDirectiveDecl::Create( |
| AST, AST.getTranslationUnitDecl(), SourceLocation(), SourceLocation(), |
| NestedNameSpecifierLoc(), SourceLocation(), HLSLNamespace, |
| AST.getTranslationUnitDecl()); |
| |
| AST.getTranslationUnitDecl()->addDecl(UsingDecl); |
| } |
| |
| void HLSLExternalSemaSource::defineHLSLVectorAlias() { |
| ASTContext &AST = SemaPtr->getASTContext(); |
| |
| llvm::SmallVector<NamedDecl *> TemplateParams; |
| |
| auto *TypeParam = TemplateTypeParmDecl::Create( |
| AST, HLSLNamespace, SourceLocation(), SourceLocation(), 0, 0, |
| &AST.Idents.get("element", tok::TokenKind::identifier), false, false); |
| TypeParam->setDefaultArgument(AST.getTrivialTypeSourceInfo(AST.FloatTy)); |
| |
| TemplateParams.emplace_back(TypeParam); |
| |
| auto *SizeParam = NonTypeTemplateParmDecl::Create( |
| AST, HLSLNamespace, SourceLocation(), SourceLocation(), 0, 1, |
| &AST.Idents.get("element_count", tok::TokenKind::identifier), AST.IntTy, |
| false, AST.getTrivialTypeSourceInfo(AST.IntTy)); |
| Expr *LiteralExpr = |
| IntegerLiteral::Create(AST, llvm::APInt(AST.getIntWidth(AST.IntTy), 4), |
| AST.IntTy, SourceLocation()); |
| SizeParam->setDefaultArgument(LiteralExpr); |
| TemplateParams.emplace_back(SizeParam); |
| |
| auto *ParamList = |
| TemplateParameterList::Create(AST, SourceLocation(), SourceLocation(), |
| TemplateParams, SourceLocation(), nullptr); |
| |
| IdentifierInfo &II = AST.Idents.get("vector", tok::TokenKind::identifier); |
| |
| QualType AliasType = AST.getDependentSizedExtVectorType( |
| AST.getTemplateTypeParmType(0, 0, false, TypeParam), |
| DeclRefExpr::Create( |
| AST, NestedNameSpecifierLoc(), SourceLocation(), SizeParam, false, |
| DeclarationNameInfo(SizeParam->getDeclName(), SourceLocation()), |
| AST.IntTy, VK_LValue), |
| SourceLocation()); |
| |
| auto *Record = TypeAliasDecl::Create(AST, HLSLNamespace, SourceLocation(), |
| SourceLocation(), &II, |
| AST.getTrivialTypeSourceInfo(AliasType)); |
| Record->setImplicit(true); |
| |
| auto *Template = |
| TypeAliasTemplateDecl::Create(AST, HLSLNamespace, SourceLocation(), |
| Record->getIdentifier(), ParamList, Record); |
| |
| Record->setDescribedAliasTemplate(Template); |
| Template->setImplicit(true); |
| Template->setLexicalDeclContext(Record->getDeclContext()); |
| HLSLNamespace->addDecl(Template); |
| } |
| |
| void HLSLExternalSemaSource::defineTrivialHLSLTypes() { |
| defineHLSLVectorAlias(); |
| |
| ResourceDecl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "Resource") |
| .startDefinition() |
| .addHandleMember(AccessSpecifier::AS_public) |
| .completeDefinition() |
| .Record; |
| } |
| |
| void HLSLExternalSemaSource::forwardDeclareHLSLTypes() { |
| CXXRecordDecl *Decl; |
| Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWBuffer") |
| .addTemplateArgumentList() |
| .addTypeParameter("element_type", SemaPtr->getASTContext().FloatTy) |
| .finalizeTemplateArgs() |
| .Record; |
| Completions.insert(std::make_pair( |
| Decl, std::bind(&HLSLExternalSemaSource::completeBufferType, this, |
| std::placeholders::_1))); |
| } |
| |
| void HLSLExternalSemaSource::CompleteType(TagDecl *Tag) { |
| if (!isa<CXXRecordDecl>(Tag)) |
| return; |
| auto Record = cast<CXXRecordDecl>(Tag); |
| |
| // If this is a specialization, we need to get the underlying templated |
| // declaration and complete that. |
| if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(Record)) |
| Record = TDecl->getSpecializedTemplate()->getTemplatedDecl(); |
| auto It = Completions.find(Record); |
| if (It == Completions.end()) |
| return; |
| It->second(Record); |
| } |
| |
| void HLSLExternalSemaSource::completeBufferType(CXXRecordDecl *Record) { |
| BuiltinTypeDeclBuilder(Record) |
| .addHandleMember() |
| .addDefaultHandleConstructor(*SemaPtr, ResourceClass::UAV) |
| .annotateResourceClass(HLSLResourceAttr::UAV) |
| .completeDefinition(); |
| } |