java-topology/defects/scala3/unit/test_scala3_0001_isless_complexity.scala
russell@unturf.com 0a580b313d undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com

Patches, unit tests, benchmarks, whitepaper, and outreach briefs.
Public domain — no copyright claimed. Use freely.
2026-03-26 17:11:57 -04:00

82 lines
3.1 KiB
Scala
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* scala3-0001 unit test — OrderingConstraint.isLess() complexity
*
* Verifies that isLess() performs O(1) contains operations (not O(n)) by
* counting hash-set contains calls via a mock Set[TypeParamRef] wrapper.
*
* The defect: upper(param1) returned List[TypeParamRef]; List.contains is O(n).
* The fix: upper(param1) is backed by Set[TypeParamRef]; Set.contains is O(1).
*
* Test structure: synthetic constraint chain A0 <: A1 <: ... <: An-1.
* For the DEFECTIVE version: isLess(A0, An-1) scans n-1 entries → O(n) comparisons.
* For the PATCHED version: isLess(A0, An-1) does 1 hash lookup → O(1).
*
* This test ONLY runs against the compiler's test harness, not standalone.
* It is structured as a dotty/scala3 test to be placed in:
* tests/unit/scala3-0001-isless-complexity.scala
*
* Proxy test: run the Scala 3 compiler on the synthetic input below and
* measure compile time growth ratio. For quadratic isLess (old code),
* growth from n=100 to n=400 should be ~16× (4²). For O(1) isLess (fix),
* growth should be ~4× (linear).
*/
// ---- Proxy complexity test (shell-runnable, measures compile time growth) ----
//
// Generate a source file with n chained implicits, compile it, measure time.
// A cubic growth from n=100 to n=200 would produce ~8× slowdown.
// A quadratic growth would produce ~4×. Linear: ~2×.
//
// Run: scala-cli run test_scala3_0001_isless_complexity.scala
//
// This file generates and times compilation of synthetic HKT chains.
import scala.sys.process.*
import java.io.{File, PrintWriter}
import java.nio.file.{Files, Path}
@main def runComplexityTest(): Unit =
val ns = List(25, 50, 100, 200)
println(f"${"n"}%6s ${"time_ms"}%10s ${"ratio"}%8s ${"expected"}%10s")
println("-" * 42)
var prevTime = 0L
for n <- ns do
val src = generateChain(n)
val tmpDir = Files.createTempDirectory("scala3-0001-")
val srcFile = tmpDir.resolve("chain.scala").toFile
val pw = PrintWriter(srcFile)
pw.write(src)
pw.close()
val t0 = System.currentTimeMillis()
val result = s"scalac -d ${tmpDir} ${srcFile}".!!
val elapsed = System.currentTimeMillis() - t0
val ratio = if prevTime > 0 then f"${elapsed.toDouble / prevTime}%.2f" else " -"
val expected = if prevTime > 0 then "(~2.0 linear, ~4.0 quadratic, ~8.0 cubic)" else ""
println(f"$n%6d $elapsed%10d $ratio%8s $expected")
prevTime = elapsed
srcFile.delete()
def generateChain(n: Int): String =
// Generates n type parameters in a chain: each T(i) is bounded by T(i-1)
// This forces the Scala 3 type inference to track n ordering constraints.
val sb = StringBuilder()
sb.append("// Auto-generated: scala3-0001 complexity probe, n=").append(n).append("\n")
sb.append("object Chain {\n")
// Type aliases forming a chain
sb.append(" type T0 = Int\n")
for i <- 1 until n do
sb.append(s" type T$i <: T${i-1}\n")
// Function forcing ordering constraint resolution
sb.append(s" def f[")
sb.append((0 until n).map(i => s"A$i").mkString(", "))
sb.append("](")
sb.append((0 until n).map(i => s"a$i: A$i").mkString(", "))
sb.append("): Unit = ()\n")
sb.append("}\n")
sb.toString