//===--- Function.swift - Defines the Function class ----------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import Basic
import AST
import SILBridging

@_semantics("arc.immortal")
final public class Function : CustomStringConvertible, HasShortDescription, Hashable {
  public private(set) var effects = FunctionEffects()

  public var name: StringRef {
    return StringRef(bridged: bridged.getName())
  }

  public var location: Location {
    return Location(bridged: bridged.getLocation())
  }

  public var declRef: DeclRef { DeclRef(bridged: bridged.getDeclRef()) }

  public var sourceFile: SourceFile? { declRef.sourceFile }

  final public var description: String {
    return String(taking: bridged.getDebugDescription())
  }

  public var shortDescription: String { name.string }

  public func hash(into hasher: inout Hasher) {
    hasher.combine(ObjectIdentifier(self))
  }

  /// True if this function is referenced from anywhere within the module,
  /// e.g. from a `function_ref` instruction.
  public var isReferencedInModule: Bool { bridged.isReferencedInModule() }

  /// True if this function should be optimized, i.e. the module is compiled with optimizations
  /// and the function has no `@_optimize(none)` attribute.
  public var shouldOptimize: Bool { bridged.shouldOptimize() }

  public var wasDeserializedCanonical: Bool { bridged.wasDeserializedCanonical() }

  public var isTrapNoReturn: Bool { bridged.isTrapNoReturn() }

  public var isAutodiffVJP: Bool { bridged.isAutodiffVJP() }

  public var isConvertPointerToPointerArgument: Bool { bridged.isConvertPointerToPointerArgument() }

  public var isAddressor: Bool { bridged.isAddressor() }

  public var specializationLevel: Int { bridged.specializationLevel() }

  public var isSpecialization: Bool { bridged.isSpecialization() }

  public var hasOwnership: Bool { bridged.hasOwnership() }

  public var hasLoweredAddresses: Bool { bridged.hasLoweredAddresses() }

  public var loweredFunctionType: CanonicalType {
    CanonicalType(bridged: bridged.getLoweredFunctionType())
  }

  /// The lowered function type, with opaque archetypes erased.
  ///
  /// For example:
  ///    @substituted <τ_0_0> () -> @out τ_0_0 for <some P>
  /// is lowered to this inside its module:
  ///    @substituted <τ_0_0> () -> @out τ_0_0 for <ActualResultType>
  /// and this outside its module
  ///    @substituted <τ_0_0> () -> @out τ_0_0 for <some P>
  public var loweredFunctionTypeInContext: CanonicalType {
    CanonicalType(bridged: bridged.getLoweredFunctionTypeInContext())
  }

  public var genericSignature: GenericSignature {
    GenericSignature(bridged: bridged.getGenericSignature())
  }

  public var forwardingSubstitutionMap: SubstitutionMap {
    SubstitutionMap(bridged: bridged.getForwardingSubstitutionMap())
  }

  public func mapTypeIntoEnvironment(_ type: AST.`Type`) -> AST.`Type` {
    return AST.`Type`(bridged: bridged.mapTypeIntoEnvironment(type.bridged))
  }

  public func mapTypeIntoEnvironment(_ type: Type) -> Type {
    return Type(bridged: bridged.mapTypeIntoEnvironment(type.bridged))
  }

  /// Returns true if the function is a definition and not only an external declaration.
  ///
  /// This is the case if the function contains a body, i.e. some basic blocks.
  public var isDefinition: Bool { blocks.first != nil }

  public var blocks : BasicBlockList { BasicBlockList(first: bridged.getFirstBlock().block) }

  public var entryBlock: BasicBlock { blocks.first! }

  public func appendNewBlock(_ context: some MutatingContext) -> BasicBlock {
    context.notifyBranchesChanged()
    return context._bridged.appendBlock(bridged).block
  }

  public var arguments: LazyMapSequence<ArgumentArray, FunctionArgument> {
    entryBlock.arguments.lazy.map { $0 as! FunctionArgument }
  }

  public func argument(at index: Int) -> FunctionArgument {
    entryBlock.arguments[index] as! FunctionArgument
  }

  /// All instructions of all blocks.
  public var instructions: LazySequence<FlattenSequence<LazyMapSequence<BasicBlockList, InstructionList>>> {
    blocks.lazy.flatMap { $0.instructions }
  }

  /// Warning: This is O(n).
  public func getInstructionCount() -> Int {
    // TODO: once CI upgrades the host compiler to swift >= 6.0, we can use `Sequence.count`
    var numInsts = 0
    for _ in instructions {
      numInsts += 1
    }
    return numInsts
  }

  public var reversedInstructions: LazySequence<FlattenSequence<LazyMapSequence<ReverseBasicBlockList, ReverseInstructionList>>>  {
    blocks.reversed().lazy.flatMap { $0.instructions.reversed() }
  }
  
  public var returnInstruction: ReturnInstruction? {
    for block in blocks.reversed() {
      if let retInst = block.terminator as? ReturnInstruction { return retInst }
    }
    return nil
  }

  /// True if the callee function is annotated with @_semantics("programtermination_point").
  /// This means that the function terminates the program.
  public var isProgramTerminationPoint: Bool { hasSemanticsAttribute("programtermination_point") }

  public var isTransparent: Bool { bridged.isTransparent() }

  public var isAsync: Bool { bridged.isAsync() }

  /// True if this is a `[global_init]` function.
  ///
  /// Such a function is typically a global addressor which calls the global's
  /// initializer (`[global_init_once_fn]`) via a `builtin "once"`.
  public var isGlobalInitFunction: Bool { bridged.isGlobalInitFunction() }

  /// True if this is a `[global_init_once_fn]` function.
  ///
  /// Such a function allocates a global and stores the global's init value.
  /// It's called from a `[global_init]` function via a `builtin "once"`.
  public var isGlobalInitOnceFunction: Bool { bridged.isGlobalInitOnceFunction() }

  public var isLazyPropertyGetter: Bool { bridged.isLazyPropertyGetter() }

  public var isDestructor: Bool { bridged.isDestructor() }

  public var isGeneric: Bool { bridged.isGeneric() }

  public var linkage: Linkage { bridged.getLinkage().linkage }

  /// True, if the linkage of the function indicates that it is visible outside the current
  /// compilation unit and therefore not all of its uses are known.
  ///
  /// For example, `public` linkage.
  public var isPossiblyUsedExternally: Bool {
    return bridged.isPossiblyUsedExternally()
  }

  /// True, if the linkage of the function indicates that it has a definition outside the
  /// current compilation unit.
  ///
  /// For example, `public_external` linkage.
  public var isDefinedExternally: Bool { linkage.isExternal }

  public func hasSemanticsAttribute(_ attr: StaticString) -> Bool {
    attr.withUTF8Buffer { (buffer: UnsafeBufferPointer<UInt8>) in
      bridged.hasSemanticsAttr(BridgedStringRef(data: buffer.baseAddress!, count: buffer.count))
    }
  }

  public var isSerialized: Bool {
    switch serializedKind {
    case .notSerialized, .serializedForPackage:
      return false
    case .serialized:
      return true
    }
  }

  public var isAnySerialized: Bool {
    switch serializedKind {
    case .notSerialized:
      return false
    case .serialized, .serializedForPackage:
      return true
    }
  }

  public enum SerializedKind {
    case notSerialized, serialized, serializedForPackage
  }

  public var serializedKind: SerializedKind {
    switch bridged.getSerializedKind() {
    case .IsNotSerialized: return .notSerialized
    case .IsSerialized: return .serialized
    case .IsSerializedForPackage: return .serializedForPackage
    @unknown default: fatalError()
    }
  }

  private func serializedKindBridged(_ arg: SerializedKind) -> BridgedFunction.SerializedKind {
    switch arg {
    case .notSerialized: return .IsNotSerialized
    case .serialized: return .IsSerialized
    case .serializedForPackage: return .IsSerializedForPackage
    }
  }

  public func canBeInlinedIntoCaller(withSerializedKind callerSerializedKind: SerializedKind) -> Bool {
    switch serializedKind {
    // If both callee and caller are not_serialized, the callee can be inlined into the caller
    // during SIL inlining passes even if it (and the caller) might contain private symbols.
    case .notSerialized:
      return callerSerializedKind == .notSerialized;

    // If Package-CMO is enabled, we serialize package, public, and @usableFromInline decls as
    // [serialized_for_package].
    // Their bodies must not, however, leak into @inlinable functions (that are [serialized])
    // since they are inlined outside of their defining module.
    //
    // If this callee is [serialized_for_package], the caller must be either non-serialized
    // or [serialized_for_package] for this callee's body to be inlined into the caller.
    // It can however be referenced by [serialized] caller.
    case .serializedForPackage:
      return callerSerializedKind != .serialized;
    case .serialized:
      return true;
    }
  }

  public func hasValidLinkageForFragileRef(_ kind: SerializedKind) -> Bool {
    bridged.hasValidLinkageForFragileRef(serializedKindBridged(kind))
  }

  public enum ThunkKind {
    case noThunk, thunk, reabstractionThunk, signatureOptimizedThunk
  }

  public var thunkKind: ThunkKind {
    switch bridged.isThunk() {
    case .IsNotThunk:                return .noThunk
    case .IsThunk:                   return .thunk
    case .IsReabstractionThunk:      return .reabstractionThunk
    case .IsSignatureOptimizedThunk: return .signatureOptimizedThunk
    default:
      fatalError()
    }
  }
  public func set(thunkKind: ThunkKind, _ context: some MutatingContext) {
    context.notifyEffectsChanged()
    switch thunkKind {
    case .noThunk:                 bridged.setThunk(.IsNotThunk)
    case .thunk:                   bridged.setThunk(.IsThunk)
    case .reabstractionThunk:      bridged.setThunk(.IsReabstractionThunk)
    case .signatureOptimizedThunk: bridged.setThunk(.IsSignatureOptimizedThunk)
    }
  }

  /// True if this is a reabstraction thunk of escaping function type whose
  /// single argument is a potentially non-escaping closure.
  public var isWithoutActuallyEscapingThunk: Bool { bridged.isWithoutActuallyEscapingThunk() }

  public var accessorKindName: String? {
    guard bridged.isAccessor() else {
      return nil
    }
    return StringRef(bridged: bridged.getAccessorName()).string
  }

  public var isInitializer: Bool {
    return bridged.isInitializer()
  }

  public var isDeinitializer: Bool {
    return bridged.isDeinitializer()
  }

  public var isImplicit: Bool {
    return bridged.isImplicit()
  }

  /// True, if the function runs with a swift 5.1 runtime.
  /// Note that this is function specific, because inlinable functions are de-serialized
  /// in a client module, which might be compiled with a different deployment target.
  public var isSwift51RuntimeAvailable: Bool {
    bridged.isSwift51RuntimeAvailable()
  }

  public var needsStackProtection: Bool {
    bridged.needsStackProtection()
  }
  public func set(needStackProtection: Bool, _ context: some MutatingContext) {
    context.notifyEffectsChanged()
    bridged.setNeedStackProtection(needStackProtection)
  }

  public var isDeinitBarrier: Bool {
    effects.sideEffects?.global.isDeinitBarrier ?? true
  }

  public enum PerformanceConstraints {
    case none
    case noAllocations
    case noLocks
    case noRuntime
    case noExistentials
    case noObjCRuntime
    case manualOwnership
  }

  public var performanceConstraints: PerformanceConstraints {
    switch bridged.getPerformanceConstraints() {
      case .None: return .none
      case .NoAllocation: return .noAllocations
      case .NoLocks: return .noLocks
      case .NoRuntime: return .noRuntime
      case .NoExistentials: return .noExistentials
      case .NoObjCBridging: return .noObjCRuntime
      case .ManualOwnership: return .manualOwnership
      default: fatalError("unknown performance constraint")
    }
  }
  public func set(isPerformanceConstraint: Bool, _ context: some MutatingContext) {
    context.notifyEffectsChanged()
    bridged.setIsPerformanceConstraint(isPerformanceConstraint)
  }

  public enum InlineStrategy {
    case automatic
    case never
    case heuristicAlways
    case always
  }

  public var inlineStrategy: InlineStrategy {
    switch bridged.getInlineStrategy() {
      case .InlineDefault: return .automatic
      case .NoInline: return .never
      case .HeuristicAlwaysInline: return .heuristicAlways
      case .AlwaysInline: return .always
      default:
        fatalError()
    }
  }

  public enum ABILanguage {
    case C
    case Swift
  }

  public var abi: ABILanguage {
    switch bridged.getSILFunctionLanguage() {
      case .C:     return .C
      case .Swift: return .Swift
      default:
        fatalError()
    }
  }

  public enum SourceFileKind {
    case library         /// A normal .swift file.
    case main            /// A .swift file that can have top-level code.
    case sil             /// Came from a .sil file.
    case interface       /// Came from a .swiftinterface file, representing another module.
    case macroExpansion  /// Came from a macro expansion.
    case defaultArgument /// Came from default argument at caller side
  };

  public var sourceFileKind: SourceFileKind? {
    switch bridged.getSourceFileKind() {
    case .Library: return .library
    case .Main: return .main
    case .SIL: return .sil
    case .Interface: return .interface
    case .MacroExpansion: return .macroExpansion
    case .DefaultArgument: return .defaultArgument
    case .None: return nil
    @unknown default:
      fatalError("unknown enum case")
    }
  }

  public var isAutodiffSubsetParametersThunk: Bool {
    bridged.isAutodiffSubsetParametersThunk()
  }
}

public func == (lhs: Function, rhs: Function) -> Bool { lhs === rhs }
public func != (lhs: Function, rhs: Function) -> Bool { lhs !== rhs }

// Function conventions.
extension Function {
  public var convention: FunctionConvention {
    FunctionConvention(for: loweredFunctionTypeInContext, in: self)
  }

  public var argumentConventions: ArgumentConventions {
    ArgumentConventions(convention: convention)
  }

  // FIXME: Change this to argumentConventions.indirectSILResultCount.
  // This is incorrect in two cases: it does not include the indirect
  // error result, and, prior to address lowering, does not include
  // pack results.
  public var numIndirectResultArguments: Int { bridged.getNumIndirectFormalResults() }

  public var hasIndirectErrorArgument: Bool { bridged.hasIndirectErrorResult() }

  /// The number of arguments which correspond to parameters (and not to indirect results).
  public var numParameterArguments: Int { convention.parameters.count }

  /// The slice of arguments starting at argumentConventions.firstParameterIndex.
  public var parameters: LazyMapSequence<Slice<ArgumentArray>, FunctionArgument> {
    let args = arguments
    return args[argumentConventions.firstParameterIndex..<args.count]
  }

  /// The total number of arguments.
  ///
  /// This is the sum of indirect result arguments and parameter arguments.
  /// If the function is a definition (i.e. it has at least an entry block), this is the
  /// number of arguments of the function's entry block.
  public var numArguments: Int { argumentConventions.count }

  public var hasSelfArgument: Bool { argumentConventions.selfIndex != nil }

  public var selfArgumentIndex: Int? { argumentConventions.selfIndex }

  public var selfArgument: FunctionArgument? {
    if let selfArgIdx = selfArgumentIndex {
      return arguments[selfArgIdx]
    }
    return nil
  }

  public var dynamicSelfMetadata: FunctionArgument? {
    if bridged.hasDynamicSelfMetadata() {
      return arguments.last!
    }
    return nil
  }

  public var argumentTypes: ArgumentTypeArray { ArgumentTypeArray(function: self) }

  public var resultType: Type { bridged.getSILResultType().type }

  public var hasUnsafeNonEscapableResult: Bool {
    return bridged.hasUnsafeNonEscapableResult()
  }

  public var hasResultDependence: Bool {
    convention.resultDependencies != nil
  }
}

public struct ArgumentTypeArray : RandomAccessCollection, FormattedLikeArray {
  fileprivate let function: Function

  public var startIndex: Int { return 0 }
  public var endIndex: Int { function.bridged.getNumSILArguments() }

  public subscript(_ index: Int) -> Type {
    function.bridged.getSILArgumentType(index).type
  }
}

// Function effects.
extension Function {
  /// Kinds of effect attributes which can be defined for a Swift function.
  public enum EffectAttribute {
    /// No effect attribute is specified.
    case none
    
    /// `[readnone]`
    ///
    /// A readnone function does not have any observable memory read or write operations.
    /// This does not mean that the function cannot read or write at all. For example,
    /// it’s allowed to allocate and write to local objects inside the function.
    ///
    /// A function can be marked as readnone if two calls of the same function with the
    /// same parameters can be simplified to one call (e.g. by the CSE optimization).
    /// Some conclusions:
    /// * A readnone function must not return a newly allocated class instance.
    /// * A readnone function can return a newly allocated copy-on-write object,
    ///   like an Array, because COW data types conceptually behave like value types.
    /// * A readnone function must not release any parameter or any object indirectly
    ///   referenced from a parameter.
    /// * Any kind of observable side-effects are not allowed, like `print`, file IO, etc.
    case readNone
    
    /// `[readonly]`
    ///
    /// A readonly function does not have any observable memory write operations.
    /// Similar to readnone, a readonly function is allowed to contain writes to e.g. local objects, etc.
    ///
    /// A function can be marked as readonly if it’s save to eliminate a call to such
    /// a function if its return value is not used.
    /// The same conclusions as for readnone also apply to readonly.
    case readOnly
    
    /// `[releasenone]`
    ///
    /// A releasenone function must not perform any observable release-operation on an object.
    /// This means, it must not do anything which might let the caller observe any decrement of
    /// a reference count or any deallocations.
    /// Note that it's allowed to release an object if the release is balancing a retain in the
    /// same function. Also, it's allowed to release (and deallocate) local objects which were
    /// allocated in the same function.
    case releaseNone
  }

  /// The effect attribute which is specified in the source code (if any).
  public var effectAttribute: EffectAttribute {
    switch bridged.getEffectAttribute() {
      case .ReadNone: return .readNone
      case .ReadOnly: return .readOnly
      case .ReleaseNone: return .releaseNone
      default: return .none
    }
  }

  public func modifyEffects(_ context: some MutatingContext, _ body: (inout FunctionEffects) -> ()) {
    context.notifyEffectsChanged()
    body(&effects)
  }
}

// Bridging utilities

extension Function {
  public var bridged: BridgedFunction {
    BridgedFunction(obj: SwiftObject(self))
  }

  static func register() {
    func checkLayout(_ p: UnsafeMutablePointer<FunctionEffects>,
                     data: UnsafeMutableRawPointer, size: Int) {
      assert(MemoryLayout<FunctionEffects>.size <= size, "wrong FunctionInfo size")
      assert(UnsafeMutableRawPointer(p) == data, "wrong FunctionInfo layout")
    }

    let metatype = unsafeBitCast(Function.self, to: SwiftMetatype.self)
    BridgedFunction.registerBridging(metatype,
      // initFn
      { (f: BridgedFunction, data: UnsafeMutableRawPointer, size: Int) in
        checkLayout(&f.function.effects, data: data, size: size)
        data.initializeMemory(as: FunctionEffects.self, repeating: FunctionEffects(), count: 1)
      },
      // destroyFn
      { (f: BridgedFunction, data: UnsafeMutableRawPointer, size: Int) in
        checkLayout(&f.function.effects, data: data, size: size)
        data.assumingMemoryBound(to: FunctionEffects.self).deinitialize(count: 1)
      },
      // writeFn
      { (f: BridgedFunction, os: BridgedOStream, idx: Int) in
        let s: String
        let effects = f.function.effects
        if idx >= 0 {
          if idx < effects.escapeEffects.arguments.count {
            s = effects.escapeEffects.arguments[idx].bodyDescription
          } else {
            let globalIdx = idx - effects.escapeEffects.arguments.count
            if globalIdx == 0 {
              s = effects.sideEffects!.global.description
            } else {
              let seIdx = globalIdx - 1
              s = effects.sideEffects!.getArgumentEffects(for: seIdx).bodyDescription
            }
          }
        } else {
          s = effects.description
        }
        s._withBridgedStringRef { $0.write(os) }
      },
      // parseFn:
      { (f: BridgedFunction, str: BridgedStringRef, mode: BridgedFunction.ParseEffectsMode, argumentIndex: Int, paramNames: BridgedArrayRef) -> BridgedFunction.ParsingError in
        do {
          var parser = StringParser(String(str))
          let function = f.function

          switch mode {
          case .argumentEffectsFromSource:
            let paramToIdx = paramNames.withElements(ofType: BridgedStringRef.self) {
                (buffer: UnsafeBufferPointer<BridgedStringRef>) -> Dictionary<String, Int> in
              let keyValPairs = buffer.enumerated().lazy.map { (String($0.1), $0.0) }
              return Dictionary(uniqueKeysWithValues: keyValPairs)
            }
            let effect = try parser.parseEffectFromSource(for: function, params: paramToIdx)
            function.effects.escapeEffects.arguments.append(effect)
          case .argumentEffectsFromSIL:
            try parser.parseEffectsFromSIL(argumentIndex: argumentIndex, to: &function.effects)
          case .globalEffectsFromSIL:
            try parser.parseGlobalSideEffectsFromSIL(to: &function.effects)
          case .multipleEffectsFromSIL:
            try parser.parseEffectsFromSIL(to: &function.effects)
          default:
            fatalError("invalid ParseEffectsMode")
          }
          if !parser.isEmpty() { try parser.throwError("syntax error") }
        } catch let error as ParsingError {
          return BridgedFunction.ParsingError(message: error.message.utf8Start, position: error.position)
        } catch {
          fatalError()
        }
        return BridgedFunction.ParsingError(message: nil, position: 0)
      },
      // copyEffectsFn
      { (toFunc: BridgedFunction, fromFunc: BridgedFunction) -> Int in
        let srcFunc = fromFunc.function
        let destFunc = toFunc.function
        let srcResultArgs = srcFunc.numIndirectResultArguments
        let destResultArgs = destFunc.numIndirectResultArguments
        
        // We only support reabstraction (indirect -> direct) of a single
        // return value.
        if srcResultArgs != destResultArgs &&
           (srcResultArgs > 1 || destResultArgs > 1) {
          return 0
        }
        destFunc.effects =
          FunctionEffects(copiedFrom: srcFunc.effects,
                          resultArgDelta: destResultArgs - srcResultArgs)
        return 1
      },
      // getEffectInfo
      {  (f: BridgedFunction, idx: Int) -> BridgedFunction.EffectInfo in
        let effects = f.function.effects
        if idx < effects.escapeEffects.arguments.count {
          let effect = effects.escapeEffects.arguments[idx]
          return BridgedFunction.EffectInfo(argumentIndex: effect.argumentIndex,
                                            isDerived: effect.isDerived, isEmpty: false, isValid: true)
        }
        if let sideEffects = effects.sideEffects {
          let globalIdx = idx - effects.escapeEffects.arguments.count
          if globalIdx == 0 {
            return BridgedFunction.EffectInfo(argumentIndex: -1, isDerived: true, isEmpty: false, isValid: true)
          }
          let seIdx = globalIdx - 1
          if seIdx < sideEffects.arguments.count {
            return BridgedFunction.EffectInfo(argumentIndex: seIdx, isDerived: true,
                                              isEmpty: sideEffects.arguments[seIdx].isEmpty, isValid: true)
          }
        }
        return BridgedFunction.EffectInfo(argumentIndex: -1, isDerived: false, isEmpty: true, isValid: false)
      },
      // getMemBehaviorFn
      { (f: BridgedFunction, observeRetains: Bool) -> BridgedMemoryBehavior in
        let e = f.function.getSideEffects()
        return e.getMemBehavior(observeRetains: observeRetains)
      },
      // argumentMayRead  (used by the MemoryLifetimeVerifier)
      { (f: BridgedFunction, bridgedArgOp: BridgedOperand, bridgedAddr: BridgedValue) -> Bool in
        let argOp = Operand(bridged: bridgedArgOp)
        let addr = bridgedAddr.value
        let applySite = argOp.instruction as! FullApplySite
        let addrPath = addr.accessPath
        let calleeArgIdx = applySite.calleeArgumentIndex(of: argOp)!
        let convention = applySite.convention(of: argOp)!
        assert(convention.isIndirectIn || convention.isInout)
        let argPath = argOp.value.accessPath
        assert(!argPath.isDistinct(from: addrPath))
        let path = argPath.getProjection(to: addrPath) ?? SmallProjectionPath()
        let effects = f.function.getSideEffects(forArgument: argOp.value.at(path),
                                                atIndex: calleeArgIdx,
                                                withConvention: convention)
        return effects.memory.read
      },
      // isDeinitBarrier
      { (f: BridgedFunction) -> Bool in
        return f.function.getSideEffects().isDeinitBarrier
      }
    )
  }
}

extension BridgedFunction {
  public var function: Function { obj.getAs(Function.self) }
}

extension OptionalBridgedFunction {
  public var function: Function? { obj.getAs(Function.self) }
}

public extension SideEffects.GlobalEffects {
  func getMemBehavior(observeRetains: Bool) -> BridgedMemoryBehavior {
    if allocates || ownership.destroy || (ownership.copy && observeRetains) {
      return .MayHaveSideEffects
    }
    switch (memory.read, memory.write) {
    case (false, false): return .None
    case (true, false): return .MayRead
    case (false, true): return .MayWrite
    case (true, true): return .MayReadWrite
    }
  }
}

public struct BasicBlockList : CollectionLikeSequence, IteratorProtocol {
  private var currentBlock: BasicBlock?

  public init(first: BasicBlock?) { currentBlock = first }

  public mutating func next() -> BasicBlock? {
    if let block = currentBlock {
      currentBlock = block.next
      return block
    }
    return nil
  }

  public var first: BasicBlock? { currentBlock }

  public func reversed() -> ReverseBasicBlockList {
    if let block = currentBlock {
      let lastBlock = block.parentFunction.bridged.getLastBlock().block
      return ReverseBasicBlockList(first: lastBlock)
    }
    return ReverseBasicBlockList(first: nil)
  }
}

public struct ReverseBasicBlockList : CollectionLikeSequence, IteratorProtocol {
  private var currentBlock: BasicBlock?

  public init(first: BasicBlock?) { currentBlock = first }

  public mutating func next() -> BasicBlock? {
    if let block = currentBlock {
      currentBlock = block.previous
      return block
    }
    return nil
  }

  public var first: BasicBlock? { currentBlock }
}
