@@ -289,8 +289,9 @@ struct BuiltinTypeDeclBuilder {
289289 }
290290
291291 TemplateParameterListBuilder addTemplateArgumentList (Sema &S);
292- BuiltinTypeDeclBuilder &addSimpleTemplateParams (Sema &S,
293- ArrayRef<StringRef> Names);
292+ BuiltinTypeDeclBuilder &
293+ addSimpleTemplateParams (Sema &S, ArrayRef<StringRef> Names, ConceptDecl *CD);
294+ BuiltinTypeDeclBuilder &addConceptSpecializationExpr (Sema &S);
294295};
295296
296297struct TemplateParameterListBuilder {
@@ -312,30 +313,129 @@ struct TemplateParameterListBuilder {
312313 S.Context , Builder.Record ->getDeclContext (), SourceLocation (),
313314 SourceLocation (), /* TemplateDepth */ 0 , Position,
314315 &S.Context .Idents .get (Name, tok::TokenKind::identifier),
315- /* Typename */ false ,
316- /* ParameterPack */ false );
316+ /* Typename */ true ,
317+ /* ParameterPack */ false ,
318+ /* HasTypeConstraint*/ false );
317319 if (!DefaultValue.isNull ())
318320 Decl->setDefaultArgument (
319321 S.Context , S.getTrivialTemplateArgumentLoc (DefaultValue, QualType (),
320322 SourceLocation ()));
321-
322323 Params.emplace_back (Decl);
323324 return *this ;
324325 }
325326
326- BuiltinTypeDeclBuilder &finalizeTemplateArgs () {
327+ /*
328+ The concept specialization expression (CSE) constructed in
329+ constructConceptSpecializationExpr is constructed so that it
330+ matches the CSE that is constructed when parsing the below C++ code:
331+
332+ template<typename T>
333+ concept is_typed_resource_element_compatible = sizeof(T) <= 16;
334+
335+ template<typename element_type> requires
336+ is_typed_resource_element_compatible<element_type>
337+ struct RWBuffer {
338+ element_type Val;
339+ };
340+
341+ int fn() {
342+ RWBuffer<int> Buf;
343+ }
344+
345+ When dumping the AST and filtering for "RWBuffer", the resulting AST
346+ structure is what we're trying to construct below, specifically the
347+ CSE portion.
348+ */
349+ ConceptSpecializationExpr *
350+ constructConceptSpecializationExpr (Sema &S, ConceptDecl *CD) {
351+ ASTContext &Context = S.getASTContext ();
352+ SourceLocation Loc = Builder.Record ->getBeginLoc ();
353+ DeclarationNameInfo DNI (CD->getDeclName (), Loc);
354+ NestedNameSpecifierLoc NNSLoc;
355+ DeclContext *DC = Builder.Record ->getDeclContext ();
356+ TemplateArgumentListInfo TALI (Loc, Loc);
357+
358+ // Assume that the concept decl has just one template parameter
359+ // This parameter should have been added when CD was constructed
360+ // in getTypedBufferConceptDecl
361+ assert (CD->getTemplateParameters ()->size () == 1 &&
362+ " unexpected concept decl parameter count" );
363+ TemplateTypeParmDecl *ConceptTTPD = dyn_cast<TemplateTypeParmDecl>(
364+ CD->getTemplateParameters ()->getParam (0 ));
365+
366+ // this TemplateTypeParmDecl is the template for the resource, and is
367+ // used to construct a template argumentthat will be used
368+ // to construct the ImplicitConceptSpecializationDecl
369+ TemplateTypeParmDecl *T = TemplateTypeParmDecl::Create (
370+ Context, // AST context
371+ Builder.Record ->getDeclContext (), // DeclContext
372+ SourceLocation (), SourceLocation (),
373+ /* depth=*/ 0 , // Depth in the template parameter list
374+ /* position=*/ 0 , // Position in the template parameter list
375+ /* id=*/ nullptr , // Identifier for 'T'
376+ /* Typename=*/ true , // Indicates this is a 'typename' or 'class'
377+ /* ParameterPack=*/ false , // Not a parameter pack
378+ /* HasTypeConstraint=*/ false // Has no type constraint
379+ );
380+
381+ T->setDeclContext (DC);
382+
383+ QualType ConceptTType = Context.getTypeDeclType (ConceptTTPD);
384+
385+ // this is the 2nd template argument node, on which
386+ // the concept constraint is actually being applied: 'element_type'
387+ TemplateArgument ConceptTA = TemplateArgument (ConceptTType);
388+
389+ QualType CSETType = Context.getTypeDeclType (T);
390+
391+ // this is the 1st template argument node, which represents
392+ // the abstract type that a concept would refer to: 'T'
393+ TemplateArgument CSETA = TemplateArgument (CSETType);
394+
395+ ImplicitConceptSpecializationDecl *ImplicitCSEDecl =
396+ ImplicitConceptSpecializationDecl::Create (
397+ Context, Builder.Record ->getDeclContext (), Loc, {CSETA});
398+
399+ // Constraint satisfaction is used to construct the
400+ // ConceptSpecailizationExpr, and represents the 2nd Template Argument,
401+ // located at the bottom of the sample AST above.
402+ const ConstraintSatisfaction CS (CD, {ConceptTA});
403+ TemplateArgumentLoc TAL = S.getTrivialTemplateArgumentLoc (
404+ ConceptTA, QualType (), SourceLocation ());
405+
406+ TALI.addArgument (TAL);
407+ const ASTTemplateArgumentListInfo *ATALI =
408+ ASTTemplateArgumentListInfo::Create (Context, TALI);
409+
410+ // In the concept reference, ATALI is what adds the extra
411+ // TemplateArgument node underneath CSE
412+ ConceptReference *CR =
413+ ConceptReference::Create (Context, NNSLoc, Loc, DNI, CD, CD, ATALI);
414+
415+ ConceptSpecializationExpr *CSE =
416+ ConceptSpecializationExpr::Create (Context, CR, ImplicitCSEDecl, &CS);
417+
418+ return CSE;
419+ }
420+
421+ BuiltinTypeDeclBuilder &finalizeTemplateArgs (ConceptDecl *CD = nullptr ) {
327422 if (Params.empty ())
328423 return Builder;
424+ ConceptSpecializationExpr *CSE =
425+ CD ? constructConceptSpecializationExpr (S, CD) : nullptr ;
426+
329427 auto *ParamList = TemplateParameterList::Create (S.Context , SourceLocation (),
330428 SourceLocation (), Params,
331- SourceLocation (), nullptr );
429+ SourceLocation (), CSE );
332430 Builder.Template = ClassTemplateDecl::Create (
333431 S.Context , Builder.Record ->getDeclContext (), SourceLocation (),
334432 DeclarationName (Builder.Record ->getIdentifier ()), ParamList,
335433 Builder.Record );
434+
336435 Builder.Record ->setDescribedClassTemplate (Builder.Template );
337436 Builder.Template ->setImplicit (true );
338437 Builder.Template ->setLexicalDeclContext (Builder.Record ->getDeclContext ());
438+
339439 // NOTE: setPreviousDecl before addDecl so new decl replace old decl when
340440 // make visible.
341441 Builder.Template ->setPreviousDecl (Builder.PrevTemplate );
@@ -355,13 +455,13 @@ BuiltinTypeDeclBuilder::addTemplateArgumentList(Sema &S) {
355455 return TemplateParameterListBuilder (S, *this );
356456}
357457
358- BuiltinTypeDeclBuilder &
359- BuiltinTypeDeclBuilder::addSimpleTemplateParams (Sema &S,
360- ArrayRef<StringRef> Names) {
458+ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addSimpleTemplateParams (
459+ Sema &S, ArrayRef<StringRef> Names, ConceptDecl *CD = nullptr ) {
361460 TemplateParameterListBuilder Builder = this ->addTemplateArgumentList (S);
362461 for (StringRef Name : Names)
363462 Builder.addTypeParameter (Name);
364- return Builder.finalizeTemplateArgs ();
463+
464+ return Builder.finalizeTemplateArgs (CD);
365465}
366466
367467HLSLExternalSemaSource::~HLSLExternalSemaSource () {}
@@ -472,10 +572,103 @@ static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S,
472572 .addDefaultHandleConstructor (S);
473573}
474574
575+ BinaryOperator *constructSizeOfLEQ16Expr (ASTContext &Context,
576+ SourceLocation NameLoc,
577+ TemplateTypeParmDecl *T) {
578+ // Obtain the QualType for 'unsigned long'
579+ QualType UnsignedLongType = Context.UnsignedLongTy ;
580+
581+ // Create a QualType that points to this TemplateTypeParmDecl
582+ QualType TType = Context.getTypeDeclType (T);
583+
584+ // Create a TypeSourceInfo for the template type parameter 'T'
585+ TypeSourceInfo *TTypeSourceInfo =
586+ Context.getTrivialTypeSourceInfo (TType, NameLoc);
587+
588+ UnaryExprOrTypeTraitExpr *sizeOfExpr = new (Context) UnaryExprOrTypeTraitExpr (
589+ UETT_SizeOf, TTypeSourceInfo, UnsignedLongType, NameLoc, NameLoc);
590+
591+ // Create an IntegerLiteral for the value '16' with size type
592+ QualType SizeType = Context.getSizeType ();
593+ llvm::APInt SizeValue = llvm::APInt (Context.getTypeSize (SizeType), 16 );
594+ IntegerLiteral *SizeLiteral =
595+ new (Context) IntegerLiteral (Context, SizeValue, SizeType, NameLoc);
596+
597+ QualType BoolTy = Context.BoolTy ;
598+
599+ BinaryOperator *binaryOperator =
600+ BinaryOperator::Create (Context, sizeOfExpr, // Left-hand side expression
601+ SizeLiteral, // Right-hand side expression
602+ BO_LE, // Binary operator kind (<=)
603+ BoolTy, // Result type (bool)
604+ VK_LValue, // Value kind
605+ OK_Ordinary, // Object kind
606+ NameLoc, // Source location of operator
607+ FPOptionsOverride ());
608+
609+ return binaryOperator;
610+ }
611+
612+ Expr *constructTypedBufferConstraintExpr (Sema &S, SourceLocation NameLoc,
613+ TemplateTypeParmDecl *T) {
614+ ASTContext &Context = S.getASTContext ();
615+
616+ // first get the "sizeof(T) <= 16" expression, as a binary operator
617+ BinaryOperator *SizeOfLEQ16 = constructSizeOfLEQ16Expr (Context, NameLoc, T);
618+ // TODO: add the 'builtin_hlsl_is_typed_resource_element_compatible' builtin
619+ // and return a binary operator that evaluates the builtin on the given
620+ // template type parameter 'T'.
621+ // Defined in issue https://github.com/llvm/llvm-project/issues/113223
622+ return SizeOfLEQ16;
623+ }
624+
625+ ConceptDecl *constructTypedBufferConceptDecl (Sema &S, NamespaceDecl *NSD) {
626+ ASTContext &Context = S.getASTContext ();
627+ DeclContext *DC = NSD->getDeclContext ();
628+ SourceLocation DeclLoc = SourceLocation ();
629+
630+ IdentifierInfo &ElementTypeII = Context.Idents .get (" element_type" );
631+ TemplateTypeParmDecl *T = TemplateTypeParmDecl::Create (
632+ Context, NSD->getDeclContext (), DeclLoc, DeclLoc,
633+ /* depth=*/ 0 ,
634+ /* position=*/ 0 ,
635+ /* id=*/ &ElementTypeII,
636+ /* Typename=*/ true ,
637+ /* ParameterPack=*/ false );
638+
639+ T->setDeclContext (DC);
640+ T->setReferenced ();
641+
642+ // Create and Attach Template Parameter List to ConceptDecl
643+ TemplateParameterList *ConceptParams = TemplateParameterList::Create (
644+ Context, DeclLoc, DeclLoc, {T}, DeclLoc, nullptr );
645+
646+ DeclarationName DeclName = DeclarationName (
647+ &Context.Idents .get (" __is_typed_resource_element_compatible" ));
648+ Expr *ConstraintExpr = constructTypedBufferConstraintExpr (S, DeclLoc, T);
649+
650+ // Create a ConceptDecl
651+ ConceptDecl *CD =
652+ ConceptDecl::Create (Context, NSD->getDeclContext (), DeclLoc, DeclName,
653+ ConceptParams, ConstraintExpr);
654+
655+ // Attach the template parameter list to the ConceptDecl
656+ CD->setTemplateParameters (ConceptParams);
657+
658+ // Add the concept declaration to the Translation Unit Decl
659+ NSD->getDeclContext ()->addDecl (CD);
660+
661+ return CD;
662+ }
663+
475664void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations () {
476665 CXXRecordDecl *Decl;
666+ ConceptDecl *TypedBufferConcept =
667+ constructTypedBufferConceptDecl (*SemaPtr, HLSLNamespace);
668+
477669 Decl = BuiltinTypeDeclBuilder (*SemaPtr, HLSLNamespace, " RWBuffer" )
478- .addSimpleTemplateParams (*SemaPtr, {" element_type" })
670+ .addSimpleTemplateParams (*SemaPtr, {" element_type" },
671+ TypedBufferConcept)
479672 .Record ;
480673
481674 onCompletion (Decl, [this ](CXXRecordDecl *Decl) {
0 commit comments