xref: /freebsd/contrib/llvm-project/clang/lib/Sema/HLSLExternalSemaSource.cpp (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
1 //===--- HLSLExternalSemaSource.cpp - HLSL Sema Source --------------------===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 //
10 //===----------------------------------------------------------------------===//
11 
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"
21 
22 #include <functional>
23 
24 using namespace clang;
25 using namespace llvm::hlsl;
26 
27 namespace {
28 
29 struct TemplateParameterListBuilder;
30 
31 struct BuiltinTypeDeclBuilder {
32   CXXRecordDecl *Record = nullptr;
33   ClassTemplateDecl *Template = nullptr;
34   ClassTemplateDecl *PrevTemplate = nullptr;
35   NamespaceDecl *HLSLNamespace = nullptr;
36   llvm::StringMap<FieldDecl *> Fields;
37 
38   BuiltinTypeDeclBuilder(CXXRecordDecl *R) : Record(R) {
39     Record->startDefinition();
40     Template = Record->getDescribedClassTemplate();
41   }
42 
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);
47 
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();
54         PrevTemplate = TD;
55       } else
56         PrevDecl = dyn_cast<CXXRecordDecl>(Found);
57       assert(PrevDecl && "Unexpected lookup result type.");
58     }
59 
60     if (PrevDecl && PrevDecl->isCompleteDefinition()) {
61       Record = PrevDecl;
62       return;
63     }
64 
65     Record = CXXRecordDecl::Create(AST, TagDecl::TagKind::Class, HLSLNamespace,
66                                    SourceLocation(), SourceLocation(), &II,
67                                    PrevDecl, true);
68     Record->setImplicit(true);
69     Record->setLexicalDeclContext(HLSLNamespace);
70     Record->setHasExternalLexicalStorage();
71 
72     // Don't let anyone derive from built-in types.
73     Record->addAttr(FinalAttr::CreateImplicit(AST, SourceRange(),
74                                               FinalAttr::Keyword_final));
75   }
76 
77   ~BuiltinTypeDeclBuilder() {
78     if (HLSLNamespace && !Template && Record->getDeclContext() == HLSLNamespace)
79       HLSLNamespace->addDecl(Record);
80   }
81 
82   BuiltinTypeDeclBuilder &
83   addMemberVariable(StringRef Name, QualType Type,
84                     AccessSpecifier Access = AccessSpecifier::AS_private) {
85     if (Record->isCompleteDefinition())
86       return *this;
87     assert(Record->isBeingDefined() &&
88            "Definition must be started before adding members!");
89     ASTContext &AST = Record->getASTContext();
90 
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;
101     return *this;
102   }
103 
104   BuiltinTypeDeclBuilder &
105   addHandleMember(AccessSpecifier Access = AccessSpecifier::AS_private) {
106     if (Record->isCompleteDefinition())
107       return *this;
108     QualType Ty = Record->getASTContext().VoidPtrTy;
109     if (Template) {
110       if (const auto *TTD = dyn_cast<TemplateTypeParmDecl>(
111               Template->getTemplateParameters()->getParam(0)))
112         Ty = Record->getASTContext().getPointerType(
113             QualType(TTD->getTypeForDecl(), 0));
114     }
115     return addMemberVariable("h", Ty, Access);
116   }
117 
118   BuiltinTypeDeclBuilder &annotateHLSLResource(ResourceClass RC,
119                                                ResourceKind RK, bool IsROV) {
120     if (Record->isCompleteDefinition())
121       return *this;
122     Record->addAttr(
123         HLSLResourceClassAttr::CreateImplicit(Record->getASTContext(), RC));
124     Record->addAttr(
125         HLSLResourceAttr::CreateImplicit(Record->getASTContext(), RK, IsROV));
126     return *this;
127   }
128 
129   static DeclRefExpr *lookupBuiltinFunction(ASTContext &AST, Sema &S,
130                                             StringRef Name) {
131     IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier);
132     DeclarationNameInfo NameInfo =
133         DeclarationNameInfo(DeclarationName(&II), SourceLocation());
134     LookupResult R(S, NameInfo, Sema::LookupOrdinaryName);
135     // AllowBuiltinCreation is false but LookupDirect will create
136     // the builtin when searching the global scope anyways...
137     S.LookupName(R, S.getCurScope());
138     // FIXME: If the builtin function was user-declared in global scope,
139     // this assert *will* fail. Should this call LookupBuiltin instead?
140     assert(R.isSingleResult() &&
141            "Since this is a builtin it should always resolve!");
142     auto *VD = cast<ValueDecl>(R.getFoundDecl());
143     QualType Ty = VD->getType();
144     return DeclRefExpr::Create(AST, NestedNameSpecifierLoc(), SourceLocation(),
145                                VD, false, NameInfo, Ty, VK_PRValue);
146   }
147 
148   static Expr *emitResourceClassExpr(ASTContext &AST, ResourceClass RC) {
149     return IntegerLiteral::Create(
150         AST,
151         llvm::APInt(AST.getIntWidth(AST.UnsignedCharTy),
152                     static_cast<uint8_t>(RC)),
153         AST.UnsignedCharTy, SourceLocation());
154   }
155 
156   BuiltinTypeDeclBuilder &addDefaultHandleConstructor(Sema &S,
157                                                       ResourceClass RC) {
158     if (Record->isCompleteDefinition())
159       return *this;
160     ASTContext &AST = Record->getASTContext();
161 
162     QualType ConstructorType =
163         AST.getFunctionType(AST.VoidTy, {}, FunctionProtoType::ExtProtoInfo());
164 
165     CanQualType CanTy = Record->getTypeForDecl()->getCanonicalTypeUnqualified();
166     DeclarationName Name = AST.DeclarationNames.getCXXConstructorName(CanTy);
167     CXXConstructorDecl *Constructor = CXXConstructorDecl::Create(
168         AST, Record, SourceLocation(),
169         DeclarationNameInfo(Name, SourceLocation()), ConstructorType,
170         AST.getTrivialTypeSourceInfo(ConstructorType, SourceLocation()),
171         ExplicitSpecifier(), false, true, false,
172         ConstexprSpecKind::Unspecified);
173 
174     DeclRefExpr *Fn =
175         lookupBuiltinFunction(AST, S, "__builtin_hlsl_create_handle");
176     Expr *RCExpr = emitResourceClassExpr(AST, RC);
177     Expr *Call = CallExpr::Create(AST, Fn, {RCExpr}, AST.VoidPtrTy, VK_PRValue,
178                                   SourceLocation(), FPOptionsOverride());
179 
180     CXXThisExpr *This = CXXThisExpr::Create(
181         AST, SourceLocation(), Constructor->getFunctionObjectParameterType(),
182         true);
183     Expr *Handle = MemberExpr::CreateImplicit(AST, This, false, Fields["h"],
184                                               Fields["h"]->getType(), VK_LValue,
185                                               OK_Ordinary);
186 
187     // If the handle isn't a void pointer, cast the builtin result to the
188     // correct type.
189     if (Handle->getType().getCanonicalType() != AST.VoidPtrTy) {
190       Call = CXXStaticCastExpr::Create(
191           AST, Handle->getType(), VK_PRValue, CK_Dependent, Call, nullptr,
192           AST.getTrivialTypeSourceInfo(Handle->getType(), SourceLocation()),
193           FPOptionsOverride(), SourceLocation(), SourceLocation(),
194           SourceRange());
195     }
196 
197     BinaryOperator *Assign = BinaryOperator::Create(
198         AST, Handle, Call, BO_Assign, Handle->getType(), VK_LValue, OK_Ordinary,
199         SourceLocation(), FPOptionsOverride());
200 
201     Constructor->setBody(
202         CompoundStmt::Create(AST, {Assign}, FPOptionsOverride(),
203                              SourceLocation(), SourceLocation()));
204     Constructor->setAccess(AccessSpecifier::AS_public);
205     Record->addDecl(Constructor);
206     return *this;
207   }
208 
209   BuiltinTypeDeclBuilder &addArraySubscriptOperators() {
210     if (Record->isCompleteDefinition())
211       return *this;
212     addArraySubscriptOperator(true);
213     addArraySubscriptOperator(false);
214     return *this;
215   }
216 
217   BuiltinTypeDeclBuilder &addArraySubscriptOperator(bool IsConst) {
218     if (Record->isCompleteDefinition())
219       return *this;
220     assert(Fields.count("h") > 0 &&
221            "Subscript operator must be added after the handle.");
222 
223     FieldDecl *Handle = Fields["h"];
224     ASTContext &AST = Record->getASTContext();
225 
226     assert(Handle->getType().getCanonicalType() != AST.VoidPtrTy &&
227            "Not yet supported for void pointer handles.");
228 
229     QualType ElemTy =
230         QualType(Handle->getType()->getPointeeOrArrayElementType(), 0);
231     QualType ReturnTy = ElemTy;
232 
233     FunctionProtoType::ExtProtoInfo ExtInfo;
234 
235     // Subscript operators return references to elements, const makes the
236     // reference and method const so that the underlying data is not mutable.
237     ReturnTy = AST.getLValueReferenceType(ReturnTy);
238     if (IsConst) {
239       ExtInfo.TypeQuals.addConst();
240       ReturnTy.addConst();
241     }
242 
243     QualType MethodTy =
244         AST.getFunctionType(ReturnTy, {AST.UnsignedIntTy}, ExtInfo);
245     auto *TSInfo = AST.getTrivialTypeSourceInfo(MethodTy, SourceLocation());
246     auto *MethodDecl = CXXMethodDecl::Create(
247         AST, Record, SourceLocation(),
248         DeclarationNameInfo(
249             AST.DeclarationNames.getCXXOperatorName(OO_Subscript),
250             SourceLocation()),
251         MethodTy, TSInfo, SC_None, false, false, ConstexprSpecKind::Unspecified,
252         SourceLocation());
253 
254     IdentifierInfo &II = AST.Idents.get("Idx", tok::TokenKind::identifier);
255     auto *IdxParam = ParmVarDecl::Create(
256         AST, MethodDecl->getDeclContext(), SourceLocation(), SourceLocation(),
257         &II, AST.UnsignedIntTy,
258         AST.getTrivialTypeSourceInfo(AST.UnsignedIntTy, SourceLocation()),
259         SC_None, nullptr);
260     MethodDecl->setParams({IdxParam});
261 
262     // Also add the parameter to the function prototype.
263     auto FnProtoLoc = TSInfo->getTypeLoc().getAs<FunctionProtoTypeLoc>();
264     FnProtoLoc.setParam(0, IdxParam);
265 
266     auto *This =
267         CXXThisExpr::Create(AST, SourceLocation(),
268                             MethodDecl->getFunctionObjectParameterType(), true);
269     auto *HandleAccess = MemberExpr::CreateImplicit(
270         AST, This, false, Handle, Handle->getType(), VK_LValue, OK_Ordinary);
271 
272     auto *IndexExpr = DeclRefExpr::Create(
273         AST, NestedNameSpecifierLoc(), SourceLocation(), IdxParam, false,
274         DeclarationNameInfo(IdxParam->getDeclName(), SourceLocation()),
275         AST.UnsignedIntTy, VK_PRValue);
276 
277     auto *Array =
278         new (AST) ArraySubscriptExpr(HandleAccess, IndexExpr, ElemTy, VK_LValue,
279                                      OK_Ordinary, SourceLocation());
280 
281     auto *Return = ReturnStmt::Create(AST, SourceLocation(), Array, nullptr);
282 
283     MethodDecl->setBody(CompoundStmt::Create(AST, {Return}, FPOptionsOverride(),
284                                              SourceLocation(),
285                                              SourceLocation()));
286     MethodDecl->setLexicalDeclContext(Record);
287     MethodDecl->setAccess(AccessSpecifier::AS_public);
288     MethodDecl->addAttr(AlwaysInlineAttr::CreateImplicit(
289         AST, SourceRange(), AlwaysInlineAttr::CXX11_clang_always_inline));
290     Record->addDecl(MethodDecl);
291 
292     return *this;
293   }
294 
295   BuiltinTypeDeclBuilder &startDefinition() {
296     if (Record->isCompleteDefinition())
297       return *this;
298     Record->startDefinition();
299     return *this;
300   }
301 
302   BuiltinTypeDeclBuilder &completeDefinition() {
303     if (Record->isCompleteDefinition())
304       return *this;
305     assert(Record->isBeingDefined() &&
306            "Definition must be started before completing it.");
307 
308     Record->completeDefinition();
309     return *this;
310   }
311 
312   TemplateParameterListBuilder addTemplateArgumentList(Sema &S);
313   BuiltinTypeDeclBuilder &addSimpleTemplateParams(Sema &S,
314                                                   ArrayRef<StringRef> Names);
315 };
316 
317 struct TemplateParameterListBuilder {
318   BuiltinTypeDeclBuilder &Builder;
319   Sema &S;
320   llvm::SmallVector<NamedDecl *> Params;
321 
322   TemplateParameterListBuilder(Sema &S, BuiltinTypeDeclBuilder &RB)
323       : Builder(RB), S(S) {}
324 
325   ~TemplateParameterListBuilder() { finalizeTemplateArgs(); }
326 
327   TemplateParameterListBuilder &
328   addTypeParameter(StringRef Name, QualType DefaultValue = QualType()) {
329     if (Builder.Record->isCompleteDefinition())
330       return *this;
331     unsigned Position = static_cast<unsigned>(Params.size());
332     auto *Decl = TemplateTypeParmDecl::Create(
333         S.Context, Builder.Record->getDeclContext(), SourceLocation(),
334         SourceLocation(), /* TemplateDepth */ 0, Position,
335         &S.Context.Idents.get(Name, tok::TokenKind::identifier),
336         /* Typename */ false,
337         /* ParameterPack */ false);
338     if (!DefaultValue.isNull())
339       Decl->setDefaultArgument(
340           S.Context, S.getTrivialTemplateArgumentLoc(DefaultValue, QualType(),
341                                                      SourceLocation()));
342 
343     Params.emplace_back(Decl);
344     return *this;
345   }
346 
347   BuiltinTypeDeclBuilder &finalizeTemplateArgs() {
348     if (Params.empty())
349       return Builder;
350     auto *ParamList = TemplateParameterList::Create(S.Context, SourceLocation(),
351                                                     SourceLocation(), Params,
352                                                     SourceLocation(), nullptr);
353     Builder.Template = ClassTemplateDecl::Create(
354         S.Context, Builder.Record->getDeclContext(), SourceLocation(),
355         DeclarationName(Builder.Record->getIdentifier()), ParamList,
356         Builder.Record);
357     Builder.Record->setDescribedClassTemplate(Builder.Template);
358     Builder.Template->setImplicit(true);
359     Builder.Template->setLexicalDeclContext(Builder.Record->getDeclContext());
360     // NOTE: setPreviousDecl before addDecl so new decl replace old decl when
361     // make visible.
362     Builder.Template->setPreviousDecl(Builder.PrevTemplate);
363     Builder.Record->getDeclContext()->addDecl(Builder.Template);
364     Params.clear();
365 
366     QualType T = Builder.Template->getInjectedClassNameSpecialization();
367     T = S.Context.getInjectedClassNameType(Builder.Record, T);
368 
369     return Builder;
370   }
371 };
372 } // namespace
373 
374 TemplateParameterListBuilder
375 BuiltinTypeDeclBuilder::addTemplateArgumentList(Sema &S) {
376   return TemplateParameterListBuilder(S, *this);
377 }
378 
379 BuiltinTypeDeclBuilder &
380 BuiltinTypeDeclBuilder::addSimpleTemplateParams(Sema &S,
381                                                 ArrayRef<StringRef> Names) {
382   TemplateParameterListBuilder Builder = this->addTemplateArgumentList(S);
383   for (StringRef Name : Names)
384     Builder.addTypeParameter(Name);
385   return Builder.finalizeTemplateArgs();
386 }
387 
388 HLSLExternalSemaSource::~HLSLExternalSemaSource() {}
389 
390 void HLSLExternalSemaSource::InitializeSema(Sema &S) {
391   SemaPtr = &S;
392   ASTContext &AST = SemaPtr->getASTContext();
393   // If the translation unit has external storage force external decls to load.
394   if (AST.getTranslationUnitDecl()->hasExternalLexicalStorage())
395     (void)AST.getTranslationUnitDecl()->decls_begin();
396 
397   IdentifierInfo &HLSL = AST.Idents.get("hlsl", tok::TokenKind::identifier);
398   LookupResult Result(S, &HLSL, SourceLocation(), Sema::LookupNamespaceName);
399   NamespaceDecl *PrevDecl = nullptr;
400   if (S.LookupQualifiedName(Result, AST.getTranslationUnitDecl()))
401     PrevDecl = Result.getAsSingle<NamespaceDecl>();
402   HLSLNamespace = NamespaceDecl::Create(
403       AST, AST.getTranslationUnitDecl(), /*Inline=*/false, SourceLocation(),
404       SourceLocation(), &HLSL, PrevDecl, /*Nested=*/false);
405   HLSLNamespace->setImplicit(true);
406   HLSLNamespace->setHasExternalLexicalStorage();
407   AST.getTranslationUnitDecl()->addDecl(HLSLNamespace);
408 
409   // Force external decls in the HLSL namespace to load from the PCH.
410   (void)HLSLNamespace->getCanonicalDecl()->decls_begin();
411   defineTrivialHLSLTypes();
412   defineHLSLTypesWithForwardDeclarations();
413 
414   // This adds a `using namespace hlsl` directive. In DXC, we don't put HLSL's
415   // built in types inside a namespace, but we are planning to change that in
416   // the near future. In order to be source compatible older versions of HLSL
417   // will need to implicitly use the hlsl namespace. For now in clang everything
418   // will get added to the namespace, and we can remove the using directive for
419   // future language versions to match HLSL's evolution.
420   auto *UsingDecl = UsingDirectiveDecl::Create(
421       AST, AST.getTranslationUnitDecl(), SourceLocation(), SourceLocation(),
422       NestedNameSpecifierLoc(), SourceLocation(), HLSLNamespace,
423       AST.getTranslationUnitDecl());
424 
425   AST.getTranslationUnitDecl()->addDecl(UsingDecl);
426 }
427 
428 void HLSLExternalSemaSource::defineHLSLVectorAlias() {
429   ASTContext &AST = SemaPtr->getASTContext();
430 
431   llvm::SmallVector<NamedDecl *> TemplateParams;
432 
433   auto *TypeParam = TemplateTypeParmDecl::Create(
434       AST, HLSLNamespace, SourceLocation(), SourceLocation(), 0, 0,
435       &AST.Idents.get("element", tok::TokenKind::identifier), false, false);
436   TypeParam->setDefaultArgument(
437       AST, SemaPtr->getTrivialTemplateArgumentLoc(
438                TemplateArgument(AST.FloatTy), QualType(), SourceLocation()));
439 
440   TemplateParams.emplace_back(TypeParam);
441 
442   auto *SizeParam = NonTypeTemplateParmDecl::Create(
443       AST, HLSLNamespace, SourceLocation(), SourceLocation(), 0, 1,
444       &AST.Idents.get("element_count", tok::TokenKind::identifier), AST.IntTy,
445       false, AST.getTrivialTypeSourceInfo(AST.IntTy));
446   llvm::APInt Val(AST.getIntWidth(AST.IntTy), 4);
447   TemplateArgument Default(AST, llvm::APSInt(std::move(Val)), AST.IntTy,
448                            /*IsDefaulted=*/true);
449   SizeParam->setDefaultArgument(
450       AST, SemaPtr->getTrivialTemplateArgumentLoc(Default, AST.IntTy,
451                                                   SourceLocation(), SizeParam));
452   TemplateParams.emplace_back(SizeParam);
453 
454   auto *ParamList =
455       TemplateParameterList::Create(AST, SourceLocation(), SourceLocation(),
456                                     TemplateParams, SourceLocation(), nullptr);
457 
458   IdentifierInfo &II = AST.Idents.get("vector", tok::TokenKind::identifier);
459 
460   QualType AliasType = AST.getDependentSizedExtVectorType(
461       AST.getTemplateTypeParmType(0, 0, false, TypeParam),
462       DeclRefExpr::Create(
463           AST, NestedNameSpecifierLoc(), SourceLocation(), SizeParam, false,
464           DeclarationNameInfo(SizeParam->getDeclName(), SourceLocation()),
465           AST.IntTy, VK_LValue),
466       SourceLocation());
467 
468   auto *Record = TypeAliasDecl::Create(AST, HLSLNamespace, SourceLocation(),
469                                        SourceLocation(), &II,
470                                        AST.getTrivialTypeSourceInfo(AliasType));
471   Record->setImplicit(true);
472 
473   auto *Template =
474       TypeAliasTemplateDecl::Create(AST, HLSLNamespace, SourceLocation(),
475                                     Record->getIdentifier(), ParamList, Record);
476 
477   Record->setDescribedAliasTemplate(Template);
478   Template->setImplicit(true);
479   Template->setLexicalDeclContext(Record->getDeclContext());
480   HLSLNamespace->addDecl(Template);
481 }
482 
483 void HLSLExternalSemaSource::defineTrivialHLSLTypes() {
484   defineHLSLVectorAlias();
485 }
486 
487 /// Set up common members and attributes for buffer types
488 static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S,
489                                               ResourceClass RC, ResourceKind RK,
490                                               bool IsROV) {
491   return BuiltinTypeDeclBuilder(Decl)
492       .addHandleMember()
493       .addDefaultHandleConstructor(S, RC)
494       .annotateHLSLResource(RC, RK, IsROV);
495 }
496 
497 void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
498   CXXRecordDecl *Decl;
499   Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWBuffer")
500              .addSimpleTemplateParams(*SemaPtr, {"element_type"})
501              .Record;
502   onCompletion(Decl, [this](CXXRecordDecl *Decl) {
503     setupBufferType(Decl, *SemaPtr, ResourceClass::UAV,
504                     ResourceKind::TypedBuffer, /*IsROV=*/false)
505         .addArraySubscriptOperators()
506         .completeDefinition();
507   });
508 
509   Decl =
510       BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RasterizerOrderedBuffer")
511           .addSimpleTemplateParams(*SemaPtr, {"element_type"})
512           .Record;
513   onCompletion(Decl, [this](CXXRecordDecl *Decl) {
514     setupBufferType(Decl, *SemaPtr, ResourceClass::UAV,
515                     ResourceKind::TypedBuffer, /*IsROV=*/true)
516         .addArraySubscriptOperators()
517         .completeDefinition();
518   });
519 }
520 
521 void HLSLExternalSemaSource::onCompletion(CXXRecordDecl *Record,
522                                           CompletionFunction Fn) {
523   Completions.insert(std::make_pair(Record->getCanonicalDecl(), Fn));
524 }
525 
526 void HLSLExternalSemaSource::CompleteType(TagDecl *Tag) {
527   if (!isa<CXXRecordDecl>(Tag))
528     return;
529   auto Record = cast<CXXRecordDecl>(Tag);
530 
531   // If this is a specialization, we need to get the underlying templated
532   // declaration and complete that.
533   if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(Record))
534     Record = TDecl->getSpecializedTemplate()->getTemplatedDecl();
535   Record = Record->getCanonicalDecl();
536   auto It = Completions.find(Record);
537   if (It == Completions.end())
538     return;
539   It->second(Record);
540 }
541