Skip to content

Missed KeyPath optimization in combination with parameter packs #84819

@dblsaiko

Description

@dblsaiko

Description

Swift tries to optimize out KeyPath accesses where possible. However, I could not get it to do this when the KeyPaths are stored within a parameter pack in a struct, even if all the code constructing and then using the KeyPaths is inlined into the client function. By comparison the alternative implementation, using a recursive generic struct instead of a parameter pack, does eliminate the KeyPath accesses.

Reproduction

Compiler Explorer

Roughly, this implementation, which is supposed to reduce boilerplate for custom Comparable implementations and ad-hoc comparisons. (See the Compiler Explorer link for a complete example including usage)

import Foundation

infix operator <=>

func <=> <C: Comparable>(_ a: C, _ b: C) -> ComparisonResult {
  if a < b {
    return .orderedAscending
  } else if a > b {
    return .orderedDescending
  } else {
    return .orderedSame
  }
}

struct ComponentwiseComparator<Root, each T: Comparable> {
  let keyPaths: (repeat KeyPath<Root, each T>)

  @inline(__always)
  init(_ keyPaths: repeat KeyPath<Root, each T>) {
    self.keyPaths = (repeat each keyPaths)
  }

  @inline(__always)
  func compare(_ left: Root, _ right: Root) -> ComparisonResult {
    for path in repeat each keyPaths {
      switch left[keyPath: path] <=> right[keyPath: path] {
      case .orderedSame:
        break
      case let x:
        return x
      }
    }

    return .orderedSame
  }
}

Expected behavior

Above code is inlined the same as the recursive implementation, which ends up greatly reducing code size (compare static output.A.< infix(output.A, output.A) -> Swift.Bool in the two assembly outputs)

Environment

Swift version 6.2 (swift-6.2-RELEASE)
Target: x86_64-unknown-linux-gnu

Additional information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugA deviation from expected or documented behavior. Also: expected but undesirable behavior.triage neededThis issue needs more specific labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions