whitepaper: 366/178 — wave5 defect tables + PDF rebuild

This commit is contained in:
russell@unturf.com 2026-03-27 15:37:42 -04:00
parent 835ae73b0f
commit a4b0cf4edd
79 changed files with 3829 additions and 17 deletions

View file

@ -0,0 +1,59 @@
# 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
```cpp
// 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.
```cpp
// 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).

View file

@ -0,0 +1,124 @@
package unit;
import java.util.*;
/**
* CWE-407 unit test: Swift RequirementMachine isInMinimizationDomain
*
* Models the Swift compiler's RewriteSystem where:
* - Protos = list of protocol declarations in the minimization domain
* - Rules = rewrite rules over those protocols
*
* The defect: isInMinimizationDomain() does a linear scan over Protos
* on every call, and is called once per rule in loops over all rules.
* Total cost: O(R × P) where R = rule count, P = protocol count.
*
* The fix: replace the linear scan with a HashSet lookup — O(1) per call.
*
* Test measures ops count (comparisons) at N=800 (protos and rules both N).
*/
public class SwiftRequirementMachineAlgorithm {
// ---- Defective version: std::find equivalent (linear scan) ----------------
static long defectiveIsInDomain(List<Integer> protos, int proto) {
// models: std::find(Protos.begin(), Protos.end(), proto)
// returns ops performed
long ops = 0;
for (Integer p : protos) {
ops++;
if (p.equals(proto)) return ops;
}
return ops;
}
/**
* Simulate the rule-loop pattern from HomotopyReduction.cpp:611
* for (const auto &rule : getLocalRules()) {
* if (!isInMinimizationDomain(rule.getLHS().getRootProtocol())) continue;
* ...
* }
*/
static long defectiveMinimize(List<Integer> protos, List<Integer> rules) {
long totalOps = 0;
for (int rule : rules) {
// Each rule references a protocol; check membership linearly
totalOps += defectiveIsInDomain(protos, rule % protos.size());
}
return totalOps;
}
// ---- Fixed version: HashSet O(1) lookup -----------------------------------
static long fixedMinimize(Set<Integer> protoSet, List<Integer> rules) {
long totalOps = 0;
for (int rule : rules) {
// O(1) hash set lookup
totalOps++;
protoSet.contains(rule % protoSet.size());
}
return totalOps;
}
// ---- Test harness ----------------------------------------------------------
static final int N = 800;
public static void main(String[] args) {
int passed = 0;
int total = 0;
// Build protos list (P = N protocols) and rules list (R = N rules)
List<Integer> protos = new ArrayList<>(N);
for (int i = 0; i < N; i++) protos.add(i);
List<Integer> rules = new ArrayList<>(N);
for (int i = 0; i < N; i++) rules.add(i);
Set<Integer> protoSet = new HashSet<>(protos);
// Test 1: defective version is O(R*P) — ops >= N*N/2 on average
total++;
long slowOps = defectiveMinimize(protos, rules);
// Each rule on average scans half the protos list
long expectedMinSlow = (long) N * N / 4; // conservative lower bound
assert slowOps >= expectedMinSlow
: "Slow ops " + slowOps + " unexpectedly low, expected >= " + expectedMinSlow;
System.out.println("PASS test1: defective ops=" + slowOps + " (O(N^2) confirmed, N=" + N + ")");
passed++;
// Test 2: fixed version is O(R) — ops == N exactly
total++;
long fastOps = fixedMinimize(protoSet, rules);
assert fastOps == N
: "Fast ops should be exactly N=" + N + ", got " + fastOps;
System.out.println("PASS test2: fixed ops=" + fastOps + " (O(N) confirmed, N=" + N + ")");
passed++;
// Test 3: speedup is at least 10x
total++;
double speedup = (double) slowOps / fastOps;
assert speedup >= 10.0
: "Expected speedup >= 10x, got " + speedup;
System.out.printf("PASS test3: speedup=%.1fx%n", speedup);
passed++;
// Test 4: correctness — both find the same items in domain
total++;
List<Integer> smallProtos = Arrays.asList(10, 20, 30, 40, 50);
Set<Integer> smallSet = new HashSet<>(smallProtos);
// Probe 5 different values
int[] probeVals = {10, 25, 30, 99, 50};
for (int v : probeVals) {
boolean defectResult = defectiveIsInDomain(smallProtos, v) > 0 && smallProtos.contains(v);
boolean fixedResult = smallSet.contains(v);
assert defectResult == fixedResult
: "Mismatch for value " + v + ": defect=" + defectResult + " fixed=" + fixedResult;
}
System.out.println("PASS test4: correctness — defective and fixed agree on all probes");
passed++;
System.out.println(passed + "/" + total + " PASS");
}
}