1010//
1111// ===----------------------------------------------------------------------===//
1212
13+ #include " ArgumentScope.h"
14+ #include " RValue.h"
1315#include " SILGenFunction.h"
1416#include " SILGenFunctionBuilder.h"
15- #include " RValue.h"
16- #include " ArgumentScope.h"
17+ #include " SwitchEnumBuilder.h"
1718#include " swift/AST/GenericSignature.h"
1819#include " swift/AST/SubstitutionMap.h"
1920#include " swift/SIL/TypeLowering.h"
21+ #include " llvm/ADT/SmallSet.h"
2022
2123using namespace swift ;
2224using namespace Lowering ;
@@ -219,6 +221,142 @@ void SILGenFunction::destroyClassMember(SILLocation cleanupLoc,
219221 }
220222}
221223
224+ // / Finds stored properties that have the same type as `cd` and thus form
225+ // / a recursive structure.
226+ // /
227+ // / Example:
228+ // /
229+ // / class Node<T> {
230+ // / let element: T
231+ // / let next: Node<T>?
232+ // / }
233+ // /
234+ // / In the above example `next` is a recursive link and would be recognized
235+ // / by this function and added to the result set.
236+ static void findRecursiveLinks (ClassDecl *cd,
237+ llvm::SmallSetVector<VarDecl *, 4 > &result) {
238+ auto selfTy = cd->getDeclaredInterfaceType ();
239+
240+ // Collect all stored properties that would form a recursive structure,
241+ // so we can remove the recursion and prevent the call stack from
242+ // overflowing.
243+ for (VarDecl *vd : cd->getStoredProperties ()) {
244+ auto Ty = vd->getInterfaceType ()->getOptionalObjectType ();
245+ if (Ty && Ty->getCanonicalType () == selfTy->getCanonicalType ()) {
246+ result.insert (vd);
247+ }
248+ }
249+
250+ // NOTE: Right now we only optimize linear recursion, so if there is more
251+ // than one stored property of the same type, clear out the set and don't
252+ // perform any recursion optimization.
253+ if (result.size () > 1 ) {
254+ result.clear ();
255+ }
256+ }
257+
258+ void SILGenFunction::emitRecursiveChainDestruction (ManagedValue selfValue,
259+ ClassDecl *cd,
260+ VarDecl *recursiveLink,
261+ CleanupLocation cleanupLoc) {
262+ auto selfTy = F.mapTypeIntoContext (cd->getDeclaredInterfaceType ());
263+
264+ auto selfTyLowered = getTypeLowering (selfTy).getLoweredType ();
265+
266+ SILBasicBlock *cleanBB = createBasicBlock ();
267+ SILBasicBlock *noneBB = createBasicBlock ();
268+ SILBasicBlock *notUniqueBB = createBasicBlock ();
269+ SILBasicBlock *uniqueBB = createBasicBlock ();
270+ SILBasicBlock *someBB = createBasicBlock ();
271+ SILBasicBlock *loopBB = createBasicBlock ();
272+
273+ // var iter = self.link
274+ // self.link = nil
275+ auto Ty = getTypeLowering (F.mapTypeIntoContext (recursiveLink->getInterfaceType ())).getLoweredType ();
276+ auto optionalNone = B.createOptionalNone (cleanupLoc, Ty);
277+ SILValue varAddr =
278+ B.createRefElementAddr (cleanupLoc, selfValue.getValue (), recursiveLink,
279+ Ty.getAddressType ());
280+ auto *iterAddr = B.createAllocStack (cleanupLoc, Ty);
281+ SILValue addr = B.createBeginAccess (
282+ cleanupLoc, varAddr, SILAccessKind::Modify, SILAccessEnforcement::Static,
283+ true /* noNestedConflict*/ , false /* fromBuiltin*/ );
284+ SILValue iter = B.createLoad (cleanupLoc, addr, LoadOwnershipQualifier::Take);
285+ B.createStore (cleanupLoc, optionalNone, addr, StoreOwnershipQualifier::Init);
286+ B.createEndAccess (cleanupLoc, addr, false /* is aborting*/ );
287+ B.createStore (cleanupLoc, iter, iterAddr, StoreOwnershipQualifier::Init);
288+
289+ B.createBranch (cleanupLoc, loopBB);
290+
291+ // while iter != nil {
292+ {
293+ B.emitBlock (loopBB);
294+ auto iterBorrow =
295+ ManagedValue::forUnmanaged (iterAddr).borrow (*this , cleanupLoc);
296+ SwitchEnumBuilder switchBuilder (B, cleanupLoc, iterBorrow);
297+ switchBuilder.addOptionalSomeCase (someBB);
298+ switchBuilder.addOptionalNoneCase (noneBB);
299+ std::move (switchBuilder).emit ();
300+ }
301+
302+ // if isKnownUniquelyReferenced(&iter) {
303+ {
304+ B.emitBlock (someBB);
305+ auto isUnique = B.createIsUnique (cleanupLoc, iterAddr);
306+ B.createCondBranch (cleanupLoc, isUnique, uniqueBB, notUniqueBB);
307+ }
308+
309+ // we have a uniquely referenced link, so we need to deinit
310+ {
311+ B.emitBlock (uniqueBB);
312+
313+ // let tail = iter.unsafelyUnwrapped.next
314+ // iter = tail
315+ SILValue iterBorrow = B.createLoadBorrow (cleanupLoc, iterAddr);
316+ auto *link = B.createUncheckedEnumData (
317+ cleanupLoc, iterBorrow, getASTContext ().getOptionalSomeDecl (),
318+ selfTyLowered);
319+
320+ varAddr = B.createRefElementAddr (cleanupLoc, link, recursiveLink,
321+ Ty.getAddressType ());
322+
323+ addr = B.createBeginAccess (
324+ cleanupLoc, varAddr, SILAccessKind::Read, SILAccessEnforcement::Static,
325+ true /* noNestedConflict */ , false /* fromBuiltin*/ );
326+
327+ // The deinit of `iter` will decrement the ref count of the field
328+ // containing the next element and potentially leading to its
329+ // deinitialization, causing the recursion. The prevent that,
330+ // we `load [copy]` here to ensure the object stays alive until
331+ // we explicitly release it in the next step of the iteration.
332+ iter = B.createLoad (cleanupLoc, addr, LoadOwnershipQualifier::Copy);
333+ B.createEndAccess (cleanupLoc, addr, false /* is aborting*/ );
334+ B.createEndBorrow (cleanupLoc, iterBorrow);
335+
336+ B.createStore (cleanupLoc, iter, iterAddr, StoreOwnershipQualifier::Assign);
337+
338+ B.createBranch (cleanupLoc, loopBB);
339+ }
340+
341+ // the next link in the chain is not unique, so we are done here
342+ {
343+ B.emitBlock (notUniqueBB);
344+ B.createBranch (cleanupLoc, cleanBB);
345+ }
346+
347+ // we reached the end of the chain
348+ {
349+ B.emitBlock (noneBB);
350+ B.createBranch (cleanupLoc, cleanBB);
351+ }
352+
353+ {
354+ B.emitBlock (cleanBB);
355+ B.createDestroyAddr (cleanupLoc, iterAddr);
356+ B.createDeallocStack (cleanupLoc, iterAddr);
357+ }
358+ }
359+
222360void SILGenFunction::emitClassMemberDestruction (ManagedValue selfValue,
223361 ClassDecl *cd,
224362 CleanupLocation cleanupLoc) {
@@ -231,10 +369,10 @@ void SILGenFunction::emitClassMemberDestruction(ManagedValue selfValue,
231369 // / For other cases, the basic blocks are not necessary and the destructor
232370 // / can just emit all the normal destruction code right into the current block.
233371 // If set, used as the basic block for the destroying of all members.
234- SILBasicBlock* normalMemberDestroyBB = nullptr ;
372+ SILBasicBlock * normalMemberDestroyBB = nullptr ;
235373 // If set, used as the basic block after members have been destroyed,
236374 // and we're ready to perform final cleanups before returning.
237- SILBasicBlock* finishBB = nullptr ;
375+ SILBasicBlock * finishBB = nullptr ;
238376
239377 // / A distributed actor may be 'remote' in which case there is no need to
240378 // / destroy "all" members, because they never had storage to begin with.
@@ -247,13 +385,29 @@ void SILGenFunction::emitClassMemberDestruction(ManagedValue selfValue,
247385 finishBB);
248386 }
249387
388+ // Before we destroy all fields, we check if any of them are
389+ // recursively the same type as `self`, so we can iteratively
390+ // deinitialize them, to prevent deep recursion and potential
391+ // stack overflows.
392+
393+ llvm::SmallSetVector<VarDecl *, 4 > recursiveLinks;
394+ findRecursiveLinks (cd, recursiveLinks);
395+
250396 // / Destroy all members.
251397 {
252398 if (normalMemberDestroyBB)
253399 B.emitBlock (normalMemberDestroyBB);
254400
255- for (VarDecl *vd : cd->getStoredProperties ())
401+ for (VarDecl *vd : cd->getStoredProperties ()) {
402+ if (recursiveLinks.contains (vd))
403+ continue ;
256404 destroyClassMember (cleanupLoc, selfValue, vd);
405+ }
406+
407+ if (!recursiveLinks.empty ()) {
408+ assert (recursiveLinks.size () == 1 && " Only linear recursion supported." );
409+ emitRecursiveChainDestruction (selfValue, cd, recursiveLinks[0 ], cleanupLoc);
410+ }
257411
258412 if (finishBB)
259413 B.createBranch (cleanupLoc, finishBB);
0 commit comments