xref: /openbsd-src/gnu/llvm/clang/lib/AST/Interp/Program.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
1e5dd7070Spatrick //===--- Program.cpp - Bytecode for the constexpr VM ------------*- C++ -*-===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick 
9e5dd7070Spatrick #include "Program.h"
10e5dd7070Spatrick #include "ByteCodeStmtGen.h"
11e5dd7070Spatrick #include "Context.h"
12e5dd7070Spatrick #include "Function.h"
13e5dd7070Spatrick #include "Opcode.h"
14e5dd7070Spatrick #include "PrimType.h"
15e5dd7070Spatrick #include "clang/AST/Decl.h"
16e5dd7070Spatrick #include "clang/AST/DeclCXX.h"
17e5dd7070Spatrick 
18e5dd7070Spatrick using namespace clang;
19e5dd7070Spatrick using namespace clang::interp;
20e5dd7070Spatrick 
getOrCreateNativePointer(const void * Ptr)21*12c85518Srobert unsigned Program::getOrCreateNativePointer(const void *Ptr) {
22*12c85518Srobert   auto It = NativePointerIndices.find(Ptr);
23*12c85518Srobert   if (It != NativePointerIndices.end())
24*12c85518Srobert     return It->second;
25*12c85518Srobert 
26*12c85518Srobert   unsigned Idx = NativePointers.size();
27*12c85518Srobert   NativePointers.push_back(Ptr);
28*12c85518Srobert   NativePointerIndices[Ptr] = Idx;
29*12c85518Srobert   return Idx;
30*12c85518Srobert }
31*12c85518Srobert 
getNativePointer(unsigned Idx)32*12c85518Srobert const void *Program::getNativePointer(unsigned Idx) {
33*12c85518Srobert   return NativePointers[Idx];
34*12c85518Srobert }
35*12c85518Srobert 
createGlobalString(const StringLiteral * S)36e5dd7070Spatrick unsigned Program::createGlobalString(const StringLiteral *S) {
37e5dd7070Spatrick   const size_t CharWidth = S->getCharByteWidth();
38e5dd7070Spatrick   const size_t BitWidth = CharWidth * Ctx.getCharBit();
39e5dd7070Spatrick 
40e5dd7070Spatrick   PrimType CharType;
41e5dd7070Spatrick   switch (CharWidth) {
42e5dd7070Spatrick   case 1:
43e5dd7070Spatrick     CharType = PT_Sint8;
44e5dd7070Spatrick     break;
45e5dd7070Spatrick   case 2:
46e5dd7070Spatrick     CharType = PT_Uint16;
47e5dd7070Spatrick     break;
48e5dd7070Spatrick   case 4:
49e5dd7070Spatrick     CharType = PT_Uint32;
50e5dd7070Spatrick     break;
51e5dd7070Spatrick   default:
52e5dd7070Spatrick     llvm_unreachable("unsupported character width");
53e5dd7070Spatrick   }
54e5dd7070Spatrick 
55e5dd7070Spatrick   // Create a descriptor for the string.
56*12c85518Srobert   Descriptor *Desc =
57*12c85518Srobert       allocateDescriptor(S, CharType, std::nullopt, S->getLength() + 1,
58e5dd7070Spatrick                          /*isConst=*/true,
59e5dd7070Spatrick                          /*isTemporary=*/false,
60e5dd7070Spatrick                          /*isMutable=*/false);
61e5dd7070Spatrick 
62e5dd7070Spatrick   // Allocate storage for the string.
63e5dd7070Spatrick   // The byte length does not include the null terminator.
64e5dd7070Spatrick   unsigned I = Globals.size();
65e5dd7070Spatrick   unsigned Sz = Desc->getAllocSize();
66e5dd7070Spatrick   auto *G = new (Allocator, Sz) Global(Desc, /*isStatic=*/true,
67e5dd7070Spatrick                                        /*isExtern=*/false);
68*12c85518Srobert   G->block()->invokeCtor();
69e5dd7070Spatrick   Globals.push_back(G);
70e5dd7070Spatrick 
71e5dd7070Spatrick   // Construct the string in storage.
72e5dd7070Spatrick   const Pointer Ptr(G->block());
73e5dd7070Spatrick   for (unsigned I = 0, N = S->getLength(); I <= N; ++I) {
74e5dd7070Spatrick     Pointer Field = Ptr.atIndex(I).narrow();
75e5dd7070Spatrick     const uint32_t CodePoint = I == N ? 0 : S->getCodeUnit(I);
76e5dd7070Spatrick     switch (CharType) {
77e5dd7070Spatrick       case PT_Sint8: {
78e5dd7070Spatrick         using T = PrimConv<PT_Sint8>::T;
79e5dd7070Spatrick         Field.deref<T>() = T::from(CodePoint, BitWidth);
80e5dd7070Spatrick         break;
81e5dd7070Spatrick       }
82e5dd7070Spatrick       case PT_Uint16: {
83e5dd7070Spatrick         using T = PrimConv<PT_Uint16>::T;
84e5dd7070Spatrick         Field.deref<T>() = T::from(CodePoint, BitWidth);
85e5dd7070Spatrick         break;
86e5dd7070Spatrick       }
87e5dd7070Spatrick       case PT_Uint32: {
88e5dd7070Spatrick         using T = PrimConv<PT_Uint32>::T;
89e5dd7070Spatrick         Field.deref<T>() = T::from(CodePoint, BitWidth);
90e5dd7070Spatrick         break;
91e5dd7070Spatrick       }
92e5dd7070Spatrick       default:
93e5dd7070Spatrick         llvm_unreachable("unsupported character type");
94e5dd7070Spatrick     }
95e5dd7070Spatrick   }
96e5dd7070Spatrick   return I;
97e5dd7070Spatrick }
98e5dd7070Spatrick 
getPtrGlobal(unsigned Idx)99e5dd7070Spatrick Pointer Program::getPtrGlobal(unsigned Idx) {
100e5dd7070Spatrick   assert(Idx < Globals.size());
101e5dd7070Spatrick   return Pointer(Globals[Idx]->block());
102e5dd7070Spatrick }
103e5dd7070Spatrick 
getGlobal(const ValueDecl * VD)104*12c85518Srobert std::optional<unsigned> Program::getGlobal(const ValueDecl *VD) {
105e5dd7070Spatrick   auto It = GlobalIndices.find(VD);
106e5dd7070Spatrick   if (It != GlobalIndices.end())
107e5dd7070Spatrick     return It->second;
108e5dd7070Spatrick 
109*12c85518Srobert   // Find any previous declarations which were already evaluated.
110*12c85518Srobert   std::optional<unsigned> Index;
111e5dd7070Spatrick   for (const Decl *P = VD; P; P = P->getPreviousDecl()) {
112e5dd7070Spatrick     auto It = GlobalIndices.find(P);
113e5dd7070Spatrick     if (It != GlobalIndices.end()) {
114e5dd7070Spatrick       Index = It->second;
115e5dd7070Spatrick       break;
116e5dd7070Spatrick     }
117e5dd7070Spatrick   }
118e5dd7070Spatrick 
119e5dd7070Spatrick   // Map the decl to the existing index.
120e5dd7070Spatrick   if (Index) {
121e5dd7070Spatrick     GlobalIndices[VD] = *Index;
122e5dd7070Spatrick     return {};
123e5dd7070Spatrick   }
124e5dd7070Spatrick 
125e5dd7070Spatrick   return Index;
126e5dd7070Spatrick }
127e5dd7070Spatrick 
getOrCreateGlobal(const ValueDecl * VD,const Expr * Init)128*12c85518Srobert std::optional<unsigned> Program::getOrCreateGlobal(const ValueDecl *VD,
129*12c85518Srobert                                                    const Expr *Init) {
130e5dd7070Spatrick   if (auto Idx = getGlobal(VD))
131e5dd7070Spatrick     return Idx;
132e5dd7070Spatrick 
133*12c85518Srobert   if (auto Idx = createGlobal(VD, Init)) {
134e5dd7070Spatrick     GlobalIndices[VD] = *Idx;
135e5dd7070Spatrick     return Idx;
136e5dd7070Spatrick   }
137e5dd7070Spatrick   return {};
138e5dd7070Spatrick }
139e5dd7070Spatrick 
getOrCreateDummy(const ParmVarDecl * PD)140*12c85518Srobert std::optional<unsigned> Program::getOrCreateDummy(const ParmVarDecl *PD) {
141e5dd7070Spatrick   auto &ASTCtx = Ctx.getASTContext();
142e5dd7070Spatrick 
143e5dd7070Spatrick   // Create a pointer to an incomplete array of the specified elements.
144e5dd7070Spatrick   QualType ElemTy = PD->getType()->castAs<PointerType>()->getPointeeType();
145e5dd7070Spatrick   QualType Ty = ASTCtx.getIncompleteArrayType(ElemTy, ArrayType::Normal, 0);
146e5dd7070Spatrick 
147e5dd7070Spatrick   // Dedup blocks since they are immutable and pointers cannot be compared.
148e5dd7070Spatrick   auto It = DummyParams.find(PD);
149e5dd7070Spatrick   if (It != DummyParams.end())
150e5dd7070Spatrick     return It->second;
151e5dd7070Spatrick 
152e5dd7070Spatrick   if (auto Idx = createGlobal(PD, Ty, /*isStatic=*/true, /*isExtern=*/true)) {
153e5dd7070Spatrick     DummyParams[PD] = *Idx;
154e5dd7070Spatrick     return Idx;
155e5dd7070Spatrick   }
156e5dd7070Spatrick   return {};
157e5dd7070Spatrick }
158e5dd7070Spatrick 
createGlobal(const ValueDecl * VD,const Expr * Init)159*12c85518Srobert std::optional<unsigned> Program::createGlobal(const ValueDecl *VD,
160*12c85518Srobert                                               const Expr *Init) {
161*12c85518Srobert   assert(!getGlobal(VD));
162e5dd7070Spatrick   bool IsStatic, IsExtern;
163e5dd7070Spatrick   if (auto *Var = dyn_cast<VarDecl>(VD)) {
164e5dd7070Spatrick     IsStatic = !Var->hasLocalStorage();
165e5dd7070Spatrick     IsExtern = !Var->getAnyInitializer();
166e5dd7070Spatrick   } else {
167e5dd7070Spatrick     IsStatic = false;
168e5dd7070Spatrick     IsExtern = true;
169e5dd7070Spatrick   }
170*12c85518Srobert   if (auto Idx = createGlobal(VD, VD->getType(), IsStatic, IsExtern, Init)) {
171e5dd7070Spatrick     for (const Decl *P = VD; P; P = P->getPreviousDecl())
172e5dd7070Spatrick       GlobalIndices[P] = *Idx;
173e5dd7070Spatrick     return *Idx;
174e5dd7070Spatrick   }
175e5dd7070Spatrick   return {};
176e5dd7070Spatrick }
177e5dd7070Spatrick 
createGlobal(const Expr * E)178*12c85518Srobert std::optional<unsigned> Program::createGlobal(const Expr *E) {
179e5dd7070Spatrick   return createGlobal(E, E->getType(), /*isStatic=*/true, /*isExtern=*/false);
180e5dd7070Spatrick }
181e5dd7070Spatrick 
createGlobal(const DeclTy & D,QualType Ty,bool IsStatic,bool IsExtern,const Expr * Init)182*12c85518Srobert std::optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty,
183*12c85518Srobert                                               bool IsStatic, bool IsExtern,
184*12c85518Srobert                                               const Expr *Init) {
185e5dd7070Spatrick   // Create a descriptor for the global.
186e5dd7070Spatrick   Descriptor *Desc;
187e5dd7070Spatrick   const bool IsConst = Ty.isConstQualified();
188e5dd7070Spatrick   const bool IsTemporary = D.dyn_cast<const Expr *>();
189e5dd7070Spatrick   if (auto T = Ctx.classify(Ty)) {
190*12c85518Srobert     Desc = createDescriptor(D, *T, std::nullopt, IsConst, IsTemporary);
191e5dd7070Spatrick   } else {
192*12c85518Srobert     Desc = createDescriptor(D, Ty.getTypePtr(), std::nullopt, IsConst,
193*12c85518Srobert                             IsTemporary);
194e5dd7070Spatrick   }
195e5dd7070Spatrick   if (!Desc)
196e5dd7070Spatrick     return {};
197e5dd7070Spatrick 
198e5dd7070Spatrick   // Allocate a block for storage.
199e5dd7070Spatrick   unsigned I = Globals.size();
200e5dd7070Spatrick 
201e5dd7070Spatrick   auto *G = new (Allocator, Desc->getAllocSize())
202e5dd7070Spatrick       Global(getCurrentDecl(), Desc, IsStatic, IsExtern);
203e5dd7070Spatrick   G->block()->invokeCtor();
204e5dd7070Spatrick 
205e5dd7070Spatrick   Globals.push_back(G);
206e5dd7070Spatrick 
207e5dd7070Spatrick   return I;
208e5dd7070Spatrick }
209e5dd7070Spatrick 
getFunction(const FunctionDecl * F)210e5dd7070Spatrick Function *Program::getFunction(const FunctionDecl *F) {
211*12c85518Srobert   F = F->getCanonicalDecl();
212*12c85518Srobert   assert(F);
213e5dd7070Spatrick   auto It = Funcs.find(F);
214e5dd7070Spatrick   return It == Funcs.end() ? nullptr : It->second.get();
215e5dd7070Spatrick }
216e5dd7070Spatrick 
getOrCreateRecord(const RecordDecl * RD)217e5dd7070Spatrick Record *Program::getOrCreateRecord(const RecordDecl *RD) {
218e5dd7070Spatrick   // Use the actual definition as a key.
219e5dd7070Spatrick   RD = RD->getDefinition();
220e5dd7070Spatrick   if (!RD)
221e5dd7070Spatrick     return nullptr;
222e5dd7070Spatrick 
223e5dd7070Spatrick   // Deduplicate records.
224e5dd7070Spatrick   auto It = Records.find(RD);
225e5dd7070Spatrick   if (It != Records.end()) {
226e5dd7070Spatrick     return It->second;
227e5dd7070Spatrick   }
228e5dd7070Spatrick 
229*12c85518Srobert   // We insert nullptr now and replace that later, so recursive calls
230*12c85518Srobert   // to this function with the same RecordDecl don't run into
231*12c85518Srobert   // infinite recursion.
232*12c85518Srobert   Records.insert({RD, nullptr});
233*12c85518Srobert 
234e5dd7070Spatrick   // Number of bytes required by fields and base classes.
235*12c85518Srobert   unsigned BaseSize = 0;
236e5dd7070Spatrick   // Number of bytes required by virtual base.
237e5dd7070Spatrick   unsigned VirtSize = 0;
238e5dd7070Spatrick 
239e5dd7070Spatrick   // Helper to get a base descriptor.
240e5dd7070Spatrick   auto GetBaseDesc = [this](const RecordDecl *BD, Record *BR) -> Descriptor * {
241e5dd7070Spatrick     if (!BR)
242e5dd7070Spatrick       return nullptr;
243*12c85518Srobert     return allocateDescriptor(BD, BR, std::nullopt, /*isConst=*/false,
244e5dd7070Spatrick                               /*isTemporary=*/false,
245e5dd7070Spatrick                               /*isMutable=*/false);
246e5dd7070Spatrick   };
247e5dd7070Spatrick 
248e5dd7070Spatrick   // Reserve space for base classes.
249e5dd7070Spatrick   Record::BaseList Bases;
250e5dd7070Spatrick   Record::VirtualBaseList VirtBases;
251e5dd7070Spatrick   if (auto *CD = dyn_cast<CXXRecordDecl>(RD)) {
252e5dd7070Spatrick     for (const CXXBaseSpecifier &Spec : CD->bases()) {
253e5dd7070Spatrick       if (Spec.isVirtual())
254e5dd7070Spatrick         continue;
255e5dd7070Spatrick 
256e5dd7070Spatrick       const RecordDecl *BD = Spec.getType()->castAs<RecordType>()->getDecl();
257e5dd7070Spatrick       Record *BR = getOrCreateRecord(BD);
258e5dd7070Spatrick       if (Descriptor *Desc = GetBaseDesc(BD, BR)) {
259*12c85518Srobert         BaseSize += align(sizeof(InlineDescriptor));
260*12c85518Srobert         Bases.push_back({BD, BaseSize, Desc, BR});
261*12c85518Srobert         BaseSize += align(BR->getSize());
262e5dd7070Spatrick         continue;
263e5dd7070Spatrick       }
264e5dd7070Spatrick       return nullptr;
265e5dd7070Spatrick     }
266e5dd7070Spatrick 
267e5dd7070Spatrick     for (const CXXBaseSpecifier &Spec : CD->vbases()) {
268e5dd7070Spatrick       const RecordDecl *BD = Spec.getType()->castAs<RecordType>()->getDecl();
269e5dd7070Spatrick       Record *BR = getOrCreateRecord(BD);
270e5dd7070Spatrick 
271e5dd7070Spatrick       if (Descriptor *Desc = GetBaseDesc(BD, BR)) {
272e5dd7070Spatrick         VirtSize += align(sizeof(InlineDescriptor));
273e5dd7070Spatrick         VirtBases.push_back({BD, VirtSize, Desc, BR});
274e5dd7070Spatrick         VirtSize += align(BR->getSize());
275e5dd7070Spatrick         continue;
276e5dd7070Spatrick       }
277e5dd7070Spatrick       return nullptr;
278e5dd7070Spatrick     }
279e5dd7070Spatrick   }
280e5dd7070Spatrick 
281e5dd7070Spatrick   // Reserve space for fields.
282e5dd7070Spatrick   Record::FieldList Fields;
283e5dd7070Spatrick   for (const FieldDecl *FD : RD->fields()) {
284e5dd7070Spatrick     // Reserve space for the field's descriptor and the offset.
285*12c85518Srobert     BaseSize += align(sizeof(InlineDescriptor));
286e5dd7070Spatrick 
287e5dd7070Spatrick     // Classify the field and add its metadata.
288e5dd7070Spatrick     QualType FT = FD->getType();
289e5dd7070Spatrick     const bool IsConst = FT.isConstQualified();
290e5dd7070Spatrick     const bool IsMutable = FD->isMutable();
291e5dd7070Spatrick     Descriptor *Desc;
292*12c85518Srobert     if (std::optional<PrimType> T = Ctx.classify(FT)) {
293*12c85518Srobert       Desc = createDescriptor(FD, *T, std::nullopt, IsConst,
294*12c85518Srobert                               /*isTemporary=*/false, IsMutable);
295e5dd7070Spatrick     } else {
296*12c85518Srobert       Desc = createDescriptor(FD, FT.getTypePtr(), std::nullopt, IsConst,
297e5dd7070Spatrick                               /*isTemporary=*/false, IsMutable);
298e5dd7070Spatrick     }
299e5dd7070Spatrick     if (!Desc)
300e5dd7070Spatrick       return nullptr;
301*12c85518Srobert     Fields.push_back({FD, BaseSize, Desc});
302*12c85518Srobert     BaseSize += align(Desc->getAllocSize());
303e5dd7070Spatrick   }
304e5dd7070Spatrick 
305e5dd7070Spatrick   Record *R = new (Allocator) Record(RD, std::move(Bases), std::move(Fields),
306*12c85518Srobert                                      std::move(VirtBases), VirtSize, BaseSize);
307*12c85518Srobert   Records[RD] = R;
308e5dd7070Spatrick   return R;
309e5dd7070Spatrick }
310e5dd7070Spatrick 
createDescriptor(const DeclTy & D,const Type * Ty,Descriptor::MetadataSize MDSize,bool IsConst,bool IsTemporary,bool IsMutable,const Expr * Init)311e5dd7070Spatrick Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty,
312*12c85518Srobert                                       Descriptor::MetadataSize MDSize,
313e5dd7070Spatrick                                       bool IsConst, bool IsTemporary,
314*12c85518Srobert                                       bool IsMutable, const Expr *Init) {
315e5dd7070Spatrick   // Classes and structures.
316e5dd7070Spatrick   if (auto *RT = Ty->getAs<RecordType>()) {
317e5dd7070Spatrick     if (auto *Record = getOrCreateRecord(RT->getDecl()))
318*12c85518Srobert       return allocateDescriptor(D, Record, MDSize, IsConst, IsTemporary,
319*12c85518Srobert                                 IsMutable);
320e5dd7070Spatrick   }
321e5dd7070Spatrick 
322e5dd7070Spatrick   // Arrays.
323e5dd7070Spatrick   if (auto ArrayType = Ty->getAsArrayTypeUnsafe()) {
324e5dd7070Spatrick     QualType ElemTy = ArrayType->getElementType();
325e5dd7070Spatrick     // Array of well-known bounds.
326e5dd7070Spatrick     if (auto CAT = dyn_cast<ConstantArrayType>(ArrayType)) {
327e5dd7070Spatrick       size_t NumElems = CAT->getSize().getZExtValue();
328*12c85518Srobert       if (std::optional<PrimType> T = Ctx.classify(ElemTy)) {
329e5dd7070Spatrick         // Arrays of primitives.
330e5dd7070Spatrick         unsigned ElemSize = primSize(*T);
331e5dd7070Spatrick         if (std::numeric_limits<unsigned>::max() / ElemSize <= NumElems) {
332e5dd7070Spatrick           return {};
333e5dd7070Spatrick         }
334*12c85518Srobert         return allocateDescriptor(D, *T, MDSize, NumElems, IsConst, IsTemporary,
335e5dd7070Spatrick                                   IsMutable);
336e5dd7070Spatrick       } else {
337e5dd7070Spatrick         // Arrays of composites. In this case, the array is a list of pointers,
338e5dd7070Spatrick         // followed by the actual elements.
339*12c85518Srobert         Descriptor *ElemDesc = createDescriptor(
340*12c85518Srobert             D, ElemTy.getTypePtr(), std::nullopt, IsConst, IsTemporary);
341*12c85518Srobert         if (!ElemDesc)
342e5dd7070Spatrick           return nullptr;
343*12c85518Srobert         InterpSize ElemSize =
344*12c85518Srobert             ElemDesc->getAllocSize() + sizeof(InlineDescriptor);
345e5dd7070Spatrick         if (std::numeric_limits<unsigned>::max() / ElemSize <= NumElems)
346e5dd7070Spatrick           return {};
347*12c85518Srobert         return allocateDescriptor(D, ElemDesc, MDSize, NumElems, IsConst,
348*12c85518Srobert                                   IsTemporary, IsMutable);
349e5dd7070Spatrick       }
350e5dd7070Spatrick     }
351e5dd7070Spatrick 
352e5dd7070Spatrick     // Array of unknown bounds - cannot be accessed and pointer arithmetic
353e5dd7070Spatrick     // is forbidden on pointers to such objects.
354e5dd7070Spatrick     if (isa<IncompleteArrayType>(ArrayType)) {
355*12c85518Srobert       if (std::optional<PrimType> T = Ctx.classify(ElemTy)) {
356e5dd7070Spatrick         return allocateDescriptor(D, *T, IsTemporary,
357e5dd7070Spatrick                                   Descriptor::UnknownSize{});
358e5dd7070Spatrick       } else {
359*12c85518Srobert         Descriptor *Desc = createDescriptor(D, ElemTy.getTypePtr(), MDSize,
360*12c85518Srobert                                             IsConst, IsTemporary);
361e5dd7070Spatrick         if (!Desc)
362e5dd7070Spatrick           return nullptr;
363e5dd7070Spatrick         return allocateDescriptor(D, Desc, IsTemporary,
364e5dd7070Spatrick                                   Descriptor::UnknownSize{});
365e5dd7070Spatrick       }
366e5dd7070Spatrick     }
367e5dd7070Spatrick   }
368e5dd7070Spatrick 
369e5dd7070Spatrick   // Atomic types.
370e5dd7070Spatrick   if (auto *AT = Ty->getAs<AtomicType>()) {
371e5dd7070Spatrick     const Type *InnerTy = AT->getValueType().getTypePtr();
372*12c85518Srobert     return createDescriptor(D, InnerTy, MDSize, IsConst, IsTemporary,
373*12c85518Srobert                             IsMutable);
374e5dd7070Spatrick   }
375e5dd7070Spatrick 
376e5dd7070Spatrick   // Complex types - represented as arrays of elements.
377e5dd7070Spatrick   if (auto *CT = Ty->getAs<ComplexType>()) {
378e5dd7070Spatrick     PrimType ElemTy = *Ctx.classify(CT->getElementType());
379*12c85518Srobert     return allocateDescriptor(D, ElemTy, MDSize, 2, IsConst, IsTemporary,
380*12c85518Srobert                               IsMutable);
381e5dd7070Spatrick   }
382e5dd7070Spatrick 
383e5dd7070Spatrick   return nullptr;
384e5dd7070Spatrick }
385