@@ -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,8 +313,9 @@ 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 (),
@@ -323,19 +325,114 @@ struct TemplateParameterListBuilder {
323325 return *this ;
324326 }
325327
326- BuiltinTypeDeclBuilder &finalizeTemplateArgs () {
328+ /*
329+ The concept specialization expression (CSE) constructed in
330+ constructConceptSpecializationExpr is constructed so that it
331+ matches the CSE that is constructed when parsing the below C++ code:
332+ template<typename T>
333+ concept is_typed_resource_element_compatible = sizeof(T) <= 16;
334+ template<typename element_type> requires
335+ is_typed_resource_element_compatible<element_type>
336+ struct RWBuffer {
337+ element_type Val;
338+ };
339+ int fn() {
340+ RWBuffer<int> Buf;
341+ }
342+ When dumping the AST and filtering for "RWBuffer", the resulting AST
343+ structure is what we're trying to construct below, specifically the
344+ CSE portion.
345+ */
346+ ConceptSpecializationExpr *
347+ constructConceptSpecializationExpr (Sema &S, ConceptDecl *CD) {
348+ ASTContext &Context = S.getASTContext ();
349+ SourceLocation Loc = Builder.Record ->getBeginLoc ();
350+ DeclarationNameInfo DNI (CD->getDeclName (), Loc);
351+ NestedNameSpecifierLoc NNSLoc;
352+ DeclContext *DC = Builder.Record ->getDeclContext ();
353+ TemplateArgumentListInfo TALI (Loc, Loc);
354+
355+ // Assume that the concept decl has just one template parameter
356+ // This parameter should have been added when CD was constructed
357+ // in getTypedBufferConceptDecl
358+ assert (CD->getTemplateParameters ()->size () == 1 &&
359+ " unexpected concept decl parameter count" );
360+ TemplateTypeParmDecl *ConceptTTPD = dyn_cast<TemplateTypeParmDecl>(
361+ CD->getTemplateParameters ()->getParam (0 ));
362+
363+ // this TemplateTypeParmDecl is the template for the resource, and is
364+ // used to construct a template argumentthat will be used
365+ // to construct the ImplicitConceptSpecializationDecl
366+ TemplateTypeParmDecl *T = TemplateTypeParmDecl::Create (
367+ Context, // AST context
368+ Builder.Record ->getDeclContext (), // DeclContext
369+ SourceLocation (), SourceLocation (),
370+ /* depth=*/ 0 , // Depth in the template parameter list
371+ /* position=*/ 0 , // Position in the template parameter list
372+ /* id=*/ nullptr , // Identifier for 'T'
373+ /* Typename=*/ true , // Indicates this is a 'typename' or 'class'
374+ /* ParameterPack=*/ false , // Not a parameter pack
375+ /* HasTypeConstraint=*/ false // Has no type constraint
376+ );
377+
378+ T->setDeclContext (DC);
379+
380+ QualType ConceptTType = Context.getTypeDeclType (ConceptTTPD);
381+
382+ // this is the 2nd template argument node, on which
383+ // the concept constraint is actually being applied: 'element_type'
384+ TemplateArgument ConceptTA = TemplateArgument (ConceptTType);
385+
386+ QualType CSETType = Context.getTypeDeclType (T);
387+
388+ // this is the 1st template argument node, which represents
389+ // the abstract type that a concept would refer to: 'T'
390+ TemplateArgument CSETA = TemplateArgument (CSETType);
391+
392+ ImplicitConceptSpecializationDecl *ImplicitCSEDecl =
393+ ImplicitConceptSpecializationDecl::Create (
394+ Context, Builder.Record ->getDeclContext (), Loc, {CSETA});
395+
396+ // Constraint satisfaction is used to construct the
397+ // ConceptSpecailizationExpr, and represents the 2nd Template Argument,
398+ // located at the bottom of the sample AST above.
399+ const ConstraintSatisfaction CS (CD, {ConceptTA});
400+ TemplateArgumentLoc TAL = S.getTrivialTemplateArgumentLoc (
401+ ConceptTA, QualType (), SourceLocation ());
402+
403+ TALI.addArgument (TAL);
404+ const ASTTemplateArgumentListInfo *ATALI =
405+ ASTTemplateArgumentListInfo::Create (Context, TALI);
406+
407+ // In the concept reference, ATALI is what adds the extra
408+ // TemplateArgument node underneath CSE
409+ ConceptReference *CR =
410+ ConceptReference::Create (Context, NNSLoc, Loc, DNI, CD, CD, ATALI);
411+
412+ ConceptSpecializationExpr *CSE =
413+ ConceptSpecializationExpr::Create (Context, CR, ImplicitCSEDecl, &CS);
414+
415+ return CSE;
416+ }
417+
418+ BuiltinTypeDeclBuilder &finalizeTemplateArgs (ConceptDecl *CD = nullptr ) {
327419 if (Params.empty ())
328420 return Builder;
421+ ConceptSpecializationExpr *CSE =
422+ CD ? constructConceptSpecializationExpr (S, CD) : nullptr ;
423+
329424 auto *ParamList = TemplateParameterList::Create (S.Context , SourceLocation (),
330425 SourceLocation (), Params,
331- SourceLocation (), nullptr );
426+ SourceLocation (), CSE );
332427 Builder.Template = ClassTemplateDecl::Create (
333428 S.Context , Builder.Record ->getDeclContext (), SourceLocation (),
334429 DeclarationName (Builder.Record ->getIdentifier ()), ParamList,
335430 Builder.Record );
431+
336432 Builder.Record ->setDescribedClassTemplate (Builder.Template );
337433 Builder.Template ->setImplicit (true );
338434 Builder.Template ->setLexicalDeclContext (Builder.Record ->getDeclContext ());
435+
339436 // NOTE: setPreviousDecl before addDecl so new decl replace old decl when
340437 // make visible.
341438 Builder.Template ->setPreviousDecl (Builder.PrevTemplate );
@@ -355,13 +452,12 @@ BuiltinTypeDeclBuilder::addTemplateArgumentList(Sema &S) {
355452 return TemplateParameterListBuilder (S, *this );
356453}
357454
358- BuiltinTypeDeclBuilder &
359- BuiltinTypeDeclBuilder::addSimpleTemplateParams (Sema &S,
360- ArrayRef<StringRef> Names) {
455+ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addSimpleTemplateParams (
456+ Sema &S, ArrayRef<StringRef> Names, ConceptDecl *CD = nullptr ) {
361457 TemplateParameterListBuilder Builder = this ->addTemplateArgumentList (S);
362458 for (StringRef Name : Names)
363459 Builder.addTypeParameter (Name);
364- return Builder.finalizeTemplateArgs ();
460+ return Builder.finalizeTemplateArgs (CD );
365461}
366462
367463HLSLExternalSemaSource::~HLSLExternalSemaSource () {}
@@ -472,10 +568,102 @@ static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S,
472568 .addDefaultHandleConstructor (S);
473569}
474570
571+ BinaryOperator *constructSizeOfLEQ16Expr (ASTContext &Context,
572+ SourceLocation NameLoc,
573+ TemplateTypeParmDecl *T) {
574+ // Obtain the QualType for 'unsigned long'
575+ QualType UnsignedLongType = Context.UnsignedLongTy ;
576+
577+ // Create a QualType that points to this TemplateTypeParmDecl
578+ QualType TType = Context.getTypeDeclType (T);
579+
580+ // Create a TypeSourceInfo for the template type parameter 'T'
581+ TypeSourceInfo *TTypeSourceInfo =
582+ Context.getTrivialTypeSourceInfo (TType, NameLoc);
583+
584+ UnaryExprOrTypeTraitExpr *sizeOfExpr = new (Context) UnaryExprOrTypeTraitExpr (
585+ UETT_SizeOf, TTypeSourceInfo, UnsignedLongType, NameLoc, NameLoc);
586+
587+ // Create an IntegerLiteral for the value '16' with size type
588+ QualType SizeType = Context.getSizeType ();
589+ llvm::APInt SizeValue = llvm::APInt (Context.getTypeSize (SizeType), 16 );
590+ IntegerLiteral *SizeLiteral =
591+ new (Context) IntegerLiteral (Context, SizeValue, SizeType, NameLoc);
592+
593+ QualType BoolTy = Context.BoolTy ;
594+
595+ BinaryOperator *binaryOperator =
596+ BinaryOperator::Create (Context, sizeOfExpr, // Left-hand side expression
597+ SizeLiteral, // Right-hand side expression
598+ BO_LE, // Binary operator kind (<=)
599+ BoolTy, // Result type (bool)
600+ VK_LValue, // Value kind
601+ OK_Ordinary, // Object kind
602+ NameLoc, // Source location of operator
603+ FPOptionsOverride ());
604+
605+ return binaryOperator;
606+ }
607+
608+ Expr *constructTypedBufferConstraintExpr (Sema &S, SourceLocation NameLoc,
609+ TemplateTypeParmDecl *T) {
610+ ASTContext &Context = S.getASTContext ();
611+
612+ // first get the "sizeof(T) <= 16" expression, as a binary operator
613+ BinaryOperator *SizeOfLEQ16 = constructSizeOfLEQ16Expr (Context, NameLoc, T);
614+ // TODO: add the 'builtin_hlsl_is_typed_resource_element_compatible' builtin
615+ // and return a binary operator that evaluates the builtin on the given
616+ // template type parameter 'T'.
617+ // Defined in issue https://github.com/llvm/llvm-project/issues/113223
618+ return SizeOfLEQ16;
619+ }
620+
621+ ConceptDecl *constructTypedBufferConceptDecl (Sema &S, NamespaceDecl *NSD) {
622+ ASTContext &Context = S.getASTContext ();
623+ DeclContext *DC = NSD->getDeclContext ();
624+ SourceLocation DeclLoc = SourceLocation ();
625+
626+ IdentifierInfo &ElementTypeII = Context.Idents .get (" element_type" );
627+ TemplateTypeParmDecl *T = TemplateTypeParmDecl::Create (
628+ Context, NSD->getDeclContext (), DeclLoc, DeclLoc,
629+ /* depth=*/ 0 ,
630+ /* position=*/ 0 ,
631+ /* id=*/ &ElementTypeII,
632+ /* Typename=*/ true ,
633+ /* ParameterPack=*/ false );
634+
635+ T->setDeclContext (DC);
636+ T->setReferenced ();
637+
638+ // Create and Attach Template Parameter List to ConceptDecl
639+ TemplateParameterList *ConceptParams = TemplateParameterList::Create (
640+ Context, DeclLoc, DeclLoc, {T}, DeclLoc, nullptr );
641+
642+ DeclarationName DeclName = DeclarationName (
643+ &Context.Idents .get (" __is_typed_resource_element_compatible" ));
644+ Expr *ConstraintExpr = constructTypedBufferConstraintExpr (S, DeclLoc, T);
645+
646+ // Create a ConceptDecl
647+ ConceptDecl *CD =
648+ ConceptDecl::Create (Context, NSD->getDeclContext (), DeclLoc, DeclName,
649+ ConceptParams, ConstraintExpr);
650+
651+ // Attach the template parameter list to the ConceptDecl
652+ CD->setTemplateParameters (ConceptParams);
653+
654+ // Add the concept declaration to the Translation Unit Decl
655+ NSD->getDeclContext ()->addDecl (CD);
656+
657+ return CD;
658+ }
659+
475660void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations () {
476661 CXXRecordDecl *Decl;
662+ ConceptDecl *TypedBufferConcept =
663+ constructTypedBufferConceptDecl (*SemaPtr, HLSLNamespace);
477664 Decl = BuiltinTypeDeclBuilder (*SemaPtr, HLSLNamespace, " RWBuffer" )
478- .addSimpleTemplateParams (*SemaPtr, {" element_type" })
665+ .addSimpleTemplateParams (*SemaPtr, {" element_type" },
666+ TypedBufferConcept)
479667 .Record ;
480668
481669 onCompletion (Decl, [this ](CXXRecordDecl *Decl) {
0 commit comments