java-topology/defects/swift/patch/swift-0002-requirement-machine-set.md

2.2 KiB
Raw Blame History

swift-0001: isInMinimizationDomain — O(P) linear scan in O(R) rule loop

Severity: HIGH

Location

  • lib/AST/RequirementMachine/RewriteSystem.cpp:484
  • Called from loops in:
    • lib/AST/RequirementMachine/HomotopyReduction.cpp:611,659,753
    • lib/AST/RequirementMachine/MinimalConformances.cpp:326
    • lib/AST/RequirementMachine/Diagnostics.cpp:349
    • lib/AST/RequirementMachine/PropertyUnification.cpp:133

Description

RewriteSystem::isInMinimizationDomain() performs a linear std::find scan over Protos (an ArrayRef<const ProtocolDecl *>) on every call. This function is invoked inside loops over all rewrite Rules (size scales with protocol graph complexity).

Complexity: O(R × P) where R = rule count, P = protocol count in connected component. For a deeply hierarchical protocol graph with N protocols, both R and P are O(N), yielding O(N²) total cost during requirement signature minimization.

Root Cause

// RewriteSystem.cpp:478
bool RewriteSystem::isInMinimizationDomain(const ProtocolDecl *proto) const {
  // ...
  if (std::find(Protos.begin(), Protos.end(), proto) != Protos.end())
    return true;
  return false;
}

Protos is ArrayRef<const ProtocolDecl *> — a plain pointer array. std::find is O(P). Called inside every rule-scanning loop; no caching or set-based lookup.

Fix

Replace ArrayRef<const ProtocolDecl *> Protos with llvm::DenseSet<const ProtocolDecl *> ProtoSet populated once at construction, or cache a llvm::SmallPtrSet alongside the ArrayRef. Then isInMinimizationDomain becomes an O(1) hash set lookup.

// In RewriteSystem.h, add:
llvm::SmallPtrSet<const ProtocolDecl *, 8> ProtoSet;

// In RewriteSystem::init or wherever Protos is set:
ProtoSet.insert(Protos.begin(), Protos.end());

// In isInMinimizationDomain:
bool RewriteSystem::isInMinimizationDomain(const ProtocolDecl *proto) const {
  if (proto == nullptr && Protos.empty())
    return true;
  return ProtoSet.count(proto) > 0;
}

Impact

Protocol-heavy Swift code (many protocols with complex inheritance) hits O(N²) during generic signature computation. Manifest as slow compilation on codebases with many protocols or deep protocol hierarchies (e.g., Combine/SwiftUI framework chains).