Repository: apple/swift
File: lib/SILOptimizer/Mandatory/DiagnoseInvalidEscapingCaptures.cpp (C++)

Code before:

    // PackElementGetInst may have a function argument as its pack operand's
    // definition. Otherwise, follow to the definition of the pack.
    if (auto *packElemGet = dyn_cast<PackElementGetInst>(inst)) {
      if (auto *arg = dyn_cast<SILArgument>(packElemGet->getPack())) {
        return arg;
      }
      worklist.pushIfNotVisited(
          packElemGet->getPack()->getDefiningInstruction());
      continue;
    }
  }

  return nullptr;
}

const ParamDecl *getParamDeclFromOperand(SILValue value) {
  while (value) {
    // Look through mark must check.
    if (auto *mmci = dyn_cast<MarkUnresolvedNonCopyableValueInst>(value)) {
      value = mmci->getOperand();
    // Look through copies.
    } else if (auto *ci = dyn_cast<CopyValueInst>(value)) {
      value = ci->getOperand();
    } else if (auto *alloc = dyn_cast<AllocStackInst>(value)) {
      value = tryGetPackForTempAllocStack(alloc);
    } else {
      break;
    }
  }

  if (value)
    if (auto *arg = dyn_cast<SILArgument>(value))
      if (auto *decl = dyn_cast_or_null<ParamDecl>(arg->getDecl()))
        return decl;

  return nullptr;
}

bool isUseOfSelfInInitializer(Operand *oper) {
  if (auto *PBI = dyn_cast<ProjectBoxInst>(oper->get())) {
    SILValue value = PBI->getOperand();
    if (auto *bbi = dyn_cast<BeginBorrowInst>(value)) {
      value = bbi->getOperand();
    }
    if (auto *MUI = dyn_cast<MarkUninitializedInst>(value)) {
      switch (MUI->getMarkUninitializedKind()) {
      case MarkUninitializedInst::Kind::Var:
      case MarkUninitializedInst::Kind::Out:
        return false;
      case MarkUninitializedInst::Kind::RootSelf:

Review comment (bug):
3 things:

1. Can you change this loop to use early continues rather than else if to communicate control flow to the reader in the future.
2. Can you eliminate the else for the break. Once you put in the early continues, the control flow is much easier to read and it is clear that if you fail you break out.
3. It is unclear when reading this code that tryGetPackForTempAllocStack can return a SILValue(). This would be a footgun for future maintainers of the code. It is better to do an explicit check if value is SILValue() here and break in that case. Then it is really clear what is happening here. Code is written once and read forever.

Code after:
/// ```
///     %3 alloc_stack             [get users]
/// -> %14 tuple_pack_element_addr [get users] (since tuple isn't an arg!)
/// -> %15 copy_addr               [get src inst]
/// -> %13 pack_element_get        [get pack arg]
/// ->  %0 "fn"
/// ```
/// 
/// We can follow use to user, until we hit copy_addr and need to find the
/// source of the copy. When we hit pack_element_get, the pack operand is the
/// argument "fn" and we are done. Defer within an escaping function and pack
/// iteration follows this pattern. The arg check on tuple_pack_element_addr exists
/// for the next case, the use of a pack inside defer:
/// 
/// ```swift
/// func foo<each T>(_ fn: repeat () -> each T) {
///   defer {
///     escapes {
///       let _ = (repeat each fn)
///     }
///   }
///   _ = 42
/// }
/// ```
/// 
/// We start at
/// 
/// ```
/// %17 = alloc_stack $(repeat @noescape <τ_0_0> () -> @out τ_0_0 for <each T>) // users: %34, %32, %28
/// ```
/// 
/// And need to navigate
/// 
/// ```
/// // %0 "fn"                                        // user: %11
/// bb0(%0 : @closureCapture $*(repeat @noescape <τ_0_0> () -> @out τ_0_0 for <each T>)):
///   %1 = alloc_pack $Pack{repeat @noescape <τ_0_0> () -> @out τ_0_0 for <each T>} // users: %38, %27, %15, %12
/// // ...
///   %11 = tuple_pack_element_addr %9 of %0 as $*@noescape <τ_0_0> () -> @out τ_0_0 for <each T> // user: %12
///   pack_element_set %11 into %9 of %1              // id: %12
/// // ...
///   %17 = alloc_stack $(repeat @noescape <τ_0_0> () -> @out τ_0_0 for <each T>) // users: %34, %32, %28
/// // ...
///   %25 = dynamic_pack_index %22 of $Pack{repeat () -> each T} // users: %28, %27, %26
///   %26 = open_pack_element %25 of <each T> at <Pack{repeat each T}>, shape $each T // users: %28, %27
///   %27 = pack_element_get %25 of %1 as $*@noescape <τ_0_0> () -> @out τ_0_0 for <each T> // user: %29
///   %28 = tuple_pack_element_addr %25 of %17 as $*@noescape <τ_0_0> () -> @out τ_0_0 for <each T> // user: %29
///   copy_addr %27 to [init] %28                     // id: %29
/// // ...
/// ```
///