1414#include " RValue.h"
1515#include " SILGenFunction.h"
1616#include " SILGenFunctionBuilder.h"
17+ #include " SwitchEnumBuilder.h"
1718#include " swift/AST/GenericSignature.h"
1819#include " swift/AST/SubstitutionMap.h"
1920#include " swift/SIL/TypeLowering.h"
@@ -220,21 +221,34 @@ void SILGenFunction::destroyClassMember(SILLocation cleanupLoc,
220221 }
221222}
222223
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.
223236void findRecursiveLinks (ClassDecl *cd, llvm::SmallSetVector<VarDecl*, 4 > &result) {
224- auto SelfTy = cd->getDeclaredInterfaceType ();
237+ auto selfTy = cd->getDeclaredInterfaceType ();
225238
226239 // Collect all stored properties that would form a recursive structure,
227240 // so we can remove the recursion and prevent the call stack from
228241 // overflowing.
229242 for (VarDecl *vd : cd->getStoredProperties ()) {
230243 auto Ty = vd->getInterfaceType ()->getOptionalObjectType ();
231- if (Ty && Ty->getCanonicalType () == SelfTy ->getCanonicalType ()) {
244+ if (Ty && Ty->getCanonicalType () == selfTy ->getCanonicalType ()) {
232245 result.insert (vd);
233246 }
234247 }
235248
236- // NOTE: Right now we only optimize linear recursion, so if there is more than
237- // one link, clear out the set and don't perform any recursion optimization.
249+ // NOTE: Right now we only optimize linear recursion, so if there is more
250+ // than one stored property of the same type, clear out the set and don't
251+ // perform any recursion optimization.
238252 if (result.size () > 1 ) {
239253 result.clear ();
240254 }
@@ -244,9 +258,9 @@ void SILGenFunction::emitRecursiveChainDestruction(ManagedValue selfValue,
244258 ClassDecl *cd,
245259 VarDecl *recursiveLink,
246260 CleanupLocation cleanupLoc) {
247- auto SelfTy = F.mapTypeIntoContext (cd->getDeclaredInterfaceType ());
261+ auto selfTy = F.mapTypeIntoContext (cd->getDeclaredInterfaceType ());
248262
249- auto SelfTyLowered = getTypeLowering (SelfTy ).getLoweredType ();
263+ auto selfTyLowered = getTypeLowering (selfTy ).getLoweredType ();
250264
251265 SILBasicBlock *cleanBB = createBasicBlock ();
252266 SILBasicBlock *noneBB = createBasicBlock ();
@@ -262,7 +276,7 @@ void SILGenFunction::emitRecursiveChainDestruction(ManagedValue selfValue,
262276 SILValue varAddr =
263277 B.createRefElementAddr (cleanupLoc, selfValue.getValue (), recursiveLink,
264278 Ty.getAddressType ());
265- auto iterAddr = B.createAllocStack (cleanupLoc, Ty);
279+ auto * iterAddr = B.createAllocStack (cleanupLoc, Ty);
266280 SILValue addr = B.createBeginAccess (
267281 cleanupLoc, varAddr, SILAccessKind::Modify, SILAccessEnforcement::Static,
268282 true /* noNestedConflict*/ , false /* fromBuiltin*/ );
@@ -274,57 +288,77 @@ void SILGenFunction::emitRecursiveChainDestruction(ManagedValue selfValue,
274288 B.createBranch (cleanupLoc, loopBB);
275289
276290 // while iter != nil {
277- B.emitBlock (loopBB);
278- B.createSwitchEnumAddr (
279- cleanupLoc, iterAddr, nullptr ,
280- {{getASTContext ().getOptionalSomeDecl (), someBB},
281- {std::make_pair (getASTContext ().getOptionalNoneDecl (), noneBB)}});
291+ {
292+ B.emitBlock (loopBB);
293+ auto iterBorrow =
294+ ManagedValue::forUnmanaged (iterAddr).borrow (*this , cleanupLoc);
295+ SwitchEnumBuilder switchBuilder (B, cleanupLoc, iterBorrow);
296+ switchBuilder.addOptionalSomeCase (someBB);
297+ switchBuilder.addOptionalNoneCase (noneBB);
298+ std::move (switchBuilder).emit ();
299+ }
282300
283301 // if isKnownUniquelyReferenced(&iter) {
284- B.emitBlock (someBB);
285- auto isUnique = B.createIsUnique (cleanupLoc, iterAddr);
286- B.createCondBranch (cleanupLoc, isUnique, uniqueBB, notUniqueBB);
302+ {
303+ B.emitBlock (someBB);
304+ auto isUnique = B.createIsUnique (cleanupLoc, iterAddr);
305+ B.createCondBranch (cleanupLoc, isUnique, uniqueBB, notUniqueBB);
306+ }
287307
288308 // we have a uniquely referenced link, so we need to deinit
289- B.emitBlock (uniqueBB);
290-
291- // NOTE: We increment the ref count of the tail instead of unlinking it,
292- // because custom deinit implementations of subclasses may access
293- // it and it would be semantically wrong to unset it before that.
294- // Making the tail non-uniquely referenced prevents the recursion.
295-
296- // let tail = iter.unsafelyUnwrapped.next
297- // iter = tail
298- SILValue iterBorrow = B.createLoadBorrow (cleanupLoc, iterAddr);
299- auto *link = B.createUncheckedEnumData (cleanupLoc, iterBorrow,
300- getASTContext ().getOptionalSomeDecl (),
301- SelfTyLowered);
302-
303- varAddr = B.createRefElementAddr (cleanupLoc, link, recursiveLink,
304- Ty.getAddressType ());
305-
306- addr = B.createBeginAccess (
307- cleanupLoc, varAddr, SILAccessKind::Read, SILAccessEnforcement::Static,
308- true /* noNestedConflict */ , false /* fromBuiltin*/ );
309- iter = B.createLoad (cleanupLoc, addr, LoadOwnershipQualifier::Copy);
310- B.createEndAccess (cleanupLoc, addr, false /* is aborting*/ );
311- B.createEndBorrow (cleanupLoc, iterBorrow);
309+ {
310+ B.emitBlock (uniqueBB);
312311
313- B.createStore (cleanupLoc, iter, iterAddr, StoreOwnershipQualifier::Assign);
312+ // NOTE: We increment the ref count of the tail instead of unlinking it,
313+ // because custom deinit implementations of subclasses may access
314+ // it and it would be semantically wrong to unset it before that.
315+ // Making the tail non-uniquely referenced prevents the recursion.
314316
315- B.createBranch (cleanupLoc, loopBB);
317+ // let tail = iter.unsafelyUnwrapped.next
318+ // iter = tail
319+ SILValue iterBorrow = B.createLoadBorrow (cleanupLoc, iterAddr);
320+ auto *link = B.createUncheckedEnumData (
321+ cleanupLoc, iterBorrow, getASTContext ().getOptionalSomeDecl (),
322+ selfTyLowered);
323+
324+ varAddr = B.createRefElementAddr (cleanupLoc, link, recursiveLink,
325+ Ty.getAddressType ());
326+
327+ addr = B.createBeginAccess (
328+ cleanupLoc, varAddr, SILAccessKind::Read, SILAccessEnforcement::Static,
329+ true /* noNestedConflict */ , false /* fromBuiltin*/ );
330+
331+ // The deinit of `iter` will decrement the ref count of the field
332+ // containing the next element and potentially leading to its
333+ // deinitialization, causing the recursion. The prevent that,
334+ // we `load [copy]` here to ensure the object stays alive until
335+ // we explicitly release it in the next step of the iteration.
336+ iter = B.createLoad (cleanupLoc, addr, LoadOwnershipQualifier::Copy);
337+ B.createEndAccess (cleanupLoc, addr, false /* is aborting*/ );
338+ B.createEndBorrow (cleanupLoc, iterBorrow);
339+
340+ B.createStore (cleanupLoc, iter, iterAddr, StoreOwnershipQualifier::Assign);
341+
342+ B.createBranch (cleanupLoc, loopBB);
343+ }
316344
317345 // the next link in the chain is not unique, so we are done here
318- B.emitBlock (notUniqueBB);
319- B.createBranch (cleanupLoc, cleanBB);
346+ {
347+ B.emitBlock (notUniqueBB);
348+ B.createBranch (cleanupLoc, cleanBB);
349+ }
320350
321351 // we reached the end of the chain
322- B.emitBlock (noneBB);
323- B.createBranch (cleanupLoc, cleanBB);
352+ {
353+ B.emitBlock (noneBB);
354+ B.createBranch (cleanupLoc, cleanBB);
355+ }
324356
325- B.emitBlock (cleanBB);
326- B.createDestroyAddr (cleanupLoc, iterAddr);
327- B.createDeallocStack (cleanupLoc, iterAddr);
357+ {
358+ B.emitBlock (cleanBB);
359+ B.createDestroyAddr (cleanupLoc, iterAddr);
360+ B.createDeallocStack (cleanupLoc, iterAddr);
361+ }
328362}
329363
330364void SILGenFunction::emitClassMemberDestruction (ManagedValue selfValue,
@@ -355,6 +389,10 @@ void SILGenFunction::emitClassMemberDestruction(ManagedValue selfValue,
355389 finishBB);
356390 }
357391
392+ // Before we destroy all fields, we check if any of them are
393+ // recursively the same type as `self`, so we can iteratively
394+ // deinitialize them, to prevent deep recursion and potential
395+ // stack overflows.
358396 llvm::SmallSetVector<VarDecl*, 4 > recursiveLinks;
359397 findRecursiveLinks (cd, recursiveLinks);
360398
0 commit comments