diff options
author | Joshua Batista <jbatista@microsoft.com> | 2024-11-14 17:00:58 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-11-14 17:00:58 -0800 |
commit | 478c24b5f86911d14256bad71c85ed0ff061070a (patch) | |
tree | f1ea395aea161a0a2125beab2a463f0b6041177b /clang/lib/Sema/HLSLExternalSemaSource.cpp | |
parent | 2f55de4e317ee93cdca839558acf8be2b5ac2b46 (diff) | |
download | llvm-478c24b5f86911d14256bad71c85ed0ff061070a.zip llvm-478c24b5f86911d14256bad71c85ed0ff061070a.tar.gz llvm-478c24b5f86911d14256bad71c85ed0ff061070a.tar.bz2 |
[HLSL] Add implicit resource element type concepts to AST (#112600)
This PR is step one on the journey to implement resource element type
validation via C++20 concepts. The PR sets up the infrastructure for
injecting implicit concept decls / concept specialization expressions
into the AST, which will then be evaluated after template arguments are
instantiated. This is not meant to be a complete implementation of the
desired validation for HLSL,
there are a couple of missing elements:
1. We need the __builtin_hlsl_is_typed_resource_element_compatible
builtin to be implemented.
2. We need other constraints, like is_intangible
3. We need to put the first 2 points together, and construct a finalized
constraint expression, which should differ between typed and raw buffers
This is just an initial PR that puts some of the core infrastructure in
place.
Diffstat (limited to 'clang/lib/Sema/HLSLExternalSemaSource.cpp')
-rw-r--r-- | clang/lib/Sema/HLSLExternalSemaSource.cpp | 217 |
1 files changed, 205 insertions, 12 deletions
diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index cac15b9..d90a7d3 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -289,8 +289,9 @@ struct BuiltinTypeDeclBuilder { } TemplateParameterListBuilder addTemplateArgumentList(Sema &S); - BuiltinTypeDeclBuilder &addSimpleTemplateParams(Sema &S, - ArrayRef<StringRef> Names); + BuiltinTypeDeclBuilder & + addSimpleTemplateParams(Sema &S, ArrayRef<StringRef> Names, ConceptDecl *CD); + BuiltinTypeDeclBuilder &addConceptSpecializationExpr(Sema &S); }; struct TemplateParameterListBuilder { @@ -312,30 +313,129 @@ struct TemplateParameterListBuilder { S.Context, Builder.Record->getDeclContext(), SourceLocation(), SourceLocation(), /* TemplateDepth */ 0, Position, &S.Context.Idents.get(Name, tok::TokenKind::identifier), - /* Typename */ false, - /* ParameterPack */ false); + /* Typename */ true, + /* ParameterPack */ false, + /* HasTypeConstraint*/ false); if (!DefaultValue.isNull()) Decl->setDefaultArgument( S.Context, S.getTrivialTemplateArgumentLoc(DefaultValue, QualType(), SourceLocation())); - Params.emplace_back(Decl); return *this; } - BuiltinTypeDeclBuilder &finalizeTemplateArgs() { + /* + The concept specialization expression (CSE) constructed in + constructConceptSpecializationExpr is constructed so that it + matches the CSE that is constructed when parsing the below C++ code: + + template<typename T> + concept is_typed_resource_element_compatible = sizeof(T) <= 16; + + template<typename element_type> requires + is_typed_resource_element_compatible<element_type> + struct RWBuffer { + element_type Val; + }; + + int fn() { + RWBuffer<int> Buf; + } + + When dumping the AST and filtering for "RWBuffer", the resulting AST + structure is what we're trying to construct below, specifically the + CSE portion. + */ + ConceptSpecializationExpr * + constructConceptSpecializationExpr(Sema &S, ConceptDecl *CD) { + ASTContext &Context = S.getASTContext(); + SourceLocation Loc = Builder.Record->getBeginLoc(); + DeclarationNameInfo DNI(CD->getDeclName(), Loc); + NestedNameSpecifierLoc NNSLoc; + DeclContext *DC = Builder.Record->getDeclContext(); + TemplateArgumentListInfo TALI(Loc, Loc); + + // Assume that the concept decl has just one template parameter + // This parameter should have been added when CD was constructed + // in getTypedBufferConceptDecl + assert(CD->getTemplateParameters()->size() == 1 && + "unexpected concept decl parameter count"); + TemplateTypeParmDecl *ConceptTTPD = dyn_cast<TemplateTypeParmDecl>( + CD->getTemplateParameters()->getParam(0)); + + // this TemplateTypeParmDecl is the template for the resource, and is + // used to construct a template argumentthat will be used + // to construct the ImplicitConceptSpecializationDecl + TemplateTypeParmDecl *T = TemplateTypeParmDecl::Create( + Context, // AST context + Builder.Record->getDeclContext(), // DeclContext + SourceLocation(), SourceLocation(), + /*depth=*/0, // Depth in the template parameter list + /*position=*/0, // Position in the template parameter list + /*id=*/nullptr, // Identifier for 'T' + /*Typename=*/true, // Indicates this is a 'typename' or 'class' + /*ParameterPack=*/false, // Not a parameter pack + /*HasTypeConstraint=*/false // Has no type constraint + ); + + T->setDeclContext(DC); + + QualType ConceptTType = Context.getTypeDeclType(ConceptTTPD); + + // this is the 2nd template argument node, on which + // the concept constraint is actually being applied: 'element_type' + TemplateArgument ConceptTA = TemplateArgument(ConceptTType); + + QualType CSETType = Context.getTypeDeclType(T); + + // this is the 1st template argument node, which represents + // the abstract type that a concept would refer to: 'T' + TemplateArgument CSETA = TemplateArgument(CSETType); + + ImplicitConceptSpecializationDecl *ImplicitCSEDecl = + ImplicitConceptSpecializationDecl::Create( + Context, Builder.Record->getDeclContext(), Loc, {CSETA}); + + // Constraint satisfaction is used to construct the + // ConceptSpecailizationExpr, and represents the 2nd Template Argument, + // located at the bottom of the sample AST above. + const ConstraintSatisfaction CS(CD, {ConceptTA}); + TemplateArgumentLoc TAL = S.getTrivialTemplateArgumentLoc( + ConceptTA, QualType(), SourceLocation()); + + TALI.addArgument(TAL); + const ASTTemplateArgumentListInfo *ATALI = + ASTTemplateArgumentListInfo::Create(Context, TALI); + + // In the concept reference, ATALI is what adds the extra + // TemplateArgument node underneath CSE + ConceptReference *CR = + ConceptReference::Create(Context, NNSLoc, Loc, DNI, CD, CD, ATALI); + + ConceptSpecializationExpr *CSE = + ConceptSpecializationExpr::Create(Context, CR, ImplicitCSEDecl, &CS); + + return CSE; + } + + BuiltinTypeDeclBuilder &finalizeTemplateArgs(ConceptDecl *CD = nullptr) { if (Params.empty()) return Builder; + ConceptSpecializationExpr *CSE = + CD ? constructConceptSpecializationExpr(S, CD) : nullptr; + auto *ParamList = TemplateParameterList::Create(S.Context, SourceLocation(), SourceLocation(), Params, - SourceLocation(), nullptr); + SourceLocation(), CSE); Builder.Template = ClassTemplateDecl::Create( S.Context, 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()); + // NOTE: setPreviousDecl before addDecl so new decl replace old decl when // make visible. Builder.Template->setPreviousDecl(Builder.PrevTemplate); @@ -355,13 +455,13 @@ BuiltinTypeDeclBuilder::addTemplateArgumentList(Sema &S) { return TemplateParameterListBuilder(S, *this); } -BuiltinTypeDeclBuilder & -BuiltinTypeDeclBuilder::addSimpleTemplateParams(Sema &S, - ArrayRef<StringRef> Names) { +BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addSimpleTemplateParams( + Sema &S, ArrayRef<StringRef> Names, ConceptDecl *CD = nullptr) { TemplateParameterListBuilder Builder = this->addTemplateArgumentList(S); for (StringRef Name : Names) Builder.addTypeParameter(Name); - return Builder.finalizeTemplateArgs(); + + return Builder.finalizeTemplateArgs(CD); } HLSLExternalSemaSource::~HLSLExternalSemaSource() {} @@ -472,10 +572,103 @@ static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S, .addDefaultHandleConstructor(S); } +BinaryOperator *constructSizeOfLEQ16Expr(ASTContext &Context, + SourceLocation NameLoc, + TemplateTypeParmDecl *T) { + // Obtain the QualType for 'unsigned long' + QualType UnsignedLongType = Context.UnsignedLongTy; + + // Create a QualType that points to this TemplateTypeParmDecl + QualType TType = Context.getTypeDeclType(T); + + // Create a TypeSourceInfo for the template type parameter 'T' + TypeSourceInfo *TTypeSourceInfo = + Context.getTrivialTypeSourceInfo(TType, NameLoc); + + UnaryExprOrTypeTraitExpr *sizeOfExpr = new (Context) UnaryExprOrTypeTraitExpr( + UETT_SizeOf, TTypeSourceInfo, UnsignedLongType, NameLoc, NameLoc); + + // Create an IntegerLiteral for the value '16' with size type + QualType SizeType = Context.getSizeType(); + llvm::APInt SizeValue = llvm::APInt(Context.getTypeSize(SizeType), 16); + IntegerLiteral *SizeLiteral = + new (Context) IntegerLiteral(Context, SizeValue, SizeType, NameLoc); + + QualType BoolTy = Context.BoolTy; + + BinaryOperator *binaryOperator = + BinaryOperator::Create(Context, sizeOfExpr, // Left-hand side expression + SizeLiteral, // Right-hand side expression + BO_LE, // Binary operator kind (<=) + BoolTy, // Result type (bool) + VK_LValue, // Value kind + OK_Ordinary, // Object kind + NameLoc, // Source location of operator + FPOptionsOverride()); + + return binaryOperator; +} + +Expr *constructTypedBufferConstraintExpr(Sema &S, SourceLocation NameLoc, + TemplateTypeParmDecl *T) { + ASTContext &Context = S.getASTContext(); + + // first get the "sizeof(T) <= 16" expression, as a binary operator + BinaryOperator *SizeOfLEQ16 = constructSizeOfLEQ16Expr(Context, NameLoc, T); + // TODO: add the 'builtin_hlsl_is_typed_resource_element_compatible' builtin + // and return a binary operator that evaluates the builtin on the given + // template type parameter 'T'. + // Defined in issue https://github.com/llvm/llvm-project/issues/113223 + return SizeOfLEQ16; +} + +ConceptDecl *constructTypedBufferConceptDecl(Sema &S, NamespaceDecl *NSD) { + ASTContext &Context = S.getASTContext(); + DeclContext *DC = NSD->getDeclContext(); + SourceLocation DeclLoc = SourceLocation(); + + IdentifierInfo &ElementTypeII = Context.Idents.get("element_type"); + TemplateTypeParmDecl *T = TemplateTypeParmDecl::Create( + Context, NSD->getDeclContext(), DeclLoc, DeclLoc, + /*depth=*/0, + /*position=*/0, + /*id=*/&ElementTypeII, + /*Typename=*/true, + /*ParameterPack=*/false); + + T->setDeclContext(DC); + T->setReferenced(); + + // Create and Attach Template Parameter List to ConceptDecl + TemplateParameterList *ConceptParams = TemplateParameterList::Create( + Context, DeclLoc, DeclLoc, {T}, DeclLoc, nullptr); + + DeclarationName DeclName = DeclarationName( + &Context.Idents.get("__is_typed_resource_element_compatible")); + Expr *ConstraintExpr = constructTypedBufferConstraintExpr(S, DeclLoc, T); + + // Create a ConceptDecl + ConceptDecl *CD = + ConceptDecl::Create(Context, NSD->getDeclContext(), DeclLoc, DeclName, + ConceptParams, ConstraintExpr); + + // Attach the template parameter list to the ConceptDecl + CD->setTemplateParameters(ConceptParams); + + // Add the concept declaration to the Translation Unit Decl + NSD->getDeclContext()->addDecl(CD); + + return CD; +} + void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { CXXRecordDecl *Decl; + ConceptDecl *TypedBufferConcept = + constructTypedBufferConceptDecl(*SemaPtr, HLSLNamespace); + Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWBuffer") - .addSimpleTemplateParams(*SemaPtr, {"element_type"}) + .addSimpleTemplateParams(*SemaPtr, {"element_type"}, + TypedBufferConcept) .Record; onCompletion(Decl, [this](CXXRecordDecl *Decl) { |